Android產品研發(二十一)-->Android中的UI優化

轉載請標明出處:一片楓葉的專欄

上一篇文章中我們講解了Android產品研發過程中的代碼Review。通過代碼Review能夠提高產品質量,增強團隊成員之間的溝通,提高開發效率,所以良好的產品開發迭代過程中,代碼Review是一個必不可少的步驟。那麼如何進行代碼Review呢?我們主要講解了團隊成員之間的代碼Review,代碼lint檢查,開發規範等方面的知識點,更多關於代碼Review相關的知識可參考我的:Android產品研發(二十)–>代碼Review

本文我們將講解一下Android UI優化方面的知識。Android系統的優化分爲好多方面:比如性能優化,UI優化,資源文件優化等等,這裏我們先暫時講解Android UI優化方面的知識點。

三種佈局方式
Android對佈局優化提供了三種佈局:

<include/>
<merge/>
<ViewStub/>

這三種佈局都可以簡化我們的佈局文件,優化繪製流程,下面我們簡單看一下這三種組件的使用方式。

1、重用佈局<include/>

<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"  
    Android:orientation="vertical"   
    Android:layout_width=”match_parent”  
    Android:layout_height=”match_parent”  
    Android:background="@color/app_bg"  
    Android:gravity="center_horizontal">  

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

    <TextView Android:layout_width=”match_parent”  
              Android:layout_height="wrap_content"  
              Android:text="@string/hello"  
              Android:padding="10dp" />  

    ...  

</LinearLayout>  

1)標籤可以使用單獨的layout屬性,這個也是必須使用的。
2)可以使用其他屬性。標籤若指定了ID屬性,而你的layout也定義了ID,則你的layout的ID會被覆蓋。
3)在include標籤中所有的Android:layout_*都是有效的,前提是必須要寫layout_width和layout_height兩個屬性。

2、減少視圖層級<merge/>
這個標籤在UI的結構優化中起着非常重要的作用,它可以刪減多餘的層級,優化UI。<merge/>多用於替換FrameLayout或者當一個佈局包含另一個時,標籤消除視圖層次結構中多餘的視圖組。例如你的主佈局文件是垂直佈局,引入了一個垂直佈局的include,這是如果include佈局使用的LinearLayout就沒意義了,使用的話反而減慢你的UI表現。這時可以使用標籤優化。

<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>  

現在,當你添加該佈局文件時(使用標籤),系統忽略節點並且直接添加兩個Button。

3、需要時使用<ViewStub/>
這個標籤最大的優點是當你需要時纔會加載,使用他並不會影響UI初始化時的性能。各種不常用的佈局想進度條、顯示錯誤消息等可以使用這個標籤,以減少內存使用量,加快渲染速度。

<ViewStub  
    Android:id="@+id/stub_import"  
    Android:inflatedId="@+id/panel_import"  
    Android:layout="@layout/progress_overlay"  
    Android:layout_width="fill_parent"  
    Android:layout_height="wrap_content"  
    Android:layout_gravity="bottom" />  

當你想加載佈局時,可以使用下面其中一種方法:

