Android 佈局優化--merge標籤

性能優化之一就是layout的優化,

as 常識:

佈局是否合理主要影響的是頁面測量時間的多少,我們知道一個頁面的顯示測量和繪製過程都是通過遞歸來完成的,多叉樹遍歷的時間與樹的高度h有關,其時間複雜度 O(h),如果層級太深,每增加一層則會增加更多的頁面顯示時間,所以佈局的合理性就顯得很重要。

那佈局優化有哪些方法呢,主要通過減少層級、減少測量和繪製時間、提高複用性三個方面入手。總結如下:

  • 減少層級。合理使用 RelativeLayout 和 LinerLayout,合理使用Merge。
  • 提高顯示速度。使用 ViewStub,它是一個看不見的、不佔佈局位置、佔用資源非常小的視圖對象。
  • 佈局複用。可以通過includ標籤來提高複用。
  • 儘可能少用wrap_content。wrap_content 會增加布局 measure 時計算成本,在已知寬高爲固定值時,不用wrap_content 。
  • 刪除控件中無用的屬性。


這裏主要說第一點的merge。

對比一下吧,使用merge之前,和使用merge之後的效果:第一張圖片是沒有使用merge的,第二章是使用merge的。

對應佈局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.spreadtrumshitaoli.layoutoptimize.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="Not merge"
        android:textSize="30dp"/>

    

</RelativeLayout>


對應佈局:

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.spreadtrumshitaoli.layoutoptimize.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="Not merge"
        android:textSize="30dp"/>



</merge>

可以看到少了RelativeLayout 這一層佈局。


現在認識到merge可以起到優化作用,辣麼,什麼場景下用好呢?爲什麼merge可以優化佈局,但是大多數layout沒有使用merge呢?

  1. 如果Activity的佈局文件根節點是FrameLayout,可以替換爲merge標籤,這樣,執行setContentView之後,會減少一層FrameLayout節點。
  2. 自定義View如果繼承LinearLayout,建議讓自定義View的佈局文件根節點設置成merge,這樣能少一層結點。
  3. 知道當前父佈局的佈局是什麼,可以使用merge並添加相應的layout屬性(正規軍(源碼中)沒有使用),推薦星爲0星,不過也是一個方案。

針對3,舉個例子:

剛纔的例子,relativelayout中,有屬性android:layout_alignParentRight 等,那麼子添加到這個layout中的merge裏,你也可以寫這個屬性。就在圖1對應的layout中,我們

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.spreadtrumshitaoli.layoutoptimize.MainActivity">


    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="Not merge"
        android:textSize="30dp"/>

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

</RelativeLayout>

merge_tag如下:其中的android:layout_alignParentBottom="true" 是有效的。

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">

    <TextView
        android:text="@string/app_name"
        android:layout_alignParentBottom="true"
        android:textColor="@android:color/holo_green_light"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"/>

</merge>

這種情況不推薦使用。

主要記住1、2的使用場景即可,其實merge標籤使用並不太多,更多的是include和viewstub這兩個。

下篇文章會介紹一下include及viewstub。


merge爲什麼會這樣?父佈局的屬性,merge裏可以使用,merge到底是view、viewgroup還是什麼呢,它是不是個layout?

使用的時候,有沒有要注意的點呢?

先來看看merge標籤:

在layoutinflater中,有源碼

if (TAG_MERGE.equals(name)) {
                    if (root == null || !attachToRoot) {
                        throw new InflateException("<merge /> can be used only with a valid "
                                + "ViewGroup root and attachToRoot=true");
                    }

                    rInflate(parser, root, inflaterContext, attrs, false);
                } 

其中,rInflate中部分代碼如下:

{
                final View view = createViewFromTag(parent, name, context, attrs);
                final ViewGroup viewGroup = (ViewGroup) parent;
                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                rInflateChildren(parser, view, attrs, true);
                viewGroup.addView(view, params);
            }

可以看到,會把merge解析到成一個view(注意是解析,merge並不是view,只不過吧內容解析了,因爲merge裏面的是view),然後add到父佈局中,帶的param是父佈局的。所以上面的3點也就得到解釋了。

注意的點:

  1. merge必須放在佈局文件的根節點上。
  2. merge並不是一個ViewGroup,也不是一個View,它相當於聲明瞭一些視圖,等待被添加。
  3. merge標籤被添加到A容器下,那麼merge下的所有視圖將被添加到A容器下。
  4. 因爲merge標籤並不是View,所以在通過LayoutInflate.inflate方法渲染的時候, 第二個參數必須指定一個父容器,且第三個參數必須爲true,也就是必須爲merge下的視圖指定一個父親節點。(其中第三個參數可以省略:源碼:
    public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    
        return inflate(resource, root, root != null);
    }

android 源碼例子:

  • only in /sprdroid8.1_trunk/frameworks/base/tests/SharedLibrary/lib/res/layout/
1<?xml version="1.0" encoding="utf-8"?>
2
3<merge xmlns:android="http://schemas.android.com/apk/res/android">
4    <TextView android:id="@+id/name"
5        android:layout_width="wrap_content"
6        android:layout_height="wrap_content"/>
7    <TextView android:id="@+id/street"
8        android:layout_width="wrap_content"
9        android:layout_height="wrap_content"/>
10    <TextView android:id="@+id/cityStateZip"
11        android:layout_width="wrap_content"
12        android:layout_height="wrap_content"/>
13    <TextView android:id="@+id/country"
14        android:layout_width="wrap_content"
15        android:layout_height="wrap_content"/>
16</merge>

sprdroid8.1_trunk/frameworks/base/tests/SharedLibrary/lib/src/com/google/android/test/shared_library/AddressView.java

public classAddressView extends LinearLayout

View view = LayoutInflater.from(context).inflate(R.layout.address, this);



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章