在開發(fā)中UI布局是我們都會遇到的問題,隨著UI越來越多省艳,布局的重復(fù)性娘纷、復(fù)雜度也會隨之增長。對此我們優(yōu)化xml布局就不得不說重用布局跋炕,為了有效地重新使用完整的布局赖晶,Google提出可以使用<include>和<merge>這兩個(gè)非常有用的標(biāo)簽,用以在當(dāng)前布局中嵌入另一個(gè)布局辐烂,下面我們就來逐個(gè)學(xué)習(xí)一下遏插。
一、include
<include/>標(biāo)簽可以允許在一個(gè)布局當(dāng)中引入另外一個(gè)布局纠修,那么比如說我們程序的所有界面都有一個(gè)公共的部分胳嘲,這個(gè)時(shí)候最好的做法就是將這個(gè)公共的部分提取到一個(gè)獨(dú)立的布局文件當(dāng)中,然后在每個(gè)界面的布局文件當(dāng)中來引用這個(gè)公共的布局扣草。這里舉個(gè)例子吧了牛,例如在include_layout.xml添加兩個(gè)按鈕:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button One"
android:id="@+id/button" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button Two"
android:id="@+id/button2" />
</LinearLayout>
好的,那這連個(gè)按鈕作為一個(gè)獨(dú)立的布局現(xiàn)在我們已經(jīng)編寫完了辰妙,接下來的工作就非常簡單了白魂,無論任何界面需要加入這個(gè)布局,只需要在布局文件中引入include_layout.xml就可以了上岗。那么比如說我們的程序當(dāng)中有一個(gè)activity_main.xml文件福荸,現(xiàn)在想要引入include_layout.xml只需要這樣寫:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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="Button"
android:onClick="onClick"
android:id="@+id/button1"/>
<include layout="@layout/include_layout"/>
</LinearLayout>
顯示的效果為:
看上去效果非常不錯(cuò)對嗎? 可是在你毫無察覺的情況下,目前activity_main.xml這個(gè)界面當(dāng)中其實(shí)已經(jīng)存在著多余的布局嵌套了肴掷!感覺還沒寫幾行代碼呢敬锐,怎么這就已經(jīng)有多余的布局嵌套了?不信的話我們可以通過UI Automator Viewer工具來查看一下呆瞻,如下圖所示:
可以看到台夺,最外層首先是一個(gè)FrameLayout,至于為什么是FrameLayout痴脾,不知道的朋友可以去參考 Android LayoutInflater原理分析颤介,帶你一步步深入了解View(一) 這篇文章。然后FrameLayout中包含的是一個(gè)LinearLayout赞赖,這個(gè)就是我們在activity_main.xml中定義的最外層布局了滚朵。
二、merge
通過上面內(nèi)容前域,相信大家已經(jīng)可以看出來了吧辕近,在使用<include/>直接引入布局的時(shí)候,這個(gè)內(nèi)部的LinearLayout其實(shí)就是一個(gè)多余的布局嵌套匿垄,實(shí)際上并不需要這樣一層移宅,讓兩個(gè)按鈕直接包含在外部的LinearLayout當(dāng)中就可以了归粉。而這個(gè)多余的布局嵌套其實(shí)就是由于布局引入所導(dǎo)致的,因?yàn)槲覀冊?strong>include_layout.xml中也定義了一個(gè)LinearLayout漏峰。那么應(yīng)該怎樣優(yōu)化掉這個(gè)問題呢糠悼?請接著往下看當(dāng)然就是使用<merge/>標(biāo)簽了,現(xiàn)在修改include_layout.xml布局如下:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button One"
android:id="@+id/button" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button Two"
android:id="@+id/button2" />
</merge>
可以看到浅乔,這里我們將include_layout.xml最外層的LinearLayout布局刪除掉绢掰,換用了<merge/>標(biāo)簽,這就表示當(dāng)有任何一個(gè)地方去include這個(gè)布局時(shí)童擎,會將<merge/>標(biāo)簽內(nèi)包含的內(nèi)容直接填充到include的位置滴劲,不會再添加任何額外的布局結(jié)構(gòu)。好的顾复,<merge/>的用法就是這么簡單班挖,現(xiàn)在重新運(yùn)行一下程序,你會看到界面沒有任何改變芯砸,然后我們再通過UI Automator Viewer工具來查看一下當(dāng)前的View結(jié)構(gòu)萧芙,如下圖所示:
好了,可以看到假丧,現(xiàn)在兩個(gè)按鈕都直接包含在了LinearLayout下面双揪,我們的include_layout.xml當(dāng)中也就不存在多余的布局嵌套了。
三包帚、ViewStub
對于ViewStub(請自備梯子)可以理解為僅在需要時(shí)才加載布局渔期。
ViewStub是一個(gè)輕量級的View,它一個(gè)看不見的渴邦,不占布局位置疯趟,占用資源非常小的控件∧彼螅可以為ViewStub指定一個(gè)布局信峻,在Inflate布局的時(shí)候,只有ViewStub會被初始化瓮床,然后當(dāng)ViewStub被設(shè)置為可見的時(shí)候盹舞,或是調(diào)用了ViewStub.inflate()的時(shí)候,ViewStub所向的布局就會被Inflate和實(shí)例化隘庄,然后ViewStub的布局屬性都會傳給它所指向的布局踢步。這樣,就可以使用ViewStub來方便的在運(yùn)行時(shí)峭沦,要不要顯示某個(gè)布局贾虽。
但ViewStub也不是萬能的,下面總結(jié)下ViewStub能做的事兒和什么時(shí)候該用ViewStub吼鱼,什么時(shí)候該用可見性的控制蓬豁。
首先來說說ViewStub的一些特點(diǎn):
- ViewStub只能Inflate一次,之后ViewStub對象會被置為空菇肃。按句話說地粪,某個(gè)被ViewStub指定的布局被Inflate后,就不會夠再通過ViewStub來控制它了琐谤。
- ViewStub只能用來Inflate一個(gè)布局文件蟆技,而不是某個(gè)具體的View,當(dāng)然也可以把View寫在某個(gè)布局文件中斗忌。
基于以上的特點(diǎn)质礼,那么可以考慮使用ViewStub的情況有:
- 在程序的運(yùn)行期間,某個(gè)布局在Inflate后织阳,就不會有變化眶蕉,除非重新啟動。因?yàn)閂iewStub只能Inflate一次唧躲,之后會被置空造挽,所以無法指望后面接著使用ViewStub來控制布局。所以當(dāng)需要在運(yùn)行時(shí)不止一次的顯示和隱藏某個(gè)布局弄痹,那么ViewStub是做不到的饭入。這時(shí)就只能使用View的可見性來控制了。
- 想要控制顯示與隱藏的是一個(gè)布局文件肛真,而非某個(gè)View谐丢。因?yàn)樵O(shè)置給ViewStub的只能是某個(gè)布局文件的Id,所以無法讓它來控制某個(gè)View蚓让。
所以庇谆,如果想要控制某個(gè)View(如Button或TextView)的顯示與隱藏,或者想要在運(yùn)行時(shí)不斷的顯示與隱藏某個(gè)布局或View凭疮,只能使用View的可見性來控制饭耳。接下來修改include_layout.xml布局如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
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="Button"
android:onClick="onClick"
android:id="@+id/button1"/>
<ViewStub
android:id="@+id/view_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/include_layout"/>
</LinearLayout>
現(xiàn)在看看MainActivity的代碼:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
void onClick(View view){
ViewStub viewStub= (ViewStub) findViewById(view_stub);
if (viewStub!=null){
viewStub.inflate();
}
}
}
最后來看看使用ViewStub的效果:
點(diǎn)擊button就會調(diào)用ViewStub的inflate();方法來加載我們需要的布局
最后使用ViewStub需要注意:
- 某些布局屬性要加在ViewStub而不是實(shí)際的布局上面,才會起作用执解,比如上面用的android:layout_margin*系列屬性寞肖,如果加在TextView上面,則不會起作用衰腌,需要放在它的ViewStub上面才會起作用新蟆。而ViewStub的屬性在inflate()后會都傳給相應(yīng)的布局。
附上UI Automator Viewer工具的打開方法:AndroidSDK > tools > uiautomatorviewer.bat
最后右蕊,如果大家想要繼續(xù)學(xué)習(xí)更多關(guān)于性能優(yōu)化的技巧琼稻,可以到這個(gè)網(wǎng)址上閱讀更多內(nèi)容 http://developer.android.com/training/best-performance.html 。