Android布局優化

CarCostello 7年前發布 | 5K 次閱讀 安卓開發 Android開發 移動開發

本文主要記錄Android布局優化的一些小技巧

Android中,如果一個View樹的高度太高,就會嚴重影響測量,布局和繪制的速度,因此可以使用一些方法來降低View樹的高度,提高用戶體驗

目錄

  • 避免使用過多嵌套
  • 重用布局文件<include>、<merge>
  • View延時加載<ViewStub>
  • 工具HierarchyView幫助優化布局(本文沒有介紹具體使用)

避免使用過多嵌套

先來看一個非常常見的效果

相信這樣的效果對我們來說完全不是問題,然而很多人會這樣寫布局

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="5dp">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="10dp"
            android:layout_weight="1"
            android:text="第一項"
            android:textSize="18sp" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:src="@drawable/arrows_right" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#cccccc" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="5dp">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="10dp"
            android:layout_weight="1"
            android:text="第二項"
            android:textSize="18sp" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:src="@drawable/arrows_right" />
    </LinearLayout>
</LinearLayout>

可以看到這里布局嵌套了多層,類似這種效果有更優雅的寫法,效果是完全一樣的

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@drawable/spacer"
    android:orientation="vertical"
    android:showDividers="middle">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableLeft="@mipmap/ic_launcher"
        android:drawablePadding="10dp"
        android:drawableRight="@drawable/arrows_right"
        android:gravity="center_vertical"
        android:padding="5dp"
        android:text="第一項"
        android:textSize="18sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableLeft="@mipmap/ic_launcher"
        android:drawablePadding="10dp"
        android:drawableRight="@drawable/arrows_right"
        android:gravity="center_vertical"
        android:padding="5dp"
        android:text="第二項"
        android:textSize="18sp" />
</LinearLayout>

這里主要用到了LinearLayout的 android:divider="@drawable/spacer" 和 android:showDividers="middle" 兩個屬性添加分割線,注意 android:divider 里面的參數必須是 shape 類型才能顯示。TextView里面則用到了 android:drawableLeft="@mipmap/ic_launcher" 設置文字左邊圖片, android:drawableRight="@drawable/arrows_right" 同理這是文字右邊圖片, android:drawablePadding="10dp" 設置圖片與文字的間距。

我們可以對比一下兩個布局文件,更少的嵌套,更簡潔的代碼,相信你會更喜歡第二種寫法。

重用布局文件<include>、<merge>

<include>

簡單來說 <include> 標簽就是為了一些通用的UI來使用的,有時候我們不想用系統的Toolbar,完全可以自己寫一個滿足我們的需求,先看效果。

這里很簡單的實現了左邊一個圖片,中間標題,右邊一個圖片,一般情況左邊是一個back按鈕,右邊一個action按鈕,這里為了方便演示就直接用系統里的圖片。關鍵代碼片段如下。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@drawable/spacer"
    android:orientation="vertical"
    android:showDividers="middle">

    <RelativeLayout 
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:padding="5dp">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="標題"
            android:textSize="18sp" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:src="@mipmap/ic_launcher" />
    </RelativeLayout>
    ...
</LinearLayout>

由于我們會在多個Activity使用到這個布局,你完全可以每個Activity都拷貝一份上面的代碼,但我相信身為程序猿的你不會那么做,因為如果某一天需求需要改動這個布局,而你每個Activity都需要修改,這時候你是崩潰的。為解決此問題我們就可以用到 <include> 標簽來重用此UI。

首先我們可以新建一個 top_view.xml 布局文件,代碼就是把上面<RelativeLayout>包含的整個提取出來。然后把剛剛的布局替換成 <include> 。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@drawable/spacer"
    android:orientation="vertical"
    android:showDividers="middle">

    <include layout="@layout/top_view" />
    ...
</LinearLayout>

使用 layout="@layout/top_view" 屬性將我們剛剛的布局引入即可。非常簡便,這樣即使需要改動我們只需修改 top_view.xml 里面的內容就能適用整個APP。

<merge>

簡單來說 <merge> 標簽可以有效的減少多余的布局嵌套,這里簡單的舉個栗子。直接關鍵貼代碼

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    ...
    <include layout="@layout/ok_cancel_layouy" />

</LinearLayout>

ok_cancel_layouy.xml

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

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ok" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="cancel" />
</LinearLayout>

這時候我們看下View的層次,拋開最外層的FrameLayout不說,先是LinearLayout接著是 <include> 里面的LinearLayout,最后是兩個Button,這時候內層的LinearLayout其實是多余的,我們可以使用 <merge> 來代替,這樣系統就會忽略 <merge> 直接放下兩個Button。

修改后的 ok_cancel_layouy.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ok" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="cancel" />
</merge>

View延時加載<ViewStub>

上面我們知道了通過 <include> 可以引用通用UI,還可以使用 <ViewStub> 來引用并實現延遲加載。 <ViewStub> 是一個非常輕量級的控件,它沒有大小,沒有繪制,也不參與布局。填寫資料的時候,有時會有更多資料填寫,這時候我們需要實現點擊更多才顯示其他資料,效果如下

點擊more的時候會多出兩個輸入框

布局文件如下

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

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:hint="name" />

    <ViewStub
        android:id="@+id/view_stub"
        android:layout_width="match_parent"
        android:layout="@layout/more_layout"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn_more"
        android:layout_width="wrap_content"
        android:text="more"
        android:layout_height="wrap_content" />
</LinearLayout>

more_layout.xml

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

    <EditText
        android:id="@+id/et_more1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:hint="more1" />

    <EditText
        android:id="@+id/et_more2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:hint="more2" />
</LinearLayout>

點擊more進行顯示

btn_more = (Button) findViewById(R.id.btn_more);
        btn_more.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);
                1.viewStub.setVisibility(View.VISIBLE);
                if (viewStub != null) {
                    2.View inflatedView = viewStub.inflate();
                    et_more1 = (EditText) inflatedView.findViewById(R.id.et_more1);
                    et_more2 = (EditText) inflatedView.findViewById(R.id.et_more2);
                }
            }
        });

有兩種方法可以讓 <ViewStub> 的布局進行顯示,一種是直接設置 viewStub.setVisibility(View.VISIBLE) ,第二種則是使用 inflate 來加載,如要獲取到里面控件,我們一般采用第二種方法。

從上面可以看出 <ViewStub> 的用法類似于 <include> ,不同的是 <ViewStub> 必須指定 layout_width 和 layout_height 屬性,還有一點需要注意 <ViewStub> 所加載的布局是不可以使用 <merge> 標簽的。

 

 

來自:http://www.jianshu.com/p/2b1aa0863cf8

 

 本文由用戶 CarCostello 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!