Hacks布局篇-Hack3 使用<merge/>标签减少布局层级

<merge/>

作者:李旺成

时间:2016年5月7日


在这个 Hack 中将详细介绍 <merge /> 标签的使用,以及一些注意事项。

关于嵌套层级

Android 开发中关于布局优化有一个很常用的技巧,就是减少布局的嵌套层级。布局的嵌套层级越深,渲染该页面就会越慢,程序的性能也就会越差。

使用 Hierarchy Viewer 可以比较直观的看到布局的嵌套层级:

HierarchyViewer使用示例

常用的减少布局层级的方法是尽量使用 RelativeLayout,往往很多使用 LinearLayout 需要嵌套很多层才能达到的效果,改用 RelativeLayout 可能就不需要嵌套了。

使用 RelativeLayout 只是一方面,Google 提供了 <merge /> 标签用于减少布局层级来。<merge /> 标签通过删减多余或者额外的层级,从而优化整个 Android 布局的结构。

注意:如果你打开 HierarchyViewer 却不能查看任何布局,那么可以试试这个 ViewServer

<merge /> 标签的使用

<merge/> 多用于替换 FrameLayout 或者当一个布局包含另一个时,<merge/> 标签消除视图层次结构中多余的视图组。例如你的主布局文件是垂直布局,引入了一个垂直布局的 include,这时如果 include 布局使用 LinearLayout 就没意义了,使用的话反而会减慢你的 UI 表现。这时可以使用<merge/> 标签优化。

替换 FrameLayout

分别来看一下根标签使用 FrameLayout 与 使用 merge 有什么区别:
activity_hack3_1.xml :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Hack3Activity">
    <include
        layout="@layout/header_withmerge"/>
    <include
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        layout="@layout/footer_app_name"/>
</FrameLayout>

activity_hack3_2.xml :

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Hack3Activity">
    <include
        layout="@layout/header_withmerge"/>
    <include
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:layout_marginBottom="30dp"
        layout="@layout/footer_app_name"/>
</merge>

上面两个布局文件差别不大,只是 activity_hack3_2 中使用 merge 替换了 activity_hack3_1 中的 Framelayout,使用 HierarchyViewer 看看这两个布局有什么差别:

Framelayout做根标签
merge做根标签

对比上面两张图,发现使用 <merge/> 标签替换 Framelayout 之后可以减少一层。所以,如果用到 Framelayout 作为根布局的地方,大可以直接替换为 merge。

与 <include /> 配合

如果要 include 的子布局的根标签是 Framelayout,那么最好替换为 merge,这样可以减少嵌套。

如果,子布局直接以一个控件为根节点,也就是只有一个控件的情况,这时就没必要再使用 <merge /> 包裹了,如下所示:

merbe 和 include

如图所示,这两个 include 的子布局都只有一层,没有其他布局嵌套。

上图对应的布局代码 activity_hack3.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    >
    <include
        layout="@layout/header_withmerge"/>
    <include
        layout="@layout/footer_app_name"/>
</RelativeLayout>

用到的两个子布局:
header_withmerge.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="40dp"
    android:text="使用了 <merge /> 标签的页眉"
    android:textColor="@android:color/holo_red_light"/>
</merge>

footer_app_name.xml,前面已经介绍过了,这里就不再赘述。

作为 ListView Item 的根标签

这里做个实验,使用根标签为 <merge /> 的布局作为 ListView 的 Item。
1、创建 Item 布局
见 item_hack3lv.xml:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="使用merge作为ListView Item的根标签"
        android:textColor="@android:color/holo_red_light"/>
</merge>

不需要多说,就是以 merge 作为根标签。

2、自定义 Adapter
这里看看 getView() 方法即可:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
//            convertView = mInflater.inflate(R.layout.item_hack3lv, parent, true);
//            convertView = mInflater.inflate(R.layout.item_hack3lv, parent, false);
        convertView = mInflater.inflate(R.layout.item_hack3lv, null);
    }
    return convertView;
}

上面的示例代码中,采用了三种方式来 inflate Item 的布局文件。先不要去关注上面的写法是否有误,这里是为了做实验。

3、为 ListView 设置 Adapter

mContentLV.setAdapter(new Hack3LVAdapter(this));

看看这三种不同方式所产生的后果:

inflate(R.layout.item_hack3lv, parent, true)
inflate(R.layout.item_hack3lv, parent, false)
inflate(R.layout.item_hack3lv, null)

PS:第二种方式和第三种方式其实是一样的,可以看源码:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    return inflate(resource, root, root != null);
}

// 就是调的 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

有兴趣的同学可以去研究下 LayoutInflate,以及 LayoutInflate 的 inflate 方法各个参数的含义和作用,这里就不展开了。

这里只是的得出一个教训,不要使用 <merge /> 作为 ListView Item 的根节点。

注意事项

使用 <merge /> 很简单,但在其使用过程中有些地方需要注意,下面提供一些供参考。

  • <merge /> 只能作为布局的根标签使用
  • 不要使用 <merge /> 作为 ListView Item 的根节点
  • <merge /> 标签不需要设置属性
    写了也不起作用,因为系统会忽略 <merge /> 标签
  • inflate 以 <merge /> 为根标签的布局时要注意
  • 必须指定一个父 ViewGroup
  • 必须设定 attachToRoot 为 true
    也就是说 inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 方法的二个参数 root 不能为 null,并且第三参数 attachToRoot 必须传 true

项目地址

AndroidHacks合集
布局篇
个人博客
示例用到代码见:
Hack3Activity.java
Hack3LVAdapter.java
activity_hack3.xml
activity_hack3_1.xml
activity_hack3_2.xml
item_hack3lv.xml
header_withmerge.xml

参考

Android抽象布局——include、merge 、ViewStub
Android 性能优化 四 布局优化merge标签的使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容