Hacks布局篇-Hack3 使用<merge/>標(biāo)簽減少布局層級(jí)

<merge/>

作者:李旺成

時(shí)間:2016年5月7日


在這個(gè) Hack 中將詳細(xì)介紹 <merge /> 標(biāo)簽的使用,以及一些注意事項(xiàng)。

關(guān)于嵌套層級(jí)

Android 開(kāi)發(fā)中關(guān)于布局優(yōu)化有一個(gè)很常用的技巧,就是減少布局的嵌套層級(jí)。布局的嵌套層級(jí)越深,渲染該頁(yè)面就會(huì)越慢,程序的性能也就會(huì)越差。

使用 Hierarchy Viewer 可以比較直觀(guān)的看到布局的嵌套層級(jí):

HierarchyViewer使用示例

常用的減少布局層級(jí)的方法是盡量使用 RelativeLayout,往往很多使用 LinearLayout 需要嵌套很多層才能達(dá)到的效果,改用 RelativeLayout 可能就不需要嵌套了。

使用 RelativeLayout 只是一方面,Google 提供了 <merge /> 標(biāo)簽用于減少布局層級(jí)來(lái)。<merge /> 標(biāo)簽通過(guò)刪減多余或者額外的層級(jí),從而優(yōu)化整個(gè) Android 布局的結(jié)構(gòu)。

注意:如果你打開(kāi) HierarchyViewer 卻不能查看任何布局,那么可以試試這個(gè) ViewServer

<merge /> 標(biāo)簽的使用

<merge/> 多用于替換 FrameLayout 或者當(dāng)一個(gè)布局包含另一個(gè)時(shí),<merge/> 標(biāo)簽消除視圖層次結(jié)構(gòu)中多余的視圖組。例如你的主布局文件是垂直布局,引入了一個(gè)垂直布局的 include,這時(shí)如果 include 布局使用 LinearLayout 就沒(méi)意義了,使用的話(huà)反而會(huì)減慢你的 UI 表現(xiàn)。這時(shí)可以使用<merge/> 標(biāo)簽優(yōu)化。

替換 FrameLayout

分別來(lái)看一下根標(biāo)簽使用 FrameLayout 與 使用 merge 有什么區(qū)別:
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>

上面兩個(gè)布局文件差別不大,只是 activity_hack3_2 中使用 merge 替換了 activity_hack3_1 中的 Framelayout,使用 HierarchyViewer 看看這兩個(gè)布局有什么差別:

Framelayout做根標(biāo)簽
merge做根標(biāo)簽

對(duì)比上面兩張圖,發(fā)現(xiàn)使用 <merge/> 標(biāo)簽替換 Framelayout 之后可以減少一層。所以,如果用到 Framelayout 作為根布局的地方,大可以直接替換為 merge。

與 <include /> 配合

如果要 include 的子布局的根標(biāo)簽是 Framelayout,那么最好替換為 merge,這樣可以減少嵌套。

如果,子布局直接以一個(gè)控件為根節(jié)點(diǎn),也就是只有一個(gè)控件的情況,這時(shí)就沒(méi)必要再使用 <merge /> 包裹了,如下所示:

merbe 和 include

如圖所示,這兩個(gè) include 的子布局都只有一層,沒(méi)有其他布局嵌套。

上圖對(duì)應(yīng)的布局代碼 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>

用到的兩個(gè)子布局:
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 /> 標(biāo)簽的頁(yè)眉"
    android:textColor="@android:color/holo_red_light"/>
</merge>

footer_app_name.xml,前面已經(jīng)介紹過(guò)了,這里就不再贅述。

作為 ListView Item 的根標(biāo)簽

這里做個(gè)實(shí)驗(yàn),使用根標(biāo)簽為 <merge /> 的布局作為 ListView 的 Item。
1、創(chuàng)建 Item 布局
見(jiàn) 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作為L(zhǎng)istView Item的根標(biāo)簽"
        android:textColor="@android:color/holo_red_light"/>
</merge>

不需要多說(shuō),就是以 merge 作為根標(biāo)簽。

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;
}

上面的示例代碼中,采用了三種方式來(lái) inflate Item 的布局文件。先不要去關(guān)注上面的寫(xiě)法是否有誤,這里是為了做實(shí)驗(yàn)。

3、為 ListView 設(shè)置 Adapter

mContentLV.setAdapter(new Hack3LVAdapter(this));

看看這三種不同方式所產(chǎn)生的后果:

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

PS:第二種方式和第三種方式其實(shí)是一樣的,可以看源碼:

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

// 就是調(diào)的 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)

有興趣的同學(xué)可以去研究下 LayoutInflate,以及 LayoutInflate 的 inflate 方法各個(gè)參數(shù)的含義和作用,這里就不展開(kāi)了。

這里只是的得出一個(gè)教訓(xùn),不要使用 <merge /> 作為 ListView Item 的根節(jié)點(diǎn)。

注意事項(xiàng)

使用 <merge /> 很簡(jiǎn)單,但在其使用過(guò)程中有些地方需要注意,下面提供一些供參考。

  • <merge /> 只能作為布局的根標(biāo)簽使用
  • 不要使用 <merge /> 作為 ListView Item 的根節(jié)點(diǎn)
  • <merge /> 標(biāo)簽不需要設(shè)置屬性
    寫(xiě)了也不起作用,因?yàn)橄到y(tǒng)會(huì)忽略 <merge /> 標(biāo)簽
  • inflate 以 <merge /> 為根標(biāo)簽的布局時(shí)要注意
  • 必須指定一個(gè)父 ViewGroup
  • 必須設(shè)定 attachToRoot 為 true
    也就是說(shuō) inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 方法的二個(gè)參數(shù) root 不能為 null,并且第三參數(shù) attachToRoot 必須傳 true

項(xiàng)目地址

AndroidHacks合集
布局篇
個(gè)人博客
示例用到代碼見(jiàn):
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 性能優(yōu)化 四 布局優(yōu)化merge標(biāo)簽的使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容