Android常見(jiàn)的5個(gè)布局,我想大家一定不會(huì)陌生。LinearLayout、RelativeLayout和FrameLayout也是使用頻率較高的布局方式,做Android開(kāi)發(fā)的一定使用過(guò)。
傳統(tǒng)的5種布局方式:
- LinearLayout
- RelativeLayout
- FrameLayout
- GridLayout
- TableLayout
不過(guò)我的問(wèn)題并不是問(wèn)面試者如何使用這些基礎(chǔ)的布局,而是要看面試者怎么解決布局嵌套(影響性能)和屏幕適配問(wèn)題。
面試題:你是如何解決Android的布局嵌套問(wèn)題的?
我們都清楚Android界面的布局太復(fù)雜,嵌套層次過(guò)深,會(huì)使整個(gè)界面的測(cè)量、布局和繪制變得更復(fù)雜,對(duì)性能會(huì)造成影響。所以我們?cè)趯?xiě)Layout文件時(shí),也要盡量避免布局的嵌套層次過(guò)深的問(wèn)題。
在怎么解決問(wèn)題之前,我們得有一個(gè)好方法先判斷當(dāng)前的問(wèn)題情況。Android SDK工具箱中有一個(gè)叫做Hierarchy Viewer的工具,能夠在App運(yùn)行時(shí)分析Layout。
注意:在ROOT的手機(jī),或者是安裝開(kāi)發(fā)版的ROM的手機(jī)可以直接使用Hierarchy Viewer。
如果沒(méi)有Root的手機(jī)(SDK 4.1及以上),需要在你的PC端添加一個(gè)環(huán)境變量“ANDROID_HVPROTO=ddm”。
Mac系統(tǒng)的配置如下:
保存后運(yùn)行:source ~/.bash_profile
最后可以DDMS的Hierarchy Viewer看到:
下面列舉一些面試者常使用的方式。
merge
merge標(biāo)簽的作用是合并UI布局,使用該標(biāo)簽?zāi)芙档蚒I布局的嵌套層次。
merge標(biāo)簽可用于兩種情況:
- 布局頂結(jié)點(diǎn)是FrameLayout且不需要設(shè)置background或padding等屬性,可以用merge代替,因?yàn)锳ctivity內(nèi)容試圖的parent view就是個(gè)FrameLayout,所以可以用merge消除只剩一個(gè)。
- 某布局作為子布局被其他布局include時(shí),使用merge當(dāng)作該布局的頂節(jié)點(diǎn),這樣在被引入時(shí)頂結(jié)點(diǎn)會(huì)自動(dòng)被忽略,而將其子節(jié)點(diǎn)全部合并到主布局中。
ViewStub
ViewStub標(biāo)簽引入的布局默認(rèn)不會(huì)inflate,既不會(huì)顯示也不會(huì)占用位置。 ViewStub常用來(lái)引入那些默認(rèn)不會(huì)顯示,只在特殊情況下顯示的布局,如數(shù)據(jù)加載進(jìn)度布局、出錯(cuò)提示布局等。
需要在使用時(shí)手動(dòng)inflate:
ViewStub stub = (ViewStub)findViewById(R.id.error_layout);
errorView = stub.inflate();
errorView.setVisibility(View.VISIBLE);
ViewStub在一定的程度可以起到減少嵌套層次的作用,特別是很多時(shí)候我們的程序可能不需要走到ViewStub的界面。
include
將可復(fù)用的組件抽取出來(lái)并通過(guò)include標(biāo)簽使用,但<include>標(biāo)簽?zāi)軠p少布局的層次嗎?
我認(rèn)為不能。include主要解決的是相同布局的復(fù)用問(wèn)題,它并不能減少布局的層次。
用RelativeLayout代替LinearLayout
很多人為了減少布局層次喜歡用RelativeLayout代替LinearLayout,不過(guò)可能達(dá)到的效果并不會(huì)很明顯。層次是減少了,但本身RelativeLayout就會(huì)比LinearLayout性能差一點(diǎn)。
有一些界面,比如一個(gè)圖片和一個(gè)文本的布局(ListItem常見(jiàn)的布局方式),可以利用TextView有drawableLeft, drawableRight等屬性,完全不需要RelativeLayout或者LinearLayout布局。
你不知道的兩種新的布局方式
傳統(tǒng)的布局方式存在一定的缺陷,如RelativeLayout要兩次測(cè)量(measure)它的子View才能知道確切的高度;如果LinearLayout布局的子View有設(shè)置了layout_weight,那么它也需要測(cè)量?jī)纱尾拍塬@得布局的高度。
相對(duì)于傳統(tǒng)的布局方式,Android官方還推出了兩種新的布局方式:ConstraintLayout和FlexboxLayout。
ConstraintLayout
ConstraintLayout即約束布局,在2016年由Google I/O推出。ConstraintLayout和RelativeLayout有點(diǎn)類似,控件之間根據(jù)依賴關(guān)系而存在,但比RelativeLayout更加靈活。創(chuàng)建大型復(fù)雜的布局仍然可以使用扁平的層級(jí)(不用嵌套View Group),說(shuō)的簡(jiǎn)單些就是,再?gòu)?fù)雜的界面也可以只有2層層次。
要使用ConstraintLayout需要在build.gradle中添加相關(guān)的support庫(kù):
compile 'com.android.support.constraint:constraint-layout:1.0.2'
Android Studio 2.3及之后的版本使用引導(dǎo)創(chuàng)建Empty的Activity時(shí),默認(rèn)就是使用ConstraintLayout布局。關(guān)于本面試題的答案,官方其實(shí)已經(jīng)明確給出信號(hào)了。

