編輯:關於Android編程
本篇文章為Android優化的布局部分,該部分應該是Android中很重要的,無論是在自定義控件中,還是在簡單的書寫布局時,都應該盡量遵循一些優化原則,這樣布局的繪制效率才會更高,體驗才能更好。
Layout結構如果太復雜,Android的繪制過程就會很復雜,measure過程就會很復雜,我分析的View繪制機制中詳細介紹了整個測量、布局和繪制過程,過於復雜、嵌套的布局會造成性能問題。
嵌套的 LinearLayout 可能會使得 View 的層級結構很深。使用LinearLayout時,通常我們喜歡用嵌套的布局來動態設置一個View的Visibility ,由於LinearLayout是線性的,因此即使隱藏一個View也不會影響到其它View的排列。而在RelativeLayout中,View的位 置都是相對於其它View的,因此,隱藏之後,會導致之前的View沒有參考對象了,導致的相對位置改變,這時你可以使用 alignWithParentIfMissing=”true”來處理這種情況。
此外,嵌套使用了 layout_weight 參數的 LinearLayout 的計算量會尤其大,因為每個子元素都需要被測量兩次。這對需要多次重復 inflate 的 Layout 尤其需要注意,比如使用 ListView 或 GridView 時。
在使用了include後可能導致布局嵌套過多,出現不必要的layout節點,從而導致解析變慢。
merge標簽可用於兩種典型情況:
布局頂結點是FrameLayout且不需要設置background或padding等屬性,可以用merge代替,因為Activity內容試圖的parent view就是個FrameLayout,所以可以用merge消除只剩一個。
某布局作為子布局被其他布局include時,使用merge當作該布局的頂節點,這樣在被引入時頂結點會自動被忽略,而將其子節點全部合並到主布局中。
比如,如果你有一個 Layout 是一個豎直方向的 LinearLayout,其中包含兩個連續的 View 可以在別的 Layout 中重用,那麼你會做一個 LinearLayout 來包含這兩個 View ,以便重用。不過,當使用另一個 LinearLayout 來嵌套這個可重用的 LinearLayout 時,這種嵌套 LinearLayout 的方式除了減慢你的 UI 性能外沒有任何意義。
為了避免這種情況,你可以用 元素來替代可重用 Layout 的根節點。例如:
1 2 3 4 5 6 7 8 9 10"http://schemas.android.com/apk/res/android"
>
現在,當你要將這個 Layout 包含到另一個 Layout 中時(並且使用了 標簽),系統會直接把兩個 Button 放到 Layout 中,而不會有多余的 Layout 被嵌套。
如果你的程序 UI 在不同地方重復使用某個 Layout,那本節教你如何創建高效的,可重用的 Layout 部件,並把它們“包含”到 UI Layout 中。
為了高效重用整個的 Layout,你可以使用 和 標簽把其他 Layout 嵌入當前 Layout。
除了簡單的把一個 Layout 包含到另一個中,你可能還想在程序開始後,僅當你的 Layout 對用戶可見時才開始載入。
ViewStub 是一個輕量的視圖,不需要大小信息,也不會在被加入的 Layout 中繪制任何東西。每個 ViewStub 只需要設置 android:layout 屬性來指定需要被 inflate 的 Layout 類型。viewstub常用來引入那些默認不會顯示,只在特殊情況下顯示的布局,如進度布局、網絡失敗顯示的刷新布局、信息出錯出現的提示布局等。
以下 ViewStub 是一個半透明的進度條覆蓋層。功能上講,它應該只在新的數據項被導入到應用程序時可見。
1 2 3 4 5 6 7
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
當你要載入用 ViewStub 聲明的 Layout 時,要麼用 setVisibility(View.VISIBLE) 設置它的可見性,要麼調用其 inflate() 方法。
下面以在一個布局main.xml中加入網絡錯誤時的提示頁面network_error.xml為例。main.mxl代碼如下:
1
2
3
4
5
6
7
8
9
10
11
"1.0"
encoding=
"utf-8"
?>
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
……
android:id=
"@+id/network_error_layout"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout=
"@layout/network_error"
/>
其中network_error.xml為只有在網絡錯誤時才需要顯示的布局
setVisibility(View.VISIBLE)方式
1
2
3
View viewStub = ((ViewStub)findViewById(R.id.stub_import));
viewStub.setVisibility(View.VISIBLE);
netErrorLayout = findViewById(R.id.net_error_layout))
inflate方式
1
View netErrorLayout = ((ViewStub) findViewById(R.id.net_error_layout)).inflate();
注意:
inflate() 方法會在渲染完成後返回給被 inflate 的視圖,所以你不需要再調用 findViewById() 去查找這個元素。減少inflate的次數,也會對效率有一點提升。
而setVisible方式還需要再次findViewById找到ViewStub中的元素。
一旦 ViewStub 可見或是被 inflate 了,ViewStub 元素就不存在了。取而代之的是被 inflate 的 Layout,其 id 是 ViewStub 上的 android:inflatedId 屬性。(ViewStub 的 android:id 屬性僅在 ViewStub 可見以前可用)
注意:ViewStub 的一個缺陷是,它目前不支持使用 標簽的 Layout
四 ListView的優化
如果你有一個包含復雜或者每個項 (item) 包含很多數據的 ListView ,那麼上下滾動的性能可能會降低。本節給你一些關於如何把滾動變得更流暢的提示。
保持程序流暢的關鍵,是讓主線程(UI 線程)不要進行大量運算。你要確保在其他線程執行磁盤讀寫、網絡讀寫或是 SQL 操作等。為了測試你的應用的狀態,你可以啟用 StrictMode。
4.1 使用後台線程
你應該把主線程中的耗時間的操作,提取到一個後台線程中,使得主線程只關注 UI 繪畫。
4.2 在 View Holder 中填入視圖對象
使用convertView、
你的代碼可能在 ListView 滑動時經常使用 findViewById(),這樣會降低性能。即使是 Adapter 返回一個用於回收的 convertView,你仍然需要查找這個元素並更新它。避免頻繁調用 findViewById() 的方法之一,就是使用 View Holder(視圖占位符)設計模式。
ViewHolder 存儲了標簽下的每個視圖。這樣你不用頻繁查找這個元素:
1
2
3
4
5
6
7
static class ViewHolder {
TextView text;
TextView timestamp;
ImageView icon;
ProgressBar progress;
int position;
}
然後,在 Layout 的類中生成一個 ViewHolder 對象:
1
2
3
4
ViewHolder holder =
new
ViewHolder();
holder.icon = (ImageView) convertView.findViewById(R.id.listitem_image);
...
convertView.setTag(holder);
這樣你就可以輕松獲取每個視圖,而不是用 findViewById() 來不斷查找視圖,節省了寶貴的運算時間。
4.3 getView不要做復雜的操作
因為每一條Item移入屏幕的時候,都會調用getView,不要在getView中做復雜的操作,不要頻繁的創建對象。Item點擊的處理不要提前做。特別是在快速滑動的時候,會導致頻繁的調用getView。
五 優化提示
盡量使用RelativeLayout,可以減少層級的嵌套。
慎用LinearLayout的layout_weight屬性,可以使用RelativeLayout的centerHorizontal=”true”、toLeft、toRight代替
六 書寫規范上的優化
6.1 Id的命名
為了便於識別,你可以根據自己的業務來對當前界面的資源進行命名,比如當前是登陸界面,那麼你可以這樣命名:
login_edit_username
login_edit_password
login_btn_submit
login_txv_forgot_pass
6.2 資源的命名
ic_action_add, ic_action_location (ActionBar Icons)
ic_play, ic_save (General Icons)
ic_tab_music, ic_tab_more (Tab Icons)
6.3 通用的資源命名
對style.xml和dimens.xml的命名可以通用的盡量通用,因為一個項目的基本視圖很多都是通用的,比如ActionBar、ListView等,規范通用的命名可以很方便的移植到其它項目中。
1
2
3
4
5
6
7
8
9
10
"list_item_large"
>
#FCA558
"list_item_small"
>
#FBA228
"list_item_large"
>24dp
"list_item_small"
>18dp
簡介系統自帶的搜索頁面類 — UISearchDisplayController和UISearchController, 讓你更方便快捷的進行搜索功能開發.
當在非UI線程中更新UI(程序界面)時會出現如下圖所示的異常: 那如何才能在非UI線程中更細UI呢? 方法有很多種,在這裡主要介紹兩種: 第一種:在需要更新UI的代碼
這裡實現兩種側滑菜單效果,第一種拖拽內容部分,菜單像是被拖出來的感覺的這種效果,第二種是拖拽內容部分,菜單在內容後面不動,感覺有一種層次感的效果,如下 packa
看了很長時間Vold存儲模塊的相關知識,也深入的研究一段時間的Android源碼,打算把自己看過的經驗之貼、參考資料和自己的一些見解,以帖子的形式發出來,供有興趣的同仁們