((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);  
// or  
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();  

Android中的過度繪製

Android開發者選項中有一項是:“調試GPU過度繪製”,過度繪製描述的是屏幕上一個像素在單個幀中被重繪了多少次。比如一個有背景的TextView,那麼顯示文本的那些像素至少繪製了兩次,一次是背景,一次是文本。過度繪製是Android平臺上一個很棘手的性能問題,它非常容易出現。

過度繪製產生的原因

  • 太多重疊的背景
    重疊着的背景有時候是有必要的,有時候是沒必要的。這要視你的項目具體情況而定.

  • 太多疊加的View
    或者本來這個UI佈局就很複雜或者你是爲了追求一個炫麗的視覺效果,這都有可能使得很多view疊加在一起。這個情況非常普遍,下面的建議中會談談怎麼減少這種情況帶來的影響。

  • 複雜的Layout層級
    複雜的層級關係,這個在佈局中也很常見,下面也會說這種情況怎麼做可以儘可能的減少過度繪製。

建議

  • 太多重疊的背景
    這個問題其實最容易解決,建議就是檢查你在佈局和代碼中設置的背景,有些背景是被隱藏在底下的,它永遠不可能顯示出來,這種沒必要的背景一定要移除,因爲它很可能會嚴重影響到app的性能。如果採用的是selector的背景,將normal狀態的color設置爲”@Android:color/transparent”,也同樣可以解決問題。

  • 太多重疊的view
    第一個建議是:使用ViewStub來加載一些不常用的佈局,它是一個輕量級且默認不可見的視圖,可以動態的加載一個佈局,只有你用到這個重疊着的view的時候才加載,推遲加載的時間。第二個建議是:如果使用了類似viewpager+Fragment這樣的組合或者有多個Fragment在一個界面上,需要控制Fragment的顯示和隱藏,儘量使用動態地Inflation view,它的性能要比SetVisiblity好。

  • 複雜的Layout層級
    這裏的建議比較多一些,首先推薦用Android提供的佈局工具Hierarchy Viewer來檢查和優化佈局。第一個建議是:如果嵌套的線性佈局加深了佈局層次,可以使用相對佈局來取代。第二個建議是:用標籤來合併佈局,這可以減少佈局層次。第三個建議是:用標籤來重用佈局,抽取通用的佈局可以讓佈局的邏輯更清晰明瞭。記住,這些建議的最終目的都是使得你的Layout在Hierarchy Viewer裏變得寬而淺,而不是窄而深。

實例分析

(一)啓用調試GPU過度繪製功能;
設置-》其他高級設置-》開發者選項-》調試GPU過度繪製;
這裏寫圖片描述

我們可以看到我們的手機界面中顯現除了一下背景顏色,這裏介紹一下這些背景顏色表示的就是佈局過度繪製的情況,其中:
藍色:一次繪製
綠色:兩次繪製
淡紅:三次繪製
深紅:四次繪製
所以當我們App的界面中深紅或者是淡紅的部分較多時我們可以分析一下我們的界面佈局是否存在過度繪製的情況。

(二)打開我們的測試頁面,這裏以一個意見反饋爲例:

這裏寫圖片描述

這裏可以看出整個頁面你的佈局爲藍色,即渲染了一次;
輸入框中的北京爲綠色,渲染了兩次;
文字部分爲淡紅色,渲染了三次;

整體來看整個佈局渲染基本正常,不存在過度繪製的問題;
下面這是佈局文件的xml定義:

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@color/c10">

    <RelativeLayout
        Android:layout_height="match_parent"
        Android:layout_width="match_parent">

        <EditText
            Android:id="@+id/feedback_content_edit"
            Android:layout_width="match_parent"
            Android:layout_height="126dp"
            Android:layout_marginTop="18dp"
            Android:paddingLeft="@dimen/s3"
            Android:paddingRight="@dimen/s3"
            Android:paddingTop="@dimen/s2"
            Android:paddingBottom="@dimen/s2"
            Android:background="#FFFFFF"
            Android:textColorHint="@color/c4"
            Android:textSize="@dimen/f4"
            Android:textColor="@color/c3"
            Android:hint="儘量詳細描述您遇到的問題,感謝您給我們提出建議"
            Android:gravity="top|left" />

        <EditText
            Android:id="@+id/feedback_contact_edit"
            Android:layout_below="@id/feedback_content_edit"
            Android:layout_width="match_parent"
            Android:layout_height="45dp"
            Android:layout_marginTop="@dimen/s3"
            Android:paddingLeft="@dimen/s3"
            Android:paddingRight="@dimen/s3"
            Android:background="#FFFFFF"
            Android:textColorHint="@color/c4"
            Android:textSize="@dimen/f4"
            Android:textColor="@color/c3"
            Android:hint="請輸入您的手機號碼、QQ或者郵箱"
            Android:singleLine="true" />

        <TextView
            Android:id="@+id/feedback_info_text"
            Android:layout_below="@id/feedback_contact_edit"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_marginTop="@dimen/s3"
            Android:paddingLeft="@dimen/s3"
            Android:paddingRight="@dimen/s3"
            Android:background="#00000000"
            Android:textSize="@dimen/f5"
            Android:textColor="@color/c5"
            Android:text="留下聯繫方式,以便能更好更快的給您答覆,還可能有意外獎勵哦" />

        <include
            layout="@layout/b3_button"
            Android:layout_width="match_parent"
            Android:layout_height="48dp"
            Android:layout_below="@id/feedback_info_text"
            Android:layout_marginLeft="@dimen/s4"
            Android:layout_marginRight="@dimen/s4"
            Android:layout_marginTop="12dp"
            Android:gravity="center"
            Android:text="提交"
            Android:textSize="@dimen/f2"
            Android:textColor="@color/c11" />

    </RelativeLayout>

</RelativeLayout>

下面我們爲整個佈局文件多添加一層佈局控件

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@color/c10">

    <!-- 這裏是新添加的佈局控件-->
    <RelativeLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

        <RelativeLayout
            Android:layout_height="match_parent"
            Android:layout_width="match_parent">

            <EditText
                Android:id="@+id/feedback_content_edit"
                Android:layout_width="match_parent"
                Android:layout_height="126dp"
                Android:layout_marginTop="18dp"
                Android:paddingLeft="@dimen/s3"
                Android:paddingRight="@dimen/s3"
                Android:paddingTop="@dimen/s2"
                Android:paddingBottom="@dimen/s2"
                Android:background="#FFFFFF"
                Android:textColorHint="@color/c4"
                Android:textSize="@dimen/f4"
                Android:textColor="@color/c3"
                Android:hint="儘量詳細描述您遇到的問題,感謝您給我們提出建議"
                Android:gravity="top|left" />

            <EditText
                Android:id="@+id/feedback_contact_edit"
                Android:layout_below="@id/feedback_content_edit"
                Android:layout_width="match_parent"
                Android:layout_height="45dp"
                Android:layout_marginTop="@dimen/s3"
                Android:paddingLeft="@dimen/s3"
                Android:paddingRight="@dimen/s3"
                Android:background="#FFFFFF"
                Android:textColorHint="@color/c4"
                Android:textSize="@dimen/f4"
                Android:textColor="@color/c3"
                Android:hint="請輸入您的手機號碼、QQ或者郵箱"
                Android:singleLine="true" />

            <TextView
                Android:id="@+id/feedback_info_text"
                Android:layout_below="@id/feedback_contact_edit"
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content"
                Android:layout_marginTop="@dimen/s3"
                Android:paddingLeft="@dimen/s3"
                Android:paddingRight="@dimen/s3"
                Android:background="#00000000"
                Android:textSize="@dimen/f5"
                Android:textColor="@color/c5"
                Android:text="留下聯繫方式,以便能更好更快的給您答覆,還可能有意外獎勵哦" />

            <!--<TextView
                Android:id="@+id/feedback_ok_text"
                Android:layout_width="match_parent"
                Android:layout_height="45dp"
                Android:layout_below="@id/feedback_info_text"
                Android:layout_marginLeft="@dimen/s1"
                Android:layout_marginRight="@dimen/s1"
                Android:layout_marginTop="18dp"
                Android:gravity="center"
                Android:text="提交"
                Android:textSize="@dimen/f2"
                Android:textColor="@color/c11"
                Android:background="@drawable/red_button_bg" />-->

            <include
                layout="@layout/b3_button"
                Android:layout_width="match_parent"
                Android:layout_height="48dp"
                Android:layout_below="@id/feedback_info_text"
                Android:layout_marginLeft="@dimen/s4"
                Android:layout_marginRight="@dimen/s4"
                Android:layout_marginTop="12dp"
                Android:gravity="center"
                Android:text="提交"
                Android:textSize="@dimen/f2"
                Android:textColor="@color/c11" />

        </RelativeLayout>
    </RelativeLayout>

</RelativeLayout>

再次執行查看繪製情況:
這裏寫圖片描述

咦?沒什麼變化麼?這是怎麼回事?其實對於Android系統來說,在佈局文件中新添加一個佈局控件只是會讓它存在佈局的邏輯關係而不會爲這個佈局控件重新繪製一次,只有這個佈局控件設置background的時候纔會重新繪製一次,下面我們爲這個RelativeLayout設置以下background

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="@color/c10">

    <!-- 這裏是新添加的佈局控件-->
    <RelativeLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:background="@color/c10"
        >

        <RelativeLayout
            Android:layout_height="match_parent"
            Android:layout_width="match_parent">

            <EditText
                Android:id="@+id/feedback_content_edit"
                Android:layout_width="match_parent"
                Android:layout_height="126dp"
                Android:layout_marginTop="18dp"
                Android:paddingLeft="@dimen/s3"
                Android:paddingRight="@dimen/s3"
                Android:paddingTop="@dimen/s2"
                Android:paddingBottom="@dimen/s2"
                Android:background="#FFFFFF"
                Android:textColorHint="@color/c4"
                Android:textSize="@dimen/f4"
                Android:textColor="@color/c3"
                Android:hint="儘量詳細描述您遇到的問題,感謝您給我們提出建議"
                Android:gravity="top|left" />

            <EditText
                Android:id="@+id/feedback_contact_edit"
                Android:layout_below="@id/feedback_content_edit"
                Android:layout_width="match_parent"
                Android:layout_height="45dp"
                Android:layout_marginTop="@dimen/s3"
                Android:paddingLeft="@dimen/s3"
                Android:paddingRight="@dimen/s3"
                Android:background="#FFFFFF"
                Android:textColorHint="@color/c4"
                Android:textSize="@dimen/f4"
                Android:textColor="@color/c3"
                Android:hint="請輸入您的手機號碼、QQ或者郵箱"
                Android:singleLine="true" />

            <TextView
                Android:id="@+id/feedback_info_text"
                Android:layout_below="@id/feedback_contact_edit"
                Android:layout_width="match_parent"
                Android:layout_height="wrap_content"
                Android:layout_marginTop="@dimen/s3"
                Android:paddingLeft="@dimen/s3"
                Android:paddingRight="@dimen/s3"
                Android:background="#00000000"
                Android:textSize="@dimen/f5"
                Android:textColor="@color/c5"
                Android:text="留下聯繫方式,以便能更好更快的給您答覆,還可能有意外獎勵哦" />

            <!--<TextView
                Android:id="@+id/feedback_ok_text"
                Android:layout_width="match_parent"
                Android:layout_height="45dp"
                Android:layout_below="@id/feedback_info_text"
                Android:layout_marginLeft="@dimen/s1"
                Android:layout_marginRight="@dimen/s1"
                Android:layout_marginTop="18dp"
                Android:gravity="center"
                Android:text="提交"
                Android:textSize="@dimen/f2"
                Android:textColor="@color/c11"
                Android:background="@drawable/red_button_bg" />-->

            <include
                layout="@layout/b3_button"
                Android:layout_width="match_parent"
                Android:layout_height="48dp"
                Android:layout_below="@id/feedback_info_text"
                Android:layout_marginLeft="@dimen/s4"
                Android:layout_marginRight="@dimen/s4"
                Android:layout_marginTop="12dp"
                Android:gravity="center"
                Android:text="提交"
                Android:textSize="@dimen/f2"
                Android:textColor="@color/c11" />

        </RelativeLayout>
    </RelativeLayout>

</RelativeLayout>

查看界面的繪製情況,我們發現:
這裏寫圖片描述

怎麼樣?藍色的區域變成綠色了,綠色的區域變成淡紅色了,淡紅色的區域變成紅色了,說明界面中存在過度繪製的情況,這時候我們就該考慮一下是否存在優化的可能了。

其他UI優化的Tips

  • 使用LieanrLayout替代RelativeLayout佈局;

  • 佈局文件中減少佈局層級,減少組件的嵌套層數;

  • 當有多個組件有相似的屬性時,可以使用styles,複用樣式定義;

  • 通過定義drawable來替代圖片資源的使用,降低內存消耗;

總結:

  • 過度繪製描述的是屏幕上一個像素在單個幀中被重繪了多少次。
    藍色:繪製一次
    綠色:繪製兩次
    淡紅色:繪製三次
    紅色:繪製四次

  • 爲佈局文件添加布局控件不會導致界面繪製次數的增加,只有爲佈局空間設置background時纔會導致繪製次數的增加

  • 當我們的界面紅色區域較多時就需要考慮我們是否存在過度繪製的問題了

  • 可以使用merge、include、ViewStub簡化佈局文件優化佈局繪製流程

  • 在使用佈局文件的時候儘量減少佈局層級


另外對產品研發技術,技巧,實踐方面感興趣的同學可以參考我的:
Android產品研發(十一)–>應用內跳轉scheme協議
Android產品研發(十二)–>App長連接實現
Android產品研發(十三)–>App輪訓操作
Android產品研發(十四)–>App升級與更新
Android產品研發(十五)–>內存對象序列化
Android產品研發(十六)–>開發者選項
Android產品研發(十七)–>Hybrid開發
Android產品研發(十八)–>webview問題集錦
Android產品研發(十九)–>Android studio中的單元測試
Android產品研發(二十)–>代碼Review


本文以同步至github中:https://github.com/yipianfengye/AndroidProject,歡迎star和follow


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章