使用ConstraintLayout可以有效的解決布局嵌套過(guò)多導(dǎo)致的性能問(wèn)題,官方也對(duì)其渲染性能進(jìn)行了優(yōu)化,并且ConstraintLayout支持可視化的方式編寫(xiě)布局。
不過(guò)學(xué)會(huì)熟練使用ConstraintLayout會(huì)需要一點(diǎn)時(shí)間,但這是值得的。
官方文檔:https://developer.android.com/training/constraint-layout/index.html
FlexBoxLayout
做過(guò)前端開(kāi)發(fā)(CSS方面)的同學(xué)對(duì)FlexBox一定不會(huì)陌生,最近我在做微信小程序開(kāi)發(fā)時(shí)也涉及到FlexBox。FlexBox(彈性布局)是w3c在2009年提出的一種新的布局方案,解決以前那種傳統(tǒng)css的盒模型的局限性。
Google開(kāi)源了FlexboxLayout布局和前端CSS FlexBox布局具有相同的功能(肯定有不一樣的地方),但已經(jīng)足夠在Android上改進(jìn)布局的構(gòu)建方式。
項(xiàng)目地址:https://github.com/google/flexbox-layout
FlexBoxLayout可以理解成一種更高級(jí)的LinearLayout,不過(guò)比LinearLayout更加強(qiáng)大和靈活。如果我們使用LinearLayout布局的話,那么不同的分辨率,也許我們要重新調(diào)整布局,勢(shì)必會(huì)需要跟多的布局文件放在不同的資源目錄。而使用FlexBoxLayout來(lái)布局的話,它可以適應(yīng)各種界面的改變(所以叫響應(yīng)式布局)。
看一下官方圖片感受一下:
如果對(duì)前端的Flexbox不太了解的話,你還需要補(bǔ)一些概念,好在這些東西在網(wǎng)上很容易找到。
小結(jié)
可能很多讀者會(huì)覺(jué)這樣的面試題是吹毛求疵,很多項(xiàng)目中哪有這么復(fù)雜的界面,根本就用不到這些優(yōu)化措施。
So,你沒(méi)有成長(zhǎng)為厲害的人。
可以說(shuō)厲害的人,或者叫高手,可能只是比較多在意這些細(xì)節(jié)而已。在實(shí)踐中的經(jīng)歷告訴我,很多難于解決的性能問(wèn)題,并不是因?yàn)橛幸粋€(gè)影響性能的問(wèn)題無(wú)法攻克,而是沒(méi)有一個(gè)明顯的制約因素,是有各種小問(wèn)題一點(diǎn)一點(diǎn)堆積起來(lái),最終積重難返。
所以,把細(xì)節(jié)做好,或者意識(shí)到細(xì)節(jié)的地方可能引發(fā)的問(wèn)題,對(duì)我們解決問(wèn)題是很有幫助的,不要浪費(fèi)了讓你可以成長(zhǎng)的細(xì)節(jié)。
相關(guān)閱讀:
Android面試一天一題(Day 37:一套高級(jí)工程師的面試題)
Android面試一天一題(Day 30:老外的自定義View面試題)

