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

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

Layout Inspector布局分析工具

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

布局分析工具

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

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

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

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

總結(jié):

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

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


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

?

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

使用場景:

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

特別說明:

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

使用方法:

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

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

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

使用場景(典型)

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

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

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

特別說明:

  1. 只能作為根布局使用

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

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

簡單使用:

<?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時,是加載到ContentFrameLayout中去,而我們從源碼可以看到ContentFrameLayout是繼承自FrameLayout的:

public class ContentFrameLayout extends FrameLayout {

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

所以我們可以直接使用merge標(biāo)簽來作為布局的根節(jié)點,我們再次使用Layout Inspector分析工具來分析,發(fā)現(xiàn)并沒有merge標(biāo)簽這一層級,如下:


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

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

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

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

使用場景

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

特別說明:

  1. ViewStub只能Inflate一次,之后ViewStub對象會被置為空。按句話說,某個被ViewStub指定的布局被Inflate后,就不會夠再通過ViewStub來控制它了。

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

簡單使用:

<!--自定義個布局-->
<?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()時,view就已經(jīng)加載好了

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

扁平化布局constraintLayout

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

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

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

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

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