上一篇文章中我們講解了Android產品研發過程中的代碼Review。通過代碼Review能夠提高產品質量,增強團隊成員之間的溝通,提高開發效率,所以良好的產品開發迭代過程中,代碼Review是一個必不可少的步驟。那么如何進行代碼Review呢?我們主要講解了團隊成員之間的代碼Review,代碼lint檢查,開發規范等方面的知識點,更多關于代碼Review相關的知識可參考我的:[Android產品研發(二十)–>代碼Review](http://blog.csdn.net/qq_23547831/article/details/51833080)
本文我們將講解一下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協議](http://blog.csdn.net/qq_23547831/article/details/51685310)
[Android產品研發(十二)–>App長連接實現](http://blog.csdn.net/qq_23547831/article/details/51719389)
[Android產品研發(十三)–>App輪詢操作](http://blog.csdn.net/qq_23547831/article/details/51764773)
[Android產品研發(十四)–>App升級與更新](http://blog.csdn.net/qq_23547831/article/details/51764773)
[Android產品研發(十五)–>內存對象序列化](http://blog.csdn.net/qq_23547831/article/details/51779528)
[Android產品研發(十六)–>開發者選項](http://blog.csdn.net/qq_23547831/article/details/51809497)
[Android產品研發(十七)–>hybrid開發](http://blog.csdn.net/qq_23547831/article/details/51812985)
[Android產品研發(十八)–>webview問題集錦](http://blog.csdn.net/qq_23547831/article/details/51820139)
[Android產品研發(十九)–>Android studio中的單元測試](http://blog.csdn.net/qq_23547831/article/details/51868451)
[Android產品研發(二十)–>代碼Review](http://blog.csdn.net/qq_23547831/article/details/51833080)
- 前言
- (一)–>實用開發規范
- (二)-->啟動頁優化
- (三)-->基類Activity
- (四)-->減小Apk大小
- (五)-->多渠道打包
- (六)-->Apk混淆
- (七)-->Apk熱修復
- (八)-->App數據統計
- (九)-->App網絡數據解析
- (十)-->盡量不使用靜態變量保存數據
- (十一)-->應用內跳轉Scheme協議
- (十二)-->App長連接實現
- (十三)-->App輪詢操作
- (十四)-->App升級與更新
- (十五)-->內存對象序列化
- (十六)-->開發者選項
- (十七)-->Hybrid開發
- (十八)-->webview問題集錦
- (十九)-->Android studio中的單元測試
- (二十)-->代碼Review
- (二十一)-->Android中的UI優化
- (二十二)-->Android實用調試技巧
- (二十三)-->Android中保存靜態秘鑰實踐
- (二十四)-->內存泄露場景與檢測
- (二十五)-->MVC/MVVM/MVP簡單理解