布局優(yōu)化的主要思想其實很簡單,就是盡量減少布局文件的層級拂到,布局層級少了Android繪制時的工作量就少了归榕,程序運行時的性能自然就高了。
既然知道了主要思想那么接下來我們要做的工作就明朗了纳账。我們可以從兩方面著手進行優(yōu)化:
第一逛薇、優(yōu)化布局方案
在寫布局的過程中我們應(yīng)該避免無用的控件和層級,并且結(jié)合具體的使用場景去選擇ViewGroup疏虫。避免無用的控件很簡單永罚,不必多說啤呼。那如何去選擇性能更好的ViewGroup呢?這里的ViewGroup也就是我們常說的五大布局——FrameLayout呢袱、LinearLayout官扣、RelativeLayout、AbsoluteLayout羞福、TableLayout惕蹄。其中FrameLayout、LinearLayout治专、RelativeLayout是最常用的卖陵,因此這里只對這三種布局進行分析。
FameLayout
FrameLayout是五大布局中最簡單的一個布局张峰,在這個布局中泪蔫,整個界面被當(dāng)成一塊空白備用區(qū)域,所有的子元素都不能被指定放置的位置挟炬,它們統(tǒng)統(tǒng)放于這塊區(qū)域的左上角鸥滨,并且后面的子元素直接覆蓋在前面的子元素之上,將前面的子元素部分和全部遮擋谤祖。相同層級布局中 FrameLayout的效率也是最高的 占用內(nèi)存相對來說也是較小的婿滓。但是FrameLayout也有其缺陷:很難去組織多個子元素位置和關(guān)系。當(dāng)然如果只有一個子元素粥喜,當(dāng)然是使用FrameLayout最好凸主。這應(yīng)該也是為何作為頂級View的DecorView也是一個FrameLayout的原因吧。
LinearLayout與RelativeLayout性能PK
細心的同學(xué)應(yīng)該會發(fā)現(xiàn):無論是Eclipse還是Android Studio额湘,新建Blank Activity時默認的layout都是RelativeLayout卿吐。查詢資料你會發(fā)現(xiàn)這是由 android-sdk\tools\templates\activities\BlankActivity\root\res\layout\activity_simple.xml.ftl 這個文件定義的,也就是說這是Google的選擇锋华,而非IDE的選擇嗡官。而且google原文中也提到
A RelativeLayout is a very powerful utility for designing a user interface because it can eliminate nested view groups and keep your layout hierarchy flat, which improves performance. If you find yourself using several nested LinearLayout groups, you may be able to replace them with a single RelativeLayout.
大概的意思是“性能至上”, RelativeLayout 在性能上更好毯焕,因為在諸如 ListView 等控件中衍腥,使用 LinearLayout 容易產(chǎn)生多層嵌套的布局結(jié)構(gòu),這在性能上是不好的纳猫。而 RelativeLayout 因其原理上的靈活性婆咸,通常層級結(jié)構(gòu)都比較扁平,很多使用LinearLayout 的情況都可以用一個 RelativeLayout 來替代芜辕,以降低布局的嵌套層級尚骄,優(yōu)化性能。所以從這一點來看侵续,Google比較推薦開發(fā)者使用RelativeLayout倔丈,因此就將其作為Blank Activity的默認布局了憨闰。
有一篇博客寫的很不錯,通過實際測試以及源碼分析比較了兩者的性能需五,總結(jié)的也不錯起趾!請移步Android中RelativeLayout和LinearLayout性能分析。關(guān)于該博文中提到的“DecorView是一個垂直方向的LinearLayout”這句話是不準(zhǔn)確的警儒,通過看源碼我們可以知道DecorView其實是一個FrameLayout,而LinearLayout是作為它的唯一子元素眶根,構(gòu)成了我們熟知的DecorView上下兩部分(上面是標(biāo)題欄蜀铲,下面是內(nèi)容欄)的結(jié)構(gòu)。而我們經(jīng)常在Activity中用到的setContentView正是將我們的布局加到了下面部分的內(nèi)容欄属百,即content记劝。而content也是一個FrameLayout。
第二族扰,使用<include>厌丑、<merge>標(biāo)簽和ViewStub
這部分內(nèi)容算是比較基礎(chǔ),詳細的使用方式可以去看下官方文檔渔呵,下邊主要介紹下使用時需要注意到的點怒竿。
<include>標(biāo)簽
<include>標(biāo)簽主要用于布局重用,從而避免同樣的布局寫重復(fù)的代碼扩氢,它的使用如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/title_bar"
layout="@layout/title_bar"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/string"/>
......
</LinearLayout>
其中layout="@layout/title_bar"
指定了另外的一個布局title_bar.xml耕驰,一個應(yīng)用內(nèi)很多界面都會用到titlebar,通過這樣的方式录豺,這個布局的內(nèi)容就只需要寫一遍就可以了朦肘。需要注意的是:1、<include>除了android:id這個屬性只支持以 android:layout_開頭的屬性双饥,而且只要指定了這種屬性媒抠,那么也必須要指定 android:layout_width 和 android:layout_height,否則會直接報語法錯誤咏花。2趴生、如果指定了id或者
android:layout_*屬性,會直接覆蓋導(dǎo)入布局的根布局的相應(yīng)屬性迟螺,如果都沒有指定則會使用導(dǎo)入布局的根布局的屬性冲秽。
<merge>標(biāo)簽
在使用<include>標(biāo)簽時,可能會導(dǎo)致重復(fù)的布局嵌套矩父,加深布局的層級锉桑,從而使得加載變慢,這時我們會用到<merge>標(biāo)簽窍株。所以<merge>標(biāo)簽要和<include>標(biāo)簽配合使用民轴,用來減少布局中重復(fù)的層級攻柠,而且<merge>標(biāo)簽只能作為<include>指定的布局的根標(biāo)簽使用,這樣<merge>標(biāo)簽中包含的子元素后裸,就直接與 <include>平級瑰钮。
因此在使用<merge>標(biāo)簽時,我們需注意一點:<merge>標(biāo)簽內(nèi)的子元素會按照<include> 所在布局的布局方式來顯示微驶。所以我們在使用的時候需要特別注意布局類型浪谴!
ViewStub
在開發(fā)過程中,經(jīng)常會遇到這樣一種情況:有些布局在正常情況下不會顯示因苹,如網(wǎng)絡(luò)異常頁苟耻、空白頁等,雖然設(shè)置可見性View.GONE,但是在整個界面初始化的時候仍然會將其加載進來扶檐,所以增加了初始化時的消耗凶杖。這時我們就可以用到ViewStub,它提供了按需加載的功能款筑,在需要的時候才會將ViewStub中的布局加載到內(nèi)存智蝠,所以提高了初始化時的性能,從而也提高了程序的效率奈梳。
看源碼可以知道杈湾,ViewStub繼承了View,非常輕量級且寬 /高都是0攘须,因此它本身不參與任何的布局和繪制過程毛秘。它的使用方式也很簡單,下面是ViewStub的示例:
<ViewStub
android:id="@+id/stub_import"
android:inflatedId="@+id/layout_import"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/layout_net_error" />
其中stub_import是ViewStub的id阻课,而layout_import是layout/layout_net_error這個布局的根元素的id叫挟。需要加載ViewStub中的布局時,可以使用以下兩種方式:
( (ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
或 View errorLayout = ((ViewStub)findViewById(R.id.stub_import)).inflate();
需要注意的是:ViewStub一旦visible/inflated限煞,就會被它內(nèi)部的布局替換掉抹恳,而它自己也不再是整個布局中的一部分了。所以后面無法再使用ViewStub來控制布局了署驻。
由于ViewStub這種使用后就置空的策略奋献,所以當(dāng)需要在運行時不止一次的顯示和隱藏某個布局時,ViewStub是做不到的旺上,這時就只有使用View的可見性來控制了瓶蚂。另外,目前ViewStub還不支持<merge>標(biāo)簽宣吱。
布局優(yōu)化到這兒就結(jié)束了窃这,如果大家還有更好的想法可以提出,我們共同學(xué)習(xí)征候,謝謝杭攻!