Android布局優(yōu)化:include 、merge、ViewStub的詳細(xì)總結(jié)

一、include的用法以及注意點(diǎn)

在開(kāi)發(fā)Android布局時(shí),我們常將一些通用的視圖提取到一個(gè)單獨(dú)的layout文件中,然后使用<include>標(biāo)簽在需要使用的其他layout布局文件中加載進(jìn)來(lái),比如我們自己App導(dǎo)航欄等。這樣,便于對(duì)相同視圖內(nèi)容進(jìn)行統(tǒng)一的控制管理,提高布局重用性。

下面我們以大部分項(xiàng)目中都有的頭部導(dǎo)航欄為例,說(shuō)明一下include的使用,比如我們項(xiàng)目自己統(tǒng)一頭部導(dǎo)航欄,抽取布局如下:

titlebar.xml:

<?xml version="1.0"encoding="utf-8"?>

????android:layout_width="match_parent"

????android:layout_height="match_parent" >


?????<Button??

????????android:id="@+id/back"??

????????android:layout_width="wrap_content"??

????????android:layout_height="wrap_content"??

????????android:layout_alignParentLeft="true"??

????????android:layout_centerVertical="true"??

????????android:text="返回按鈕" />??

????<TextView??

????????android:id="@+id/title"??

????????android:layout_width="wrap_content"??

????????android:layout_height="wrap_content"??

????????android:layout_centerInParent="true"??

????????android:text="提示文字"??

????????android:textSize="20sp" />??

????<Button??

????????android:id="@+id/close"??

????????android:layout_width="wrap_content"??

????????android:layout_height="wrap_content"??

????????android:layout_alignParentRight="true"??

????????android:layout_centerVertical="true"??

????????android:text="關(guān)閉按鈕" />??


很簡(jiǎn)單,就是左右各一個(gè)按鈕,中間是一個(gè)提示文字。使用也比較簡(jiǎn)單,如下:

activity_main.xml:

????xmlns:tools="http://schemas.android.com/tools"

????android:layout_width="match_parent"

????android:layout_height="match_parent"

????tools:context="${relativePackage}.${activityClass}" >

????<include

????????android:layout_width="match_parent"

????????android:layout_height="40dp"

????????layout="@layout/titlebar" />

????<Button

????????android:layout_width="wrap_content"

????????android:layout_height="wrap_content"

????????android:layout_centerHorizontal="true"

????????android:layout_centerVertical="true"

????????android:onClick="click"

????????android:text="點(diǎn)我。。。" />

include標(biāo)簽使用還是很簡(jiǎn)單的,主要通過(guò)layout屬性聲明要引入的布局即可。運(yùn)行程序界面如下:

include標(biāo)簽使用注意點(diǎn):

1,標(biāo)簽當(dāng)中,可以重寫(xiě)所有l(wèi)ayout屬性的,如上面include中指定的layout屬性將會(huì)覆蓋掉titlebar中指定的layout屬性。

而非layout屬性則無(wú)法在標(biāo)簽當(dāng)中進(jìn)行覆寫(xiě)。另外需要注意的是,如果我們想要在標(biāo)簽當(dāng)中覆寫(xiě)layout屬性,

必須要將layout_width和layout_height這兩個(gè)屬性也進(jìn)行覆寫(xiě),否則覆寫(xiě)效果將不會(huì)生效

2,一個(gè)xml布局文件有多個(gè)include標(biāo)簽需要設(shè)置ID,才能找到相應(yīng)子View的控件,否則只能找到第一個(gè)include的layout布局,以及該布局的控件。

3,如果我們給include所加載的layout布局的根容器設(shè)置了id屬性,也在include標(biāo)簽中設(shè)置了id屬性,同時(shí)需要在代碼中獲取根容器的控件對(duì)象時(shí),最好將這兩個(gè)id設(shè)置相同的名稱!否則,可能獲取不到根容器對(duì)象,即為null。

二、merge的用法以及注意點(diǎn)

merge標(biāo)簽存在的意義是幫助include標(biāo)簽排除多余的一層ViewGroup容器,減少view hierarchy的結(jié)構(gòu),提升UI渲染的性能。include標(biāo)簽存在著一個(gè)不好的地方,可能會(huì)導(dǎo)致產(chǎn)生多余的布局嵌套。同樣通過(guò)一個(gè)小demo來(lái)說(shuō)明:

比如項(xiàng)目中有一個(gè)公共的登錄按鈕布局,如下:

login.xml:

