布局优化技巧笔记 1. 降低 ImageView 的数目 2. 使用 ViewStub 3. 使用 merge 4. 使用 ClickableSpan 5. 代码格式化 6. 资源文件命名 7. 自己定义 View

在实际开发中怎样尽可能的降低层级、降低控件数量。并达到相同的视觉效果?本文记录开发过程中的实践。

1.1 使用 TextView.drawableXXX

如大众点评APP的首页:


布局优化技巧笔记
1. 降低 ImageView 的数目
2. 使用 ViewStub
3. 使用 merge
4. 使用 ClickableSpan
5. 代码格式化
6. 资源文件命名
7. 自己定义 View
注意红色框内的部分

红色框住的部分。由一张图片和它以下的文字组成,至少有两种实现方式:

  1. ImageView + TextView,图在上文字在下;
  2. TextView,设置 TextView.drawableTop=”xxx”。

显然第2种方式能够省掉 ImageView。

另一种情况,还是拿我经常使用的大众点评 APP 中的页面作为样例。见下图:


布局优化技巧笔记
1. 降低 ImageView 的数目
2. 使用 ViewStub
3. 使用 merge
4. 使用 ClickableSpan
5. 代码格式化
6. 资源文件命名
7. 自己定义 View
注意最右边的箭头

我们能够用例如以下的布局实现:

    <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. 降低 ImageView 的数目
2. 使用 ViewStub
3. 使用 merge
4. 使用 ClickableSpan
5. 代码格式化
6. 资源文件命名
7. 自己定义 View
注意“网友推荐”以下横线(右边的小手能够用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


布局优化技巧笔记
1. 降低 ImageView 的数目
2. 使用 ViewStub
3. 使用 merge
4. 使用 ClickableSpan
5. 代码格式化
6. 资源文件命名
7. 自己定义 View
注意红色框中不同颜色的文本

使用 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>"))。
<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 完毕。

比方微信头像墙的这个效果:


布局优化技巧笔记
1. 降低 ImageView 的数目
2. 使用 ViewStub
3. 使用 merge
4. 使用 ClickableSpan
5. 代码格式化
6. 资源文件命名
7. 自己定义 View

我们能够用 LinearLayout 实现。每一行都是一个 LinearLayout。可是这样层级较多(当然。盲目的降低层级也是不推荐的,由于 view 的绘制过程是由 meassure、layout、draw 三个过程完毕的,层级会降低 draw 的时间,可是可能会添加 meassure 的时间,甚至得不偿失)。

我们也能够自己定义一个 layout。在水平方向上放置子 view,空间不足时自己主动换行。