性能優(yōu)化之布局優(yōu)化

在app開(kāi)發(fā)中布局幾乎是必不可少的基礎(chǔ),隨著UI越來(lái)越多,布局的重復(fù)性、復(fù)雜度也會(huì)隨之增長(zhǎng)。而每一個(gè)view都需要measure ,layout ,draw這三步來(lái)完成,如果層級(jí)太深自然會(huì)導(dǎo)致整個(gè)繪制過(guò)程耗時(shí)大量的時(shí)間,從而造成啟動(dòng)速度慢,卡頓都問(wèn)題。而ondraw在頻繁刷新時(shí)可能多次觸發(fā),因此他又不能做耗時(shí)的操作,還可能內(nèi)存抖動(dòng)。對(duì)于布局檢查我們主要使用Layout Inspector.

Layout Inspector布局分析工具

Android Studio IDE的菜單Tools--->Layout Inspector子菜單,如下圖:

布局分析工具

啟動(dòng)模擬器或者真機(jī),debug運(yùn)行app至設(shè)備,選擇Layout Inspector 后,會(huì)出現(xiàn)下面的窗口:
布局分析關(guān)聯(lián)應(yīng)用

Component Tree窗口:查看當(dāng)前視圖的全部view樹(shù);控制視圖的顯示與隱藏(API 29-30的設(shè)備),如下圖:
顯示隱藏view

? 中間窗口:選擇應(yīng)用、刷新頁(yè)面視圖、呈現(xiàn)分頁(yè)的頁(yè)面效果圖、Live updates實(shí)時(shí)更新手機(jī)畫面;

? Attributes窗口:選擇Component Tree窗口中的一個(gè)子View視圖時(shí),呈現(xiàn)該子視圖的全部配置屬性。

總結(jié):

Layout Inspector 只能用于查看布局view樹(shù)層級(jí),不能查看布局性能;并且對(duì)設(shè)備的api的要求比較高(API29-30)

從上面分析的布局層級(jí)中,Google給我們提供了三個(gè)優(yōu)化布局的方案,下面就逐一介紹一下:


include標(biāo)簽(簡(jiǎn)化布局結(jié)果,提供復(fù)用性)

?

? app開(kāi)發(fā)過(guò)程中,會(huì)遇到不同的Activity里面有相同的布局,這時(shí)我們可以將這些通用的布局提取出來(lái)到一個(gè)單獨(dú)的layout文件里,再使用include標(biāo)簽引入到相應(yīng)的頁(yè)面布局文件里。

使用場(chǎng)景:

? 一個(gè)APP的頂部布局、側(cè)邊欄布局、底部Tab欄布局、ListView和GridView每一項(xiàng)的布局等

特別說(shuō)明:

  1. 如果我們需要給include標(biāo)簽和其所在加載的布局根節(jié)點(diǎn)設(shè)置id的話,這兩個(gè)id必須一致,或者只給其中一個(gè)設(shè)置id即可;
  2. include 引用的布局文件的id不要和外面主布局中的id設(shè)置相同了,否則會(huì)出現(xiàn)找錯(cuò)視圖;
  3. 如果需要給include設(shè)置位置時(shí),那么必須要設(shè)置大小width ,height,否則編譯會(huì)出問(wèn)題,一般情況不需要設(shè)置;
  4. 如果一個(gè)布局文件中引入兩個(gè)相同的include布局,那么就需要給include設(shè)置不同的id來(lái)進(jìn)行區(qū)分;
  5. 當(dāng)你使用了databinding時(shí),就必須要給include標(biāo)簽制定id

使用方法:

<include layout="@layout/custom_titlebar_layout" />

Merge標(biāo)簽(減少布局層次)

? merger標(biāo)簽相當(dāng)于一個(gè)包裹的作用,他在優(yōu)化UI接口的時(shí)候主要目的是通過(guò)刪減多余或額外的層級(jí),從而優(yōu)化整個(gè)android layout的結(jié)果。

使用場(chǎng)景(典型)

1.布局頂節(jié)點(diǎn)是FrameLayout且不需要設(shè)置background或Padding等屬性時(shí),可以用merge標(biāo)簽來(lái)替代,因?yàn)锳ctivity內(nèi)容視圖的parent view就是FrameLayout

2.當(dāng)某一個(gè)布局作為子布局include時(shí),使用merge當(dāng)作該子布局的頂節(jié)點(diǎn),這樣在被引入的時(shí)候回自動(dòng)被忽略掉,而將其子節(jié)點(diǎn)布局全部合并到主布局中