<?xml version="1.0"encoding="utf-8"?>

????android:layout_width="match_parent"

????android:layout_height="wrap_content"

????android:orientation="vertical" >


?????<Button?

????????android:layout_width="match_parent"??

????????android:layout_height="wrap_content"??

????????android:layout_marginLeft="20dp"??

????????android:layout_marginRight="20dp"??

????????android:text="登錄按鈕" />??


很簡(jiǎn)單,就是一個(gè)登錄的Button。

項(xiàng)目中有登錄功能的UI界面如下:

activity_login.xml:

????xmlns:tools="http://schemas.android.com/tools"

????android:layout_width="match_parent"

????android:layout_height="match_parent"

????android:orientation="vertical"

????android:background="@android:color/holo_blue_light">

????<EditText???

????????android:layout_width="match_parent"??

????????android:layout_height="wrap_content"?

????????android:layout_marginLeft="20dp"??

????????android:layout_marginRight="20dp"??

????????android:layout_marginTop="40dp"??

????????android:hint="請(qǐng)輸入用戶名" />


????<include layout="@layout/login" />

同樣非常簡(jiǎn)單,運(yùn)行程序,如下:

看起來(lái)沒(méi)什么問(wèn)題,其實(shí)不知不覺(jué)中我們多嵌套了一層布局。我們用工具查看一下此時(shí)布局結(jié)構(gòu):

除去系統(tǒng)布局,我們自己布局最外層是LinearLayout,然后兩個(gè)并列布局EditText與LinearLayout,在LinearLayout里面是Button登錄按鈕。

其實(shí)這種情況下:在主界面中,<include>標(biāo)簽的parent ViewGroup與包含的layout根容器ViewGroup是相同的類型,這里都是LinearLayout,那么則可以將包含的layout根容器ViewGroup使用<merge>標(biāo)簽代替,從而減少一層ViewGroup的嵌套,提升UI渲染性能。

這里我們把a(bǔ)ctivity_login.xml修改如下:

<?xml version="1.0"encoding="utf-8"?>

?????<Button?

????????android:layout_width="match_parent"??

????????android:layout_height="wrap_content"??

????????android:layout_marginLeft="20dp"??

????????android:layout_marginRight="20dp"??

????????android:text="登錄按鈕" />??


重新運(yùn)行程序UI和上面一樣效果,通過(guò)工具再次查看布局結(jié)構(gòu);

看到了吧,我們自己布局減少了一層嵌套,從而提升了UI的渲染速度。

merge標(biāo)簽使用注意點(diǎn):

1,根布局是FrameLayout且不需要設(shè)置background或padding等屬性,可以用merge代替,因?yàn)锳ctivity的ContentView父元素就是FrameLayout,所以可以用merge消除只剩一個(gè).

2,因?yàn)閙erge標(biāo)簽并不是View,所以在通過(guò)LayoutInflate.inflate()方法渲染的時(shí)候,第二個(gè)參數(shù)必須指定一個(gè)父容器,且第三個(gè)參數(shù)必須為true,也就是必須為merge下的視圖指定一個(gè)父親節(jié)點(diǎn).由于merge不是View所以****對(duì)merge標(biāo)簽設(shè)置的所有屬性都是無(wú)效的.

LayoutInflate中源碼體現(xiàn):

