布局优化技巧笔记

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

1. 降低 ImageView 的数目

1.1 使用 TextView.drawableXXX

如大众点评APP的首页:



注意红色框内的部分

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

  1. ImageView + TextView,图在上文字在下;
  2. 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 中的 ‘\r’ 和 ‘\n’ 替换成空格,须要显式的将 ‘\r’(早期 Mac 系统)和 ‘\n’ (Unix 和 Max OS X)和 ‘\r\n’(Windows) 替换成 html 识别的 ‘< br>’,不替换的话,假设 string 中出现 “xx&xx\r” 形式的子串,会发生 IOException:
Html.fromHtml(string.replace("\r\n", "< br>").replace("\n", "< br>")).replace("\r", "< 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 完毕。

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


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

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

posted @ 2018-04-05 20:16  llguanli  阅读(185)  评论(0编辑  收藏  举报