3.自定義一個(gè)組合視圖時(shí)(自定義多個(gè)系統(tǒng)widget組合的視圖,自定義視圖繼承于Linearlayout ,RelativeLayout等)

特別說(shuō)明:

  1. 只能作為根布局使用

  2. 當(dāng)Infalte以merge標(biāo)簽開(kāi)始的布局文件時(shí),必須制定一個(gè)父viewGroup ,并且必須要設(shè)定attachToRoot為true

    RelativeLayout mRootLayout = (RelativeLayout) mLayoutInflater
                    .inflate(R.layout.customview_titlebar_layout, this,true);
    

簡(jiǎn)單使用:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
        <ImageView
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:clickable="true"
            android:src="@drawable/vector"
            tools:ignore="MissingConstraints"
            app:tint="@color/selector" />
</merge>

由于activity 在加載布局setContent時(shí),是加載到ContentFrameLayout中去,而我們從源碼可以看到ContentFrameLayout是繼承自FrameLayout的:

public class ContentFrameLayout extends FrameLayout {

    public interface OnAttachListener {
        void onDetachedFromWindow();
        void onAttachedFromWindow();
    }
    //............
}

所以我們可以直接使用merge標(biāo)簽來(lái)作為布局的根節(jié)點(diǎn),我們?cè)俅问褂肔ayout Inspector分析工具來(lái)分析,發(fā)現(xiàn)并沒(méi)有merge標(biāo)簽這一層級(jí),如下:


merge標(biāo)簽布局分析

ViewStub標(biāo)簽(需要時(shí)加載)

他是在需要的時(shí)候去加載,不需要時(shí)則不用加載,這樣可以節(jié)約內(nèi)存使用。我們通常使用的visiable ,gone ,invisiable來(lái)控制顯示隱藏,其實(shí)這些視圖時(shí)已經(jīng)加載好了的。

ViewStub 是一個(gè)輕量級(jí)的View,它一個(gè)看不見(jiàn)的,不占布局位置,占用資源非常小的控件。他是一個(gè)寬高都為0的view ,默認(rèn)是不可見(jiàn)的。只有通過(guò)調(diào)用setVisibility函數(shù)或者Inflate函數(shù)才會(huì)將其要裝載的目標(biāo)布局給加載出來(lái),從而達(dá)到延遲加載的效果,這個(gè)要被加載的布局通過(guò)android:layout屬性來(lái)設(shè)置。

使用場(chǎng)景

  1. 在程序的運(yùn)行期間,某個(gè)布局在Inflate后,就不會(huì)有變化,除非重新啟動(dòng)。
  2. 想要 單次 控制顯示與隱藏的是一個(gè)布局文件,而非某個(gè)View。

特別說(shuō)明:

  1. ViewStub只能Inflate一次,之后ViewStub對(duì)象會(huì)被置為空。按句話說(shuō),某個(gè)被ViewStub指定的布局被Inflate后,就不會(huì)夠再通過(guò)ViewStub來(lái)控制它了。

  2. ViewStub只能用來(lái)Inflate一個(gè)布局文件,而不是某個(gè)具體的View,當(dāng)然也可以把View寫在某個(gè)布局文件中。

簡(jiǎn)單使用:

<!--自定義個(gè)布局-->
<?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">
    <TextView
        android:id="@+id/introduce"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/colorPrimary"
        android:textSize="18sp"
        android:text="I am a subview of viewstub "/>
</LinearLayout>
<ViewStub
        android:id="@+id/viewstub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/viewstub_layout" />
viewStub.inflate().findViewById(R.id.introduce);//在調(diào)用inflate()時(shí),view就已經(jīng)加載好了

個(gè)人覺(jué)得在列表頁(yè)面使用時(shí)比較方便,例如在頁(yè)面加載時(shí),不需要加載RecycleView,待網(wǎng)絡(luò)請(qǐng)求響應(yīng)后,再加載列表,這樣可以使頁(yè)面加載速度加快。

扁平化布局constraintLayout

  盡量使用constraintLayout約束布局來(lái)使布局盡量扁平化,減少非必需的UI組件。
  關(guān)于ConstraintLayout的使用將在新的文章中介紹。

溫馨提示:上述三個(gè)布局的使用都有一些限制,在使用時(shí)一定一定要多加注意。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容