publicViewinflate(XmlPullParser parser, @Nullable ViewGroup root,booleanattachToRoot){synchronized(mConstructorArgs) {? ? ? ? ? ? ? ? ? ? ? ? ...if(TAG_MERGE.equals(name)) {if(root ==null|| !attachToRoot) {thrownewInflateException("<merge /> can be used only with a valid "+"ViewGroup root and attachToRoot=true");? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? rInflate(parser, root, inflaterContext, attrs,false);? ? ? ? ? ? ? ? }? ? ? ? ? ? ...? ? ? ? }? ? }

3,merge標(biāo)簽必須使用在根布局,并且ViewStub標(biāo)簽中的layout布局不能使用merge標(biāo)簽.

三、ViewStub的用法以及注意點(diǎn)

ViewStub也可以用來(lái)加載布局文件,但與include標(biāo)簽完全不同。ViewStub是一個(gè)不可見(jiàn)的View類,用于在運(yùn)行時(shí)按需懶加載資源,只有在代碼中調(diào)用了viewStub.inflate()或者viewStub.setVisible(View.visible)方法時(shí)才內(nèi)容才變得可見(jiàn)。這里需要注意的一點(diǎn)是,當(dāng)ViewStub被inflate到parent時(shí),ViewStub就被remove掉了,即當(dāng)前view hierarchy中不再存在ViewStub,而是使用對(duì)應(yīng)的layout視圖代替。

同樣我們通過(guò)一個(gè)小demo說(shuō)明一下,比如我們需要保存一個(gè)用戶信息,用戶名是必須保存的,但是其余信息是不必要的,這是其余信息就可以一開(kāi)始不顯示出來(lái),用戶想輸入的時(shí)候在現(xiàn)實(shí)出來(lái)。

其余信息布局如下:

otherinfo.xml:

<?xml version="1.0"encoding="utf-8"?>

????android:layout_width="match_parent"

????android:orientation="vertical"

????android:layout_height="wrap_content" >

????<EditText

????????android:id="@+id/weichat_id"

????????android:layout_width="match_parent"

????????android:layout_height="wrap_content"

????????android:layout_marginLeft="20dp"

????????android:layout_marginRight="20dp"

????????android:layout_marginTop="10dp"

????????android:hint="請(qǐng)輸入微信號(hào)" />

????<EditText

????????android:id="@+id/address_id"

????????android:layout_width="match_parent"

????????android:layout_height="wrap_content"

????????android:layout_marginLeft="20dp"

????????android:layout_marginRight="20dp"

????????android:layout_marginTop="10dp"

????????android:hint="請(qǐng)輸入家庭住址" />

很簡(jiǎn)單,沒(méi)什么其余解釋的,主界面布局如下:

activity_main.xml:

????xmlns:tools="http://schemas.android.com/tools"

????android:layout_width="match_parent"

????android:layout_height="match_parent"

????android:background="@android:color/holo_blue_light"

????android:orientation="vertical" >

????<EditText

????????android:layout_width="match_parent"

????????android:layout_height="wrap_content"

????????android:layout_marginLeft="20dp"

????????android:layout_marginRight="20dp"

????????android:layout_marginTop="40dp"

????????android:hint="請(qǐng)輸入用戶名" />

????<ViewStub

????????android:id="@+id/viewstub"

????????android:layout_width="match_parent"

????????android:layout_height="wrap_content"

????????android:layout="@layout/otherinfo" />

????<Button

????????android:layout_width="match_parent"

????????android:layout_height="wrap_content"

????????android:onClick="show"

????????android:layout_marginLeft="20dp"

????????android:layout_marginRight="20dp"

????????android:layout_marginTop="10dp"

????????android:text="顯示" />

其余信息界面通過(guò)ViewStub引入進(jìn)來(lái),關(guān)于ViewStub主要屬性以及方法說(shuō)明如下:

android:layout屬性

加載包含的layout布局文件;

android:inflatedId屬性

重寫(xiě)包含的layout布局文件的根容器id;

inflate()方法

與setVisible(int)方法作用類似,都可以使內(nèi)容得以顯示,只是inflate()會(huì)返回一個(gè)View對(duì)象,避免了額外使用findViewById()方法獲取layout視圖對(duì)象。

activity中代碼如下:

publicvoidshow(View view){//ViewStub stub = ((ViewStub) findViewById(R.id.viewstub));if(stub!=null){? ? ? ? ? ? View stubView = stub.inflate();? ? ? ? ? ? EditText address = (EditText) stubView.findViewById(R.id.address_id);? ? ? ? ? ? ? EditText wechatId = (EditText) stubView.findViewById(R.id.weichat_id);? ? ? ? ? }? ? }

好了,運(yùn)行程序,一開(kāi)始如下:

點(diǎn)擊顯示按鈕,UI如下:

ViewStub標(biāo)簽使用注意點(diǎn):

1,ViewStub標(biāo)簽不支持merge標(biāo)簽。因此這有可能導(dǎo)致加載出來(lái)的布局存在著多余的嵌套結(jié)構(gòu),具體如何去取舍就要根據(jù)各自的實(shí)際情況來(lái)決定了。

2,ViewStub的inflate只能被調(diào)用一次,第二次調(diào)用會(huì)拋出異常。

3,雖然ViewStub是不占用任何空間的,但是每個(gè)布局都必須要指定layout_width和layout_height屬性,否則運(yùn)行就會(huì)報(bào)錯(cuò)。

好了,以上就是個(gè)人對(duì)于include 、merge、ViewStub使用的總結(jié),希望對(duì)你有用,即使已經(jīng)掌握,希望讀完此文能溫故知新

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容