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