Android性能優(yōu)化第(十)篇---布局優(yōu)化

版權(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ù)覽效果如下


include

每一個(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。

DecroView

我們看第一種情況,我們通過(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工具看一下,如圖:


使用< merge/>修改后的

我們?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è)

給公司打個(gè)廣告,有玩游戲的,可以下載小熊競(jìng)技臺(tái)

看紅框的部分,通常我們都是弄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 !

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容