佈局優化include與merge

尊重原創,轉載請註明出處:http://blog.csdn.net/a740169405/article/details/50473909

Android 官方提供了三個用來優化佈局的標籤,分別是include、merge與ViewStub,其中ViewStub是動態加載視圖到內存,大家可以查閱:Android UI佈局優化之ViewStub

一、include佈局重用:

在Android的應用程序開發中,標題欄是必不可少的一個元素,大部分頁面都要用到,而且佈局都是一樣的,這時候使用include標籤就顯得極其的方便。使用時通常需要注意以下幾點。

  1. include標籤的layout_*屬性會替換掉被include視圖的根節點的對應屬性。
  2. include標籤的id屬性會替換掉被include視圖的根節點id
  3. 一個佈局文件中支持include多個視圖,但是這樣會導致獲取被include視圖內的控件時, 解決方法請參考:www.coboltforge.com/2012/05/tech-stuff-layout/

下面例子中,titlebar_layout.xml爲標題欄佈局,而activity_main.xml爲主界面佈局,activity_setting.xml爲設置頁面佈局,這這兩個界面中都include了titlebar_layout.xml視圖。 titlebar_layout.xml: titlebar_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/preference_activity_title_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="2dip"
    android:background="@drawable/zns_activity_title_bg">

    <TextView
        android:id="@+id/preference_activity_title_text"
        android:layout_width="match_parent"
        android:layout_height="45dip"
        android:gravity="center"
        android:text="123"
        android:textColor="#ffffff"
        android:textSize="18sp" />

    <ImageView
        android:id="@+id/preference_activity_title_image"
        android:layout_width="30dip"
        android:layout_height="25dip"
        android:layout_gravity="center_vertical"
        android:scaleType="fitCenter"
        android:layout_marginLeft="5dip"
        android:src="@drawable/common_menu_selector_white" />
</FrameLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

主界面:
主界面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#000000">

    <include layout="@layout/titlebar_layout"></include>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="這是內容區域"
        android:gravity="center"
        android:textSize="25sp"
        android:textColor="#ffffff"/>
</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

當然,其他界面使用include同樣能包含該標題欄。

一、通過merge減少視圖節點:

merge翻譯成中文是合併的意思,在Android中通過使用merge能夠減少視圖的節點數,
從而減少視圖在繪製過程消耗的時間,達到提高UI性能的效果。使用merge時通常需要注意以下幾點:

  1. merge必須放在佈局文件的根節點上。
  2. merge並不是一個ViewGroup,也不是一個View,它相當於聲明瞭一些視圖,等待被添加。
  3. merge標籤被添加到A容器下,那麼merge下的所有視圖將被添加到A容器下。
  4. 因爲merge標籤並不是View,所以在通過LayoutInflate.inflate方法渲染的時候, 第二個參數必須指定一個父容器,且第三個參數必須爲true,也就是必須爲merge下的視圖指定一個父親節點。
  5. 如果Activity的佈局文件根節點是FrameLayout,可以替換爲merge標籤,這樣,執行setContentView之後,會減少一層FrameLayout節點。
  6. 自定義View如果繼承LinearLayout,建議讓自定義View的佈局文件根節點設置成merge,這樣能少一層結點。
  7. 因爲merge不是View,所以對merge標籤設置的所有屬性都是無效的。

其中第一點,我們看看LayoutInflate類的源碼說明:

} else if (TAG_MERGE.equals(name)) {
    // 如果merge不是根節點,報錯
    throw new InflateException("<merge /> must be the root element");
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

其中第三點,常用在自定義View中遇到,附上系統LayoutInflate類,對於該現象的源碼:

if (TAG_MERGE.equals(name)) {
    // 如果是merge標籤,指定的root爲空,或則attachToRoot爲false,則拋出異常信息
    if (root == null || !attachToRoot) {
        throw new InflateException("<merge /> can be used only with a valid "
                + "ViewGroup root and attachToRoot=true");
    }

    rInflate(parser, root, attrs, false, false);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

針對第五點,做一下對比:
佈局文件1:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:text="頂部Button" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="底部Button" />

</merge>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

效果1:
效果一

佈局文件2:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="top"
        android:text="頂部Button" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="底部Button" />

</FrameLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

效果2:
效果二

我們可以看到,如果使用merge,明顯少了一個FrameLayout節點,這也算一個視圖優化技巧。

下面對第六條(自定義View如果繼承LinearLayout,建議讓自定義View的佈局文件根節點設置成merge,這樣能少一層結點)進行分析:
先看看效果,就是一個線性佈局,上下各一個TextView,看看使用merge和不使用merge的視圖節點,
以及使用merge的時候layoutInflate類的注意點。
效果圖:
效果圖
第一種情況(不使用merge):
佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#000000"
        android:gravity="center"
        android:text="第一個TextView"
        android:textColor="#ffffff" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#ffffff"
        android:gravity="center"
        android:text="第一個TextView"
        android:textColor="#000000" />

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

代碼:

/**
 * 自定義的View,豎直方向的LinearLayout
 */
public class MergeLayout extends LinearLayout {

    public MergeLayout(Context context) {
        super(context);
        LayoutInflater.from(context).inflate(R.layout.merge_activity, this, true);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

視圖樹:
視圖樹一

我們發現,MergeLayout這個自定義控件的下面並不是直接跟着兩個TextView,
而是多了一個LinearLayout。

第二種情況(使用merge):
注意因爲爲merge標籤的設置的屬性都不會生效,所以原來LinearLayout標籤上的屬性需要轉移到Java代碼中設置。
佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<!-- 習慣性的標記一下,MergeLayout佈局 android:orientation="vertical" -->
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#000000"
        android:gravity="center"
        android:text="第一個TextView"
        android:textColor="#ffffff" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="#ffffff"
        android:gravity="center"
        android:text="第一個TextView"
        android:textColor="#000000" />

</merge>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

個人習慣在用merge的時候在旁邊標明使用到的屬性,以防忘記。

java代碼中需要設置orientation屬性:

/**
 * 自定義的View,豎直方向的LinearLayout
 */
public class MergeLayout extends LinearLayout {

    public MergeLayout(Context context) {
        super(context);
        // 設置爲數值方向的佈局
        setOrientation(VERTICAL);
        LayoutInflater.from(context).inflate(R.layout.merge_activity, this, true);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

再看看視圖樹:
視圖樹二
我們發現,LinearLayout節點被去掉了。但是最終顯示給用戶的界面卻是一樣的。

總結

1. 使用include標籤可以增加布局的複用性,提高效率。
2. 使用merge標籤可以減少視圖樹中的節點個數,加快視圖的繪製,提高UI性能。
3. merge標籤的使用,看上去一次只減少一個節點,但是當一個佈局嵌套很複雜的時候,
節點的個數可能達到幾百個,這個時候,如果每個地方都多一個節點,視圖的繪製時間相應的也就變長了很多。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章