版權聲明:本文出自汪磊的博客,未經作者允許禁止轉載。
本篇博客主要是對上篇博客的補充Android性能優化之UI渲染性能優化, 沒有什么新東西,覺得應該是都掌握的玩意,寫出來也只是自己做個小小的總結。
一、include的用法以及注意點
在開發Android布局時,我們常將一些通用的視圖提取到一個單獨的layout文件中,然后使用<include>
標簽在需要使用的其他layout布局文件中加載進來,比如我們自己App導航欄等。這樣,便於對相同視圖內容進行統一的控制管理,提高布局重用性。
下面我們以大部分項目中都有的頭部導航欄為例,說明一下include的使用,比如我們項目自己統一頭部導航欄,抽取布局如下:
titlebar.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" > 5 6 <Button 7 android:id="@+id/back" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:layout_alignParentLeft="true" 11 android:layout_centerVertical="true" 12 android:text="返回按鈕" /> 13 14 <TextView 15 android:id="@+id/title" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:layout_centerInParent="true" 19 android:text="提示文字" 20 android:textSize="20sp" /> 21 22 <Button 23 android:id="@+id/close" 24 android:layout_width="wrap_content" 25 android:layout_height="wrap_content" 26 android:layout_alignParentRight="true" 27 android:layout_centerVertical="true" 28 android:text="關閉按鈕" /> 29 30 </RelativeLayout>
很簡單,就是左右各一個按鈕,中間是一個提示文字。使用也比較簡單,如下:
activity_main.xml:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 tools:context="${relativePackage}.${activityClass}" > 6 7 <include 8 android:layout_width="match_parent" 9 android:layout_height="40dp" 10 layout="@layout/titlebar" /> 11 12 <Button 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:layout_centerHorizontal="true" 16 android:layout_centerVertical="true" 17 android:onClick="click" 18 android:text="點我。。。" /> 19 20 </RelativeLayout>
include標簽使用還是很簡單的,主要通過layout屬性聲明要引入的布局即可。運行程序界面如下:

include標簽使用注意點:
1,<include>標簽當中,可以重寫所有layout屬性的,如上面include中指定的layout屬性將會覆蓋掉titlebar中指定的layout屬性。
而非layout屬性則無法在<include>標簽當中進行覆寫。另外需要注意的是,如果我們想要在<include>標簽當中覆寫layout屬性,
必須要將layout_width和layout_height這兩個屬性也進行覆寫,否則覆寫效果將不會生效
2,一個xml布局文件有多個include標簽需要設置ID,才能找到相應子View的控件,否則只能找到第一個include的layout布局,以及該布局的控件。
3,如果我們給include所加載的layout布局的根容器設置了id屬性,也在include標簽中設置了id屬性,同時需要在代碼中獲取根容器的控件對象
時,最好將這兩個id設置相同的名稱!否則,可能獲取不到根容器對象,即為null。
二、merge的用法以及注意點
merge
標簽存在的意義是幫助include
標簽排除多余的一層ViewGroup容器,減少view hierarchy的結構,提升UI渲染的性能。include標簽存在着一個不好的地方,可能會導致產生多余的布局嵌套。同樣通過一個小demo來說明:
比如項目中有一個公共的登錄按鈕布局,如下:
login.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:orientation="vertical" > 6 7 <Button 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:layout_marginLeft="20dp" 11 android:layout_marginRight="20dp" 12 android:text="登錄按鈕" /> 13 14 </LinearLayout>
很簡單,就是一個登錄的Button。
項目中有登錄功能的UI界面如下:
activity_login.xml:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 android:background="@android:color/holo_blue_light"> 7 8 <EditText 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:layout_marginLeft="20dp" 12 android:layout_marginRight="20dp" 13 android:layout_marginTop="40dp" 14 android:hint="請輸入用戶名" /> 15 16 <include layout="@layout/login" /> 17 18 </LinearLayout>
同樣非常簡單,運行程序,如下:
看起來沒什么問題,其實不知不覺中我們多嵌套了一層布局。我們用工具查看一下此時布局結構:
除去系統布局,我們自己布局最外層是LinearLayout,然后兩個並列布局EditText與LinearLayout,在LinearLayout里面是Button登錄按鈕。
其實這種情況下:在主界面中,<include>
標簽的parent ViewGroup與包含的layout根容器ViewGroup是相同的類型,這里都是LinearLayout,那么則可以將包含的layout根容器ViewGroup使用<merge>
標簽代替,從而減少一層ViewGroup的嵌套,提升UI渲染性能。
這里我們把activity_login.xml修改如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <merge xmlns:android="http://schemas.android.com/apk/res/android"> 3 4 <Button 5 android:layout_width="match_parent" 6 android:layout_height="wrap_content" 7 android:layout_marginLeft="20dp" 8 android:layout_marginRight="20dp" 9 android:text="登錄按鈕" /> 10 11 </merge>
重新運行程序UI和上面一樣效果,通過工具再次查看布局結構;
看到了吧,我們自己布局減少了一層嵌套,從而提升了UI的渲染速度。
merge標簽使用注意點:
1,根布局是FrameLayout且不需要設置background或padding等屬性,可以用merge代替,因為Activity的ContentView父元素就是FrameLayout,所以可以用merge消除只剩一個.
2,因為merge標簽並不是View,所以在通過LayoutInflate.inflate()方法渲染的時候,第二個參數必須指定一個父容器,且第三個參數必須為true,也就是必須為merge下的視圖指定一個父親節點.由於merge不是View所以對merge標簽設置的所有屬性都是無效的.
LayoutInflate中源碼體現:
1 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { 2 synchronized (mConstructorArgs) { 3 4 ... 5 6 if (TAG_MERGE.equals(name)) { 7 if (root == null || !attachToRoot) { 8 throw new InflateException("<merge /> can be used only with a valid " 9 + "ViewGroup root and attachToRoot=true"); 10 } 11 12 rInflate(parser, root, inflaterContext, attrs, false); 13 } 14 ... 15 } 16 }
3,merge標簽必須使用在根布局,並且ViewStub標簽中的layout布局不能使用merge標簽.
三、ViewStub的用法以及注意點
ViewStub也可以用來加載布局文件,但與include標簽完全不同。ViewStub是一個不可見的View類,用於在運行時按需懶加載資源,只有在代碼中調用了viewStub.inflate()
或者viewStub.setVisible(View.visible)
方法時才內容才變得可見。這里需要注意的一點是,當ViewStub被inflate到parent時,ViewStub就被remove掉了,即當前view hierarchy中不再存在ViewStub,而是使用對應的layout視圖代替。
同樣我們通過一個小demo說明一下,比如我們需要保存一個用戶信息,用戶名是必須保存的,但是其余信息是不必要的,這是其余信息就可以一開始不顯示出來,用戶想輸入的時候在現實出來。
其余信息布局如下:
otherinfo.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:orientation="vertical" 5 android:layout_height="wrap_content" > 6 7 <EditText 8 android:id="@+id/weichat_id" 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:layout_marginLeft="20dp" 12 android:layout_marginRight="20dp" 13 android:layout_marginTop="10dp" 14 android:hint="請輸入微信號" /> 15 16 <EditText 17 android:id="@+id/address_id" 18 android:layout_width="match_parent" 19 android:layout_height="wrap_content" 20 android:layout_marginLeft="20dp" 21 android:layout_marginRight="20dp" 22 android:layout_marginTop="10dp" 23 android:hint="請輸入家庭住址" /> 24 25 </LinearLayout>
很簡單,沒什么其余解釋的,主界面布局如下:
activity_main.xml:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:background="@android:color/holo_blue_light" 6 android:orientation="vertical" > 7 8 <EditText 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content" 11 android:layout_marginLeft="20dp" 12 android:layout_marginRight="20dp" 13 android:layout_marginTop="40dp" 14 android:hint="請輸入用戶名" /> 15 16 <ViewStub 17 android:id="@+id/viewstub" 18 android:layout_width="match_parent" 19 android:layout_height="wrap_content" 20 android:layout="@layout/otherinfo" /> 21 22 <Button 23 android:layout_width="match_parent" 24 android:layout_height="wrap_content" 25 android:onClick="show" 26 android:layout_marginLeft="20dp" 27 android:layout_marginRight="20dp" 28 android:layout_marginTop="10dp" 29 android:text="顯示" /> 30 31 </LinearLayout>
其余信息界面通過ViewStub引入進來,關於ViewStub主要屬性以及方法說明如下:
-
android:layout
屬性
加載包含的layout布局文件; -
android:inflatedId
屬性
重寫包含的layout布局文件的根容器id; -
inflate()
方法
與setVisible(int)
方法作用類似,都可以使內容得以顯示,只是inflate()
會返回一個View對象,避免了額外使用findViewById()
方法獲取layout視圖對象。
activity中代碼如下:
1 public void show(View view){ 2 // 3 ViewStub stub = ((ViewStub) findViewById(R.id.viewstub)); 4 if(stub!=null){ 5 View stubView = stub.inflate(); 6 EditText address = (EditText) stubView.findViewById(R.id.address_id); 7 EditText wechatId = (EditText) stubView.findViewById(R.id.weichat_id); 8 } 9 }
好了,運行程序,一開始如下:
點擊顯示按鈕,UI如下:
ViewStub標簽使用注意點:
1,ViewStub標簽不支持merge標簽。因此這有可能導致加載出來的布局存在着多余的嵌套結構,具體如何去取舍就要根據各自的實際情況來決定了。
2,ViewStub的inflate只能被調用一次,第二次調用會拋出異常。
3,雖然ViewStub是不占用任何空間的,但是每個布局都必須要指定layout_width和layout_height屬性,否則運行就會報錯。
好了,以上就是個人對於include 、merge、ViewStub使用的總結,希望對你有用,即使已經掌握,希望讀完此文能溫故知新。