主目錄見:Android高級進(jìn)階知識(這是總目錄索引)
?今天之所以講這一篇主要是為了下一篇[APP啟動速度優(yōu)化實例解析]做鋪墊的,我們都知道我們解決UI卡頓問題中主要就是:
- 優(yōu)化CPU的計算時間或者不必要的布局導(dǎo)致測量布局時間變長;
- 優(yōu)化GPU的過度繪制祸轮,主要方法可以在手機(jī)打開GPU檢測或者在Android studio中的Hierarchy Viewer可以查看層級。
針對這兩種情況一般會有下面的因素或者處理方式可以優(yōu)化:
- 在拼接字符串時候盡量使用StringBuilder避免大量的GC導(dǎo)致卡頓侥钳;
- 避免在主線程做大量的計算任務(wù)适袜,比如遞歸的操作導(dǎo)致CPU時間占用長導(dǎo)致卡頓;
- 去掉window的背景舷夺,因為DecorView默認(rèn)會有一個純色的背景苦酱,在我們布局設(shè)置了背景的話,那么這個背景對我們來說是多余的给猾;
- 去掉不必要的背景疫萤,因為在我們布局有嵌套的情況下,如果都設(shè)置了背景的話有可能存在不必要的背景導(dǎo)致重繪耙册;
- 利用clipRect的方式來減少繪制層數(shù),一個典型的例子就是撲克牌重疊導(dǎo)致重繪毫捣;
- 利用include详拙,merge,ViewStub等標(biāo)簽來減少嵌套層數(shù)蔓同。
今天我們就是主要來講的最后一種方法的使用饶辙,這個方式應(yīng)該說能很有效解決過渡繪制的問題。
一.目標(biāo)
我們今天的目標(biāo)也是很簡單的斑粱,就是看這幾個標(biāo)簽是怎么使用的弃揽,然后能在實際應(yīng)用中使用到,所以今天目標(biāo):
1.學(xué)會include则北,merge矿微,ViewStub這三個標(biāo)簽怎么使用;
2.明白什么情況下使用哪個標(biāo)簽以及用這三個標(biāo)簽來優(yōu)化尚揣。
二.標(biāo)簽使用
1.<include/>重用布局
首先我們第一個來講講<include/>的使用涌矢,若幾個布局界面存在較多的共同模塊,可以進(jìn)行代碼塊的重用,編寫進(jìn)入一個共同的布局里面,然后在多個布局文件中使用include標(biāo)簽進(jìn)行引入。這里我們舉個例子比如頂部欄:
<?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">
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/back_arrow"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="標(biāo)題"
android:textColor="@color/colorPrimary"
android:textSize="20sp"
android:gravity="center"/>
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/btn_search"
android:layout_gravity="center"/>
</LinearLayout>
這里只是簡單布局了下快骗,大家湊合著看哈娜庇,效果圖如下:
那么如果我們的項目中會使用多次這個頂部欄的話(當(dāng)然增加頂部欄不會每個頁面include),那么我們這時候就可以用include:
<?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"
>
<include layout="@layout/layout"/>
</LinearLayout>
這樣我們就可以把公共的布局抽出來當(dāng)做獨立的部分了方篮,這個標(biāo)簽應(yīng)該來說用的還是比較多的名秀。
2.<merge/>減少布局層數(shù)
我們知道我們布局解析的時候是一層一層遞歸調(diào)用rinflate,然后再回歸講view添加到父視圖中去藕溅,最后整個視圖才創(chuàng)建完畢匕得。如果嵌套層數(shù)太多的話,就會導(dǎo)致這個解析的過程任務(wù)量變大從而導(dǎo)致解析速度變慢巾表,這樣的話我們可以用<merge/>標(biāo)簽來進(jìn)行優(yōu)化耗跛。我們首先來用androidstudio中的Hierarchy Viewer來查看我們剛才布局的層級:
可以看到我們布局是從最上面的contentFrameLayout往下的裕照,為什么呢?因為我們看setContentView源碼的時候我們知道调塌,我們的布局是在id為content的
FrameLayout
下面晋南,如果不知道可以參考setContentView源碼分析這篇文章,然后我們看到往下的話還有兩層的LinearLayout布局羔砾,很明顯负间,有一層LinearLayout是沒有用的。所以我們來優(yōu)化一下:
<?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"
>
<include layout="@layout/layout"/>
</merge>
我們看到姜凄,這樣的話就少了一層LinearLayout政溃,也可以把里面的LinearLayout去掉,但是這樣的布局就要又外層的布局來決定了:
<?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="50dp">
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/back_arrow"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="標(biāo)題"
android:textColor="@color/colorPrimary"
android:textSize="20sp"
android:gravity="center"/>
<Button
android:layout_width="35dp"
android:layout_height="35dp"
android:background="@mipmap/btn_search"
android:layout_gravity="center"/>
</merge>
那么我們外層的布局就要改成:
<?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"
>
<include layout="@layout/layout"/>
</LinearLayout>
好啦态秧,到這里我們這個標(biāo)簽已經(jīng)講完了董虱,這個標(biāo)簽的功能主要就是為了減少層數(shù)的。
3.<ViewStub/>延遲加載布局
其實ViewStub就是一個寬高都為0的一個View申鱼,它默認(rèn)是不可見的愤诱,只有通過調(diào)用setVisibility函數(shù)或者Inflate函數(shù)才 會將其要裝載的目標(biāo)布局給加載出來,從而達(dá)到延遲加載的效果捐友,這個要被加載的布局通過android:layout屬性來設(shè)置淫半。例如我們通過一個 ViewStub來惰性加載一個消息流的評論列表,因為一個帖子可能并沒有評論匣砖,此時我可以不加載這個評論的ListView科吭,只有當(dāng)有評論時我才把它加 載出來,這樣就去除了加載ListView帶來的資源消耗以及延時:
<?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"
>
<include layout="@layout/layout"/>
<ViewStub
android:inflatedId="@+id/network_error_id"
android:layout="@layout/network_error"
android:id="@+id/network_error"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</merge>
這里的inflatedId指的是layout/network_error的跟id猴鲫,android:layout指的是布局network_error的布局对人。然后我們要讓他顯示出來:
if (null == netWorkError){
ViewStub netWorkErrorStub = (ViewStub)findViewById(R.id.network_error);
netWorkError = netWorkErrorStub.inflate();
}else{
netWorkError.setVisibility(View.VISIBLE);
}
ViewStub標(biāo)簽和GONE都是會使一個視圖不可見,但是設(shè)置為GONE的話在渲染的時候還是會被添加到視圖樹里面拂共,而ViewStub只有在inflate之后才會被添加到視圖樹上面规伐,所以減少了一次性渲染的壓力。
注意事項
- 判斷是否已經(jīng)加載過匣缘, 如果通過setVisibility來加載猖闪,那么通過判斷可見性即可;如果通過inflate()來加載是不可以通過判斷可見性來處理的肌厨,所以需要判斷加載的視圖是否為空來判斷培慌。
- findViewById的問題,注意ViewStub中是否設(shè)置了inflatedId柑爸,如果設(shè)置了則需要通過inflatedId來查找目標(biāo)布局的根元素吵护。
- ViewStub不能與merge一起聯(lián)合使用。
總結(jié):今天我們講了這三個標(biāo)簽,應(yīng)該說是開發(fā)過程中用的比較頻繁的馅而,希望大家如果又遇到布局優(yōu)化問題能想到用這個來解決祥诽,當(dāng)然,今天只是講了一小部分的內(nèi)容瓮恭,優(yōu)化的內(nèi)容還是很多的雄坪,希望我們一起努力。