在实际开发中怎样尽可能的降低层级、降低控件数量,并达到相同的视觉效果?本文记录开发过程中的实践。
1. 降低 ImageView 的数目
1.1 使用 TextView.drawableXXX
如大众点评APP的首页:
注意红色框内的部分
红色框住的部分。由一张图片和它以下的文字组成,至少有两种实现方式:
- ImageView + TextView,图在上文字在下;
- TextView,设置 TextView.drawableTop=”xxx”。
显然第2种方式能够省掉 ImageView。
另一种情况,还是拿我经常使用的大众点评 APP 中的页面作为样例。见下图:
注意最右边的箭头
我们能够用例如以下的布局实现:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableRight="@drawable/arrow"
android:gravity="center_vertical"
android:text="查看所有网友点评"
android:textSize="16sp" />
除非 android:layout_width = “wrap_content”,否则即使把 android:gravity 属性改成 center,箭头仍然是居右的。这是非常恶心的地方,导致我们无法将文本和 drawable 一起居中(解决方法见 自己定义控件让TextView、Button的drawableLeft和drawableRight与文本一起居中显示),可是我们却能够利用这一点实现上述布局。
1.2 使用 layer-list 画线
经经常使用到横线。最直观的实现方法是放一张图片,事实上这张图片能够省掉。
相同以大众点评APP的页面为例:
注意“网友推荐”以下横线(右边的小手能够用1.1的方法实现哦)
当然能够用 ImageView 实现。
或者换种方法:将“网友推荐”所在的 layout 的 background 属性设置为:
background="@drawale/bg_line"
当中 bg_line.xml :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="#DADADA" />
</shape>
</item>
<item android:bottom="1px">
<shape>
<solid android:color="@color/white" />
</shape>
</item>
</layer-list>
2 个 item 画了 2 个层叠的矩形。下层矩形填充色是 #DADADA,上层矩形的填充色是白色。并且上层矩形的下边框比下层矩形的下边框高 1px,于是就有宽 1px、颜色为 #DADADA 的矩形区域显示出来。看起来就是一条横线。
2. 使用 ViewStub
3. 使用 merge
4. 使用 ClickableSpan
注意红色框中不同颜色的文本
使用 ClickableSpan 富文本实如今同一个 TextView 中的文本的颜色、大小、背景色等属性的多样化和个性化。例如以下图红色框内是一个 TextView(也可能是多个 TextView),可是却有两种不同的颜色,这样的效果就能够用 Spannale 实现:
Spannable richText = new Spannale("<font color=#E3E5F3>Alan海波</font>回复<font color=#E3E5F3>大赞</font>:你走开···");
textView.setText(richText);
假设不仅颜色不同,还要对某些文字加入响应事件(如跳转链接等),能够使用例如以下方式:
String username = "Alan 海波";
String content = "你走开……";
SpannableString spannableString = new SpannableString(username);
ClickableSpan span = new ClickableSpan() {
@Override
public void onClick(View widget) {
// do sth.
}
@Override
public void updateDrawState(TextPaint ds) {
ds.setColor(getResources().getColor(R.color.link_color));
ds.setUnderlineText(false);
}
};
spannableString.setSpan(span, 0, username.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
Spanned replyText = Html.fromHtml("<font color=" + getColor(R.color.deep_gray) + ">回复</font>");
Spanned colon = Html.fromHtml("<font color=" +getColor(R.color.link_color) + ">:</font>");
Spanned body = Html.fromHtml("<font color=" + getColor(R.color.text_color) + ">" + content + "</font>");
Spanned richText = (Spanned) android.text.TextUtils.concat(spannableString, replyText, spannableString, colon, body);
textView.setText(richText);
tv.setMovementMethod(LinkMovementMethod.getInstance());
注意:
- Html.fromHtml(string) 会将 string 中的 ‘ ’ 和 ‘ ’ 替换成空格,须要显式的将 ‘ ’(早期 Mac 系统)和 ‘ ’ (Unix 和 Max OS X)和 ‘ ’(Windows) 替换成 html 识别的 ‘< br>’,不替换的话,假设 string 中出现 “xx&xx ” 形式的子串,会发生 IOException:
Html.fromHtml(string.replace("
", "< br>").replace("
", "< br>")).replace("
", "< br>"));
- 用 ClickableSpan 给 TextView 中的文本设置响应事件 a,再对 TextView 设置响应事件 b。在某些机型上点击文本时会同一时候触发 a 和 b;
- 安卓原生只支持以下 HTML tag 标签:(详细见 http://commonsware.com/blog/Android/2010/05/26/html-tags-supported-by-textview.html):
<a href="...">
<b>
<big>
<blockquote>
<br>
<cite>
<dfn>
<div align="...">
<em>
<font size="..." color="..." face="...">
<h1>
<h2>
<h3>
<h4>
<h5>
<h6>
<i>
<img src="...">
<p>
<small>
<strike>
<strong>
<sub>
<sup>
<tt>
<u>
有支持所有的 HTML tag 标签的库,详细见 https://github.com/NightWhistler/HtmlSpanner。
5. 代码格式化
使用 Cmd + Alt + L(Mac)。Ctrl+Alt+L (Windows)快捷键格式化 layout 代码,使得属性排列更加规范。
6. 资源文件命名
很多其它信息请參考 该站点。
控件类型 | 前缀 | 演示样例 |
---|---|---|
Action bar | ab_ | ab_stacked.9.png |
Button | btn_ | btn_send_pressed.9.png |
Dialog | dialog_ | dialog_top.9.png |
Divider | divider_ | divider_horizontal.9.png |
Icon | ic_ | ic_star.png |
Menu | menu_ | menu_submenu_bg.9.png |
Notification | notification_ | notification_bg.9.png |
Tabs | tab_ | tab_pressed.9.png |
图标资源的命名:
资源类型 | 前缀 | 演示样例 |
---|---|---|
Icons | ic_ | ic_star.png |
Launcher icons | ic_launcher | ic_launcher_calendar.png |
Action bar icons | ic_menu | iic_menu_archive.png |
Status bar icons | ic_stat_notify | ic_stat_notify_msg.png |
Tab icons | ic_tab | ic_tab_recent.png |
Dialog icons | ic_dialog | ic_dialog_info.png |
PopupWindow icons | ic_pop | ic_dialog_like.png |
按压态的命名:
状态 | 后缀 | 演示样例 |
---|---|---|
Normal | _normal | btn_order_normal.9.png |
Pressed | _pressed | btn_order_pressed.9.png |
Focused | _focused | btn_order_focused.9.png |
Disabled | _disabled | btn_order_disabled.9.png |
Selected | _selected | btn_order_selected.9.png |
简单的project建议使用上述前缀来差别文件的类型。
复杂的project,建议以 module_ (如,个人中心模块的前缀为 account_。account_ic_launcher_calendar.png)作为前缀命名,公用的资源能够使用 base_ 或 common_ 作为前缀,这样命名有例如以下优点:
- 格式整齐。易读性强;
- 清理废弃资源文件时,比方使用 lint 来检查废弃资源时,方便划分责任范围;
- 防止出现引用非本module资源文件的发生;
7. 自己定义 View
假设使用现有的控件特别是 layout 不能非常多好的满足需求。我们能够自己定义 View 完毕。
比方微信头像墙的这个效果:
我们能够用 LinearLayout 实现。每一行都是一个 LinearLayout。可是这样层级较多(当然,盲目的降低层级也是不推荐的,由于 view 的绘制过程是由 meassure、layout、draw 三个过程完毕的。层级会降低 draw 的时间,可是可能会添加 meassure 的时间。甚至得不偿失)。
我们也能够自己定义一个 layout。在水平方向上放置子 view,空间不足时自己主动换行。