版權(quán)聲明:本文為LooperJing原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處!
之前在Android性能優(yōu)化第(四)篇---Android渲染機(jī)制說(shuō)了一下UI的渲染,我們知道Android系統(tǒng)每隔16ms就重新繪制一次Activity,如果沒(méi)有完成就會(huì)有丟幀的現(xiàn)象。為了減輕UI繪制的負(fù)擔(dān),有必要把Layout編寫的一些注意事項(xiàng)總結(jié)一下。
首先說(shuō)一下< include/>,< merge/>,ViewStub,如果對(duì)這部分清楚的朋友可以跳過(guò)。
1、< include/>重用
比如我們要寫一個(gè)TitleBar(title_bar_layout.xml),是這樣子的。
<?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="50dp"
android:orientation="horizontal">
<TextView
android:layout_width="50dp"
android:layout_height="match_parent"
android:background="@drawable/back" />
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="競(jìng)技臺(tái)"
android:textSize="18sp" />
<TextView
android:layout_width="50dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="確定"
android:textSize="18sp" />
</LinearLayout>
預(yù)覽效果如下
每一個(gè)項(xiàng)目中都有TitleBar,所以使用< include/>進(jìn)行服用,以便統(tǒng)一管理,在使用的時(shí)候,一行代碼就OK了。
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_layout_opt"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="zhangwan.wj.com.myshare.activity.LayoutOptActivity">
<include layout="@layout/title_bar_layout" />
</FrameLayout>
實(shí)際上對(duì)于TitleBar我們一般都會(huì)封裝成一個(gè)控件來(lái)使用。
2、< merge/>減少視圖層級(jí)
為什么要減少視圖層級(jí),在加載布局的時(shí)候,rinflater是通過(guò)深度優(yōu)先遍歷來(lái)構(gòu)造視圖樹,每解析到一個(gè)View,就會(huì)遞歸調(diào)用rinflater,直到這條路徑的最后一個(gè)元素,然后再回溯過(guò)來(lái)將每一個(gè)View添加到他們的parent中,整個(gè)View樹構(gòu)建完畢。在使用了include后可能導(dǎo)致布局嵌套過(guò)多,出現(xiàn)不必要的layout節(jié)點(diǎn),從而導(dǎo)致解析變慢。這個(gè)時(shí)候我們可以使用< merge/>標(biāo)簽。< 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)全部合并到主布局中。
為了更好理解這兩種情況,我們復(fù)習(xí)一下DecroView,DecroView是Activity的頂級(jí)View,繼承與FramLayout,內(nèi)部有一個(gè)豎直方向的LinerLayout,上面是標(biāo)題欄,下面內(nèi)容欄,內(nèi)容欄是我們Activity的setContentView的布局,這個(gè)內(nèi)容欄是個(gè)FramLayout。
我們看第一種情況,我們通過(guò)View Hierarchy工具看一下,如圖:
發(fā)現(xiàn)了有一個(gè)FrameLayout是不需要的。使用< merge/>修改。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_layout_opt"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="zhangwan.wj.com.myshare.activity.LayoutOptActivity">
<include layout="@layout/title_bar_layout" />
</merge>
我們?cè)谕ㄟ^(guò)View Hierarchy工具看一下,如圖:
我們?cè)诳吹诙N情況
比如,如果你有一個(gè) Layout 是一個(gè)豎直方向的 LinearLayout,其中包含兩個(gè)連續(xù)的 View 可以在別的 Layout 中重用,那么你會(huì)做一個(gè) LinearLayout 來(lái)包含這兩個(gè) View ,以便重用。不過(guò),當(dāng)使用另一個(gè) LinearLayout 來(lái)嵌套這個(gè)可重用的 LinearLayout 時(shí),這種嵌套 LinearLayout 的方式除了減慢你的 UI 性能外沒(méi)有任何意義。
為了避免這種情況,你可以用 < merge/> 元素來(lái)替代可重用 Layout 的根節(jié)點(diǎn)。例如:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/add"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/delete"/>
</merge>
3、ViewStub延遲加載
延遲加載意味著我們可以分批次的將一個(gè)Layout中的View解析加載到內(nèi)存,比如說(shuō),我們最先加載Loading的布局,等待網(wǎng)絡(luò)請(qǐng)求,然后加載常規(guī)展示的布局,當(dāng)網(wǎng)絡(luò)請(qǐng)求發(fā)生錯(cuò)誤或者空數(shù)據(jù)的時(shí)候,加載錯(cuò)誤布局。分批次加載減輕了CPU和GPU的負(fù)擔(dān),ViewStub 是一個(gè)輕量的視圖,不需要大小信息,也不會(huì)在被加入的 Layout 中繪制任何東西。ViewStub可見或是被inflate了,ViewStub就不存在了,取而代之的是被inflate的Layout。所以它也被稱做惰性控件。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="zhangwan.wj.com.myshare.activity.LayoutOptActivity">
<include layout="@layout/title_bar_layout" />
<ViewStub
android:inflatedId="@+id/no_data_view"
android:id="@+id/no_data_view"
android:layout="@layout/no_data_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</merge>
注意android:inflatedId指定的no_data_view是布局no_data_layout的根id, android:layout="@layout/no_data_layout"是指定加載哪個(gè)布局。
要讓ViewStub顯示出來(lái),可以用viewStub.infalte()或viewStub.setVisibility(View.VISIBLE)來(lái)完成,如下。
public void showEmptyView() {
// listview.setVisibility(View.GONE);
if (mNoDataView == null) {
ViewStub noDataViewStub = (ViewStub)findViewById(R.id.no_data_view);
mNoDataView = noDataViewStub.inflate();
} else {
mNoDataView.setVisibility(View.VISIBLE);
}
}
現(xiàn)在你可能會(huì)問(wèn),ViewStub和View.GONE的區(qū)別。他們的共同點(diǎn)是一開始的時(shí)候都不會(huì)顯示,但是View.GONE在布局加載的時(shí)候,就已經(jīng)添加到布局樹上了,而ViewStub只會(huì)在顯示的時(shí)候才會(huì)渲染布局。最后注意ViewStub加載的布局中不能有merge。
4、能用一個(gè)View搞定的,別用兩個(gè)
看紅框的部分,通常我們都是弄5個(gè)TextView在一個(gè)豎直方向的LinerLayout中,但是我們可以這樣寫
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="100dp"
android:layout_margin="20dp"
android:layout_width="match_parent">
<TextView
android:textSize="14dp"
android:lineSpacingMultiplier="1.3"
android:gravity="center_vertical"
android:text="玩家等級(jí)\n對(duì)戰(zhàn)總勝利\n勝率\n參與比賽\n獲得冠軍"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
在比如下圖中紅框部分我們可以使用SpannableStringBuilder
又比如下圖
每個(gè)條目之間的間隔不要在使用一個(gè)空View了,如果使用margin閱讀不直觀??梢允褂肧pace控件
<Space
android:layout_width="match_parent"
android:layout_height="15dp"/>
加入上圖中,需求不是間隙,而是一條分隔線,怎么辦呢?,難道是用一個(gè)View設(shè)置高度實(shí)現(xiàn)嗎?非也!LinearLayout和ListView一樣,有android:divider與android:showDividers屬性。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content_home"
android:orientation="vertical"
android:divider="@drawable/divider"
android:showDividers="middle"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="個(gè)人信息" />
...
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="1000dp"
android:height="15dp" />
<solid android:color="#e7e7e7" />
</shape>
OK,布局優(yōu)化的部分到此結(jié)束。
Please accept mybest wishes for your happiness and success !