當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > 性能優(yōu)化之布局文件優(yōu)化
隨著android手機(jī)的迅速發(fā)展以及技術(shù)的更新,市場對(duì)應(yīng)用的性能提出了更高的要求,android圖形界面是用戶直接的感受渠道,因此做好UI界面的設(shè)計(jì)日益重要。而其實(shí)相同的布局效果可以有不同的實(shí)現(xiàn)方式。糟糕的布局會(huì)使程序加載UI的速度非常慢,從而降低了用戶體驗(yàn)滿意度,同時(shí)也對(duì)程序的性能產(chǎn)生比較大的影響,因此優(yōu)化布局是提高性能的一種重要途徑,以下從三方面講述布局的三重優(yōu)化。
第一重優(yōu)化:布局中樣式的抽取
布局文件中,系統(tǒng)給我們提供了很多控件的屬性,我們可以方便的使用,但有時(shí)在同一個(gè)布局中甚至不同的布局中會(huì)重復(fù)出現(xiàn)同一個(gè)控件并且其屬性設(shè)置相似,這時(shí)如果我們重復(fù)的單個(gè)定義相同控件的屬性會(huì)使布局代碼顯得龐大冗雜,減低了布局文件的可讀性同時(shí)開發(fā)的效率和程序性能也會(huì)受很大的影響,這就需要我們將相同控件的相同屬性抽取出來做成樣式模板。
舉例1:
同一個(gè)布局或不同布局文件中常常會(huì)用到控件顯示文字比如TextView,Button等,而這時(shí)候?qū)τ诳丶拇笮〖帮@示的文字的大小和顏色等要求又是相同的,這時(shí)我們就可以將這些相同的屬性抽取出來。如下圖布局是一個(gè)簡單的LinearLayout布局中的三個(gè)控件中間一個(gè)Button,兩邊各一個(gè)TextView,看代碼不難發(fā)現(xiàn)他們的尺寸,背景,和字體大小顏色都是相同的,這時(shí)我們則可以抽取出來。
將控件相同的樣式抽取到values文件夾下的style.xml文件中如下圖紅色圈住部分:
抽取之后,布局文件中我們只需引入此樣式即可,如果其他控件有個(gè)別屬性不同,則只需在布局文件中重寫此屬性,系統(tǒng)會(huì)自動(dòng)將布局文件中的屬性覆蓋抽取出的style屬性,如下圖:
此時(shí)布局則大大簡化,效果則完全相同,如下圖:
第二重優(yōu)化:布局文件復(fù)用
布局文件的復(fù)用思想和樣式抽取的思想是相同的,都是將相同的東西抽取出來以達(dá)到可以重復(fù)利用的目的。當(dāng)然Android也已經(jīng)充分考慮到了布局重用的重要性,于是提供了
1.<include>
<include>
舉例2:我們應(yīng)該都知道,目前幾乎所有的軟件都會(huì)有一個(gè)頭布局,頭布局中可以包含界面的標(biāo)題、返回按鈕、以及其它一些操作功能等。那這樣的一個(gè)頭布局,有些軟件是使用ActionBar來實(shí)現(xiàn)的,但是由于ActionBar的靈活性不太好,因而也有很多軟件會(huì)選擇自己去編寫實(shí)現(xiàn)。那如果自己去實(shí)現(xiàn)的話,由于這個(gè)頭布局是在所有界面都要使用的,顯然我們不可能在每個(gè)界面當(dāng)中都去寫一遍這個(gè)頭布局的代碼,因此這種情況下使用
titlebar.xml中的布局非常簡單,外層RelativeLayout里面只有兩個(gè)Button和一個(gè)TextView,左邊Button用于實(shí)現(xiàn)返回功能,右邊的Button用于實(shí)現(xiàn)完成功能,中間的TextView則可以用于顯示當(dāng)前界面的標(biāo)題。我們可以來預(yù)覽一下titlebar的樣子,如下圖所示:
titlebar的布局寫完,接下來就可以對(duì)布局文件進(jìn)行復(fù)用了,無論任何界面需要加入titlebar這個(gè)功能,只需要在布局文件中引入titlebar.xml就可以了。比如我們的程序當(dāng)中有一個(gè)activity_main.xml文件,現(xiàn)在想要引入titlebar,布局文件只需要這樣寫:
這樣就非常簡單,一行include語句就搞定了,而無需重復(fù)寫titlebar里的布局。
在
這時(shí)重新運(yùn)行一下則是可以正常顯示titlebar之外的其他內(nèi)容的。
除了layout_height之外,我們還可以覆寫titlebar中的任何一個(gè)layout屬性,如layout_gravity、layout_margin等,而非layout屬性則無法在
2.
<merge>標(biāo)簽是作為
可以看到,這個(gè)界面也是非常簡單,外層是一個(gè)水平方向LinearLayout,LinearLayout中包含了兩個(gè)按鈕,一個(gè)用于實(shí)現(xiàn)確定功能,一個(gè)用于實(shí)現(xiàn)取消功能,F(xiàn)在我們可以來預(yù)覽一下這個(gè)界面,如下圖所示:
然后我們有一個(gè)profile.xml的界面需要編輯一些內(nèi)容,那么這里就可以將ok_cancel_layout這個(gè)布局引入到profile.xml界面當(dāng)中,如下所示:
運(yùn)行效果如圖:
這個(gè)看上去并沒有什么不對(duì),可是在你毫無察覺的情況下,目profile.xml這個(gè)界面當(dāng)中其實(shí)已經(jīng)存在著多余的布局嵌套了!感覺還沒寫幾行代碼呢,怎么這就已經(jīng)有多余的布局嵌套了?我們可以通過View Hierarchy工具來查看一下,如下圖所示:
可以看到,外層首先是一個(gè)FrameLayout,然后FrameLayout中包含的是一個(gè)LinearLayout,這個(gè)就是我們?cè)趐rofile.xml中定義的外層布局。接下來的部分就有問題了,在外層的LinearLayout當(dāng)中包含了兩個(gè)元素,一個(gè)是EditText,另一個(gè)又是一個(gè)LinearLayout,然后在這個(gè)內(nèi)部的LinearLayout當(dāng)中才包含了確定和取消這兩個(gè)按鈕。這個(gè)內(nèi)部的LinearLayout就是一個(gè)多余的布局嵌套,實(shí)際上并不需要這樣一層,讓兩個(gè)按鈕直接包含在外部的LinearLayout當(dāng)中就可以了。而這個(gè)多余的布局嵌套其實(shí)就是由于布局引入所導(dǎo)致的,因?yàn)槲覀冊(cè)趏k_cancel_layout.xml中也定義了一個(gè)LinearLayout。那么應(yīng)該怎樣優(yōu)化掉這個(gè)問題呢?當(dāng)然就是使用
可以看到,這里我們將ok_cancel_layout外層的LinearLayout布局刪除掉,換用了
第三重優(yōu)化:僅在需要時(shí)才加載布局
有的時(shí)候我們會(huì)遇到這樣的場景,就是某個(gè)布局當(dāng)中的元素非常多,但并不是所有元素都一起顯示出來的,而是普通情況下只顯示部分常用的元素,而那些不常用的元素只有在用戶進(jìn)行特定操作的情況下才會(huì)顯示出來。
這里舉個(gè)大家都非常熟悉的例子,我們?cè)谔砑勇?lián)系人的時(shí)候其實(shí)可以編輯的字段真的非常多,姓名、電話、email、傳真、住址、昵稱等等等等,但其實(shí)基本上大家常用的就是填一個(gè)姓名,填一個(gè)電話而已。那么將這么多繁雜的字段都一起顯示在界面上其實(shí)并不是一種很好的做法,因?yàn)榇蠖鄶?shù)人都是用不到這些字段的。比較聰明的做法就是把常用的姓名和電話顯示在界面上,然后給用戶提供一個(gè)添加更多字段的選項(xiàng),當(dāng)用戶真的有需要去添加其它信息的時(shí)候,我們才將另外的元素顯示到界面上。
說到實(shí)現(xiàn)這樣一個(gè)功能,我相信大多數(shù)人的第一反應(yīng)就是將不常用的元素使用INVISIBLE或者GONE進(jìn)行隱藏,然后當(dāng)用戶需要使用這些元素的時(shí)候再把它們置成VISIBLE顯示出來。使用這種方式肯定可以實(shí)現(xiàn)功能的,但是性能方面就表現(xiàn)得一般了,因?yàn)榧词故菍⒃剡M(jìn)行隱藏,它們其實(shí)還是在布局當(dāng)中的,每個(gè)元素還擁有著自己的寬、高、背景等等屬性,解析布局的時(shí)候也會(huì)將這些隱藏的元素一一解析出來。
那么我們?nèi)绾尾拍茏屵@些不常用的元素僅在需要時(shí)才去加載呢?Android為此提供了一種非常輕量級(jí)的控件,ViewStub。ViewStub雖說也是View的一種,但是它沒有大小,沒有繪制功能,也不參與布局,資源消耗非常低,將它放置在布局當(dāng)中基本可以認(rèn)為是完全不會(huì)影響性能的。
下面我們就來學(xué)習(xí)一下如何使用ViewStub來完成僅在需要時(shí)才去加載布局的功能,目前profile.xml中只有一個(gè)EditText用于編輯信息,那么比如說我們還有另外三個(gè)不太常用的EditText,就可以將它們定義在另外一個(gè)布局文件當(dāng)中。新建profile_extra.xml文件,代碼如下所示:
運(yùn)行效果如圖:
目前profile_extra.xml是一個(gè)獨(dú)立的布局,和profile.xml這個(gè)布局文件是完全沒有關(guān)系的。接下來我們修改profile.xml文件中的代碼,如下所示:
可以看到LinearLayout布局中我們新增了一個(gè)More Button,這個(gè)按鈕就是用于去加載那些不常用的元素的,然后在Button的下面定義了一個(gè)ViewStub。在ViewStub控件中,我們先是通過id屬性給它指定了一個(gè)唯一標(biāo)識(shí),又通過layout屬性將profile_extra布局傳入進(jìn)來,接著給ViewStub指定了一個(gè)寬高。注意,雖然ViewStub是不占用任何空間的,但是每個(gè)布局都必須要指定layout_width和layout_height屬性,否則運(yùn)行就會(huì)報(bào)錯(cuò)。
接著修改ProfileActivity中的代碼,在Activity中添加More Button的點(diǎn)擊事件,并在點(diǎn)擊事件中進(jìn)行如下邏輯處理:
當(dāng)點(diǎn)擊More Button之后我們首先會(huì)調(diào)用findViewById()方法將ViewStub的實(shí)例獲取到,拿到ViewStub的實(shí)例之后就很簡單了,調(diào)用inflate()方法或者setVisibility(View.VISIBLE)都可以將隱藏的布局給加載出來,而加載的這個(gè)布局就是剛才在XML當(dāng)中配置的profile_extra布局。
調(diào)用inflate()方法之后會(huì)將加載出來的布局進(jìn)行返回,之后我們就可以對(duì)這個(gè)布局進(jìn)行任意的操作了,再次隱藏顯示,或者獲取子元素的實(shí)例等。注意這里我對(duì)ViewStub的實(shí)例進(jìn)行了一個(gè)非空判斷,這是因?yàn)閂iewStub在XML中定義的id只在一開始有效,一旦ViewStub中指定的布局加載之后,這個(gè)id也就失敗了,那么此時(shí)findViewById()得到的值也會(huì)是空。
運(yùn)行結(jié)果如圖所示:
可以看到,界面上只有一個(gè)More按鈕,ViewStub是完全不占用任何空間的。然后點(diǎn)擊一下More按鈕,新的界面如下所示:
運(yùn)行結(jié)果正常,profile_extra.xml中定義的布局已經(jīng)加載出來了,而且顯示的位置正是ViewStub控件定義的位置,說明我們已經(jīng)成功是實(shí)現(xiàn)了ViewStub的功能。但是需要注意的是ViewStub所加載的布局不能使用