1、抽象佈局標籤
(1) <include>標籤
include標籤常用於將佈局中的公共部分提取出來供其他layout共用,以實現佈局模塊化,這在佈局編寫方便提供了大大的便利。
下面以在一個佈局main.xml中用include引入另一個佈局foot.xml爲例。main.mxl代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android:id="@+id/simple_list_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="@dimen/dp_80" /> <include layout="@layout/foot.xml" /> </RelativeLayout> |
其中include引入的foot.xml爲公用的頁面底部,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?xml
version="1.0"
encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_above="@+id/text"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_alignParentBottom="true"
android:text="@string/app_name"
/>
</RelativeLayout>
|
<include>標籤唯一需要的屬性是layout屬性,指定需要包含的佈局文件。可以定義android:id和android:layout_*屬性來覆蓋被引入佈局根節點的對應屬性值。注意重新定義android:id後,子佈局的頂結點i就變化了。
(2) <viewstub>標籤
viewstub標籤同include標籤一樣可以用來引入一個外部佈局,不同的是,viewstub引入的佈局默認不會擴張,即既不會佔用顯示也不會佔用位置,從而在解析layout時節省cpu和內存。
viewstub常用來引入那些默認不會顯示,只在特殊情況下顯示的佈局,如進度佈局、網絡失敗顯示的刷新佈局、信息出錯出現的提示佈局等。
下面以在一個佈局main.xml中加入網絡錯誤時的提示頁面network_error.xml爲例。main.mxl代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > …… <ViewStub android:id="@+id/network_error_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout="@layout/network_error" /> </RelativeLayout> |
其中network_error.xml爲只有在網絡錯誤時才需要顯示的佈局,默認不會被解析,示例代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?xml
version="1.0"
encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/network_setting"
android:layout_width="@dimen/dp_160"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="@string/network_setting"
/>
<Button
android:id="@+id/network_refresh"
android:layout_width="@dimen/dp_160"
android:layout_height="wrap_content"
android:layout_below="@+id/network_setting"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/dp_10"
android:text="@string/network_refresh"
/>
</RelativeLayout>
|
在java中通過(ViewStub)findViewById(id)找到ViewStub,通過stub.inflate()展開ViewStub,然後得到子View,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | private View networkErrorView; private void showNetError() { // not repeated infalte if (networkErrorView != null) { networkErrorView.setVisibility(View.VISIBLE); return; } ViewStub stub = (ViewStub)findViewById(R.id.network_error_layout); networkErrorView = stub.inflate(); Button networkSetting = (Button)networkErrorView.findViewById(R.id.network_setting); Button refresh = (Button)findViewById(R.id.network_refresh); } private void showNormal() { if (networkErrorView != null) { networkErrorView.setVisibility(View.GONE); } } |
在上面showNetError()中展開了ViewStub,同時我們對networkErrorView進行了保存,這樣下次不用繼續inflate。這就是後面第三部分提到的減少不必要的infalte。
viewstub標籤大部分屬性同include標籤類似。
上面展開ViewStub部分代碼
1
2
|
ViewStub
stub
=
(ViewStub)findViewById(R.id.network_error_layout);
networkErrorView
=
stub.inflate();
|
也可以寫成下面的形式
1 2 3 | View viewStub = findViewById(R.id.network_error_layout); viewStub.setVisibility(View.VISIBLE); // ViewStub被展開後的佈局所替換 networkErrorView = findViewById(R.id.network_error_layout); // 獲取展開後的佈局 |
效果一致,只是不用顯示的轉換爲ViewStub。通過viewstub的原理我們可以知道將一個view設置爲GONE不會被解析,從而提高layout解析速度,而VISIBLE和INVISIBLE這兩個可見性屬性會被正常解析。
(3) <merge>標籤
在使用了include後可能導致佈局嵌套過多,多餘不必要的layout節點,從而導致解析變慢,不必要的節點和嵌套可通過hierarchy viewer(下面佈局調優工具中有具體介紹)或設置->開發者選項->顯示佈局邊界查看。
merge標籤可用於兩種典型情況:
a. 佈局頂結點是FrameLayout且不需要設置background或padding等屬性,可以用merge代替,因爲Activity內容試圖的parent view就是個FrameLayout,所以可以用merge消除只剩一個。
b. 某佈局作爲子佈局被其他佈局include時,使用merge當作該佈局的頂節點,這樣在被引入時頂結點會自動被忽略,而將其子節點全部合併到主佈局中。
以(1) <include>標籤的示例爲例,用hierarchy viewer查看main.xml佈局如下圖:
可以發現多了一層沒必要的RelativeLayout,將foot.xml中RelativeLayout改爲merge,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?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"
>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_above="@+id/text"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_alignParentBottom="true"
android:text="@string/app_name"
/>
</merge>
|
運行後再次用hierarchy viewer查看main.xml佈局如下圖:
這樣就不會有多餘的RelativeLayout節點了。
2、去除不必要的嵌套和View節點
(1) 首次不需要使用的節點設置爲GONE或使用viewstub
(2) 使用RelativeLayout代替LinearLayout
大約在Android4.0之前,新建工程的默認main.xml中頂節點是LinearLayout,而在之後已經改爲RelativeLayout,因爲RelativeLayout性能更優,且可以簡單實現LinearLayout嵌套才能實現的佈局。
4.0及以上Android版本可通過設置->開發者選項->顯示佈局邊界打開頁面佈局顯示,看看是否有不必要的節點和嵌套。4.0以下版本可通過hierarchy viewer查看。
3、減少不必要的infalte
(1) 對於inflate的佈局可以直接緩存,用全部變量代替局部變量,避免下次需再次inflate
如上面ViewStub示例中的
1 2 3 4 | if (networkErrorView != null) { networkErrorView.setVisibility(View.VISIBLE); return; } |
(2) ListView提供了item緩存,adapter getView的標準寫法,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
@Override
public
View
getView(int
position,
View
convertView,
ViewGroup
parent)
{
ViewHolder
holder;
if
(convertView
==
null)
{
convertView
=
inflater.inflate(R.layout.list_item,
null);
holder
=
new
ViewHolder();
……
convertView.setTag(holder);
}
else
{
holder
=
(ViewHolder)convertView.getTag();
}
}
/**
*
ViewHolder
*
*
@author [email protected] 2013-08-01
*/
private
static
class
ViewHolder
{
ImageView
appIcon;
TextView appName;
TextView appInfo;
}
|
關於ListView緩存原理可見Android ListView緩存機制。
4、其他點
(1) 用SurfaceView或TextureView代替普通View
SurfaceView或TextureView可以通過將繪圖操作移動到另一個單獨線程上提高性能。
普通View的繪製過程都是在主線程(UI線程)中完成,如果某些繪圖操作影響性能就不好優化了,這時我們可以考慮使用SurfaceView和TextureView,他們的繪圖操作發生在UI線程之外的另一個線程上。
因爲SurfaceView在常規視圖系統之外,所以無法像常規試圖一樣移動、縮放或旋轉一個SurfaceView。TextureView是Android4.0引入的,除了與SurfaceView一樣在單獨線程繪製外,還可以像常規視圖一樣被改變。
(2) 使用RenderJavascript
RenderScript是Adnroid3.0引進的用來在Android上寫高性能代碼的一種語言,語法給予C語言的C99標準,他的結構是獨立的,所以不需要爲不同的CPU或者GPU定製代碼代碼。
(3) 使用OpenGL繪圖
Android支持使用OpenGL API的高性能繪圖,這是Android可用的最高級的繪圖機制,在遊戲類對性能要求較高的應用中得到廣泛使用。
Android 4.3最大的改變,就是支持OpenGL ES 3.0。相比2.0,3.0有更多的緩衝區對象、增加了新的着色語言、增加多紋理支持等等,將爲Android遊戲帶來更出色的視覺體驗。
(4) 儘量爲所有分辨率創建資源
減少不必要的硬件縮放,這會降低UI的繪製速度,可藉助Android asset studio
5、佈局調優工具
(1) hierarchy viewer
hierarchy viewer可以方便的查看Activity的佈局,各個View的屬性、measure、layout、draw的時間,如果耗時較多會用紅色標記,否則顯示綠色。
hierarchy viewer.bat位於<sdk>/tools/目錄下。使用可見:Using Hierarchy Viewer , 示例圖如下:
(2) layoutopt
layoutopt是一個可以提供layout及其層級優化提示的命令行,在sdk16以後已經被lint取代,在Windows->Show View->Other->Android->Lint Warnings查看lint優化提示,lint具體介紹可見Improving
Your Code with lint。