前些天在 那些年大家都在談論的Android性能優化 中已經跟大家分享了Android中性能優化的概念,優化的一些關鍵點以及優化方案。今天跟大家探討一下AndroidUI優化中的佈局優化具體是怎麼操作的。
選擇合適的ViewGroup
1.FrameLayout和LinearLayout是Android中比較簡單並且高效的viewgroup,如果不考慮佈局嵌套的話,儘量使用這兩種。
2.大多數情況單純使用FrameLayout或者LinearLayout無法實現產品效果,需要進行佈局嵌套,這種情況儘量使用RelativeLayout。嵌套會增加布局的層級,降低App的性能。大約在Android4.0之後,新建工程的默認main.xml中頂節點改成了RelativeLayout,因爲RelativeLayout性能更優,可以簡單實現LinearLayout嵌套才能實現的佈局。
抽象佈局標籤
include
include主要用於佈局重用,將佈局中的公共部分抽離出來供其他佈局用,實現佈局的模塊化,比如App的頂欄等。
<?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">
<include
android:id="@+id/title"
layout="@layout/layout_title" />
<android.support.v4.view.ViewPager
android:id="@+id/fragment_home_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
include標籤唯一需要的屬性是layout屬性,指定需要包含的佈局文件。可以定義android:id和android:layout_*屬性來覆蓋被引入佈局根節點的對應屬性值。注意重新定義android:id後,子佈局的頂結點id就變化了。如果被指定了android:layout_*屬性,那麼android:layout_width和android:layout_height也必須存在,否則其他android:layout_*屬性不生效。
merge
merge標籤一般和include標籤一起使用,可以減少佈局的層級。假如我的layout_title文件中的根節點也是豎直方向的linearlayout,我們可以通過merge標籤來減少這一層linearlayout。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</merge>
另一種情況是當佈局頂結點是FrameLayout且不需要設置background或padding等屬性,可以用merge代替,因爲Activity內容視圖的parent view就是個FrameLayout,所以可以用merge消除只剩一個。
viewstub
viewstub引入的佈局默認不會擴張,即既不會佔用顯示也不會佔用位置,不參與任何的佈局和繪製過程,從而在解析layout時節省cpu和內存。它的意義在於按需加載佈局文件,比如網絡異常的界面正常情況下是不會顯示的,我們就沒有必要在初始化的時候就把它加載進來。
<?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">
<include
android:id="@+id/title"
layout="@layout/layout_title" />
<android.support.v4.view.ViewPager
android:id="@+id/fragment_home_viewpager"
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" />
</LinearLayout>
network_error的內容
<?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="160dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="@string/network_setting" />
<Button
android:id="@+id/network_refresh"
android:layout_width="160dp"
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>
如果要顯示viewstub中的內容,可以通過setVisibility或者inflate進行加載。
private View networkErrorView;
private void showNetError() {
//保證不重複加載
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);
}
}
減少不必要的infalte
1.對於inflate的佈局可以直接緩存,用全部變量代替局部變量,避免下次需再次inflate
2.ListView提供了item緩存,adapter getView的標準寫法,如下:
@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;
}
決定偉大水平和一般水平的關鍵因素,既不是天賦,也不是經驗,而是刻意練習的程度。從下一頁面開始,希望大家都能按照這些標準去寫,刻意練習,熟能生巧。