標籤:git play 建立對象 new flat visible master 3.2 ...
#前言#
本篇文章為Android最佳化的布局部分,該部分應該是Android中很重要的,無論是在自訂控制項中,還是在簡單的書寫布局時,都應該盡量遵循一些最佳化原則,這樣布局的繪製效率才會更高,體驗才能更好。
#一 最佳化layout的層級#
Layout結構如果太複雜,Android的繪製過程就會很複雜,measure過程就會很複雜,我分析的View繪製機制中詳細介紹了整個測量、布局和繪製過程,過於複雜、嵌套的布局會造成效能問題。
1.1 避免嵌套
嵌套的 LinearLayout 可能會使得 View 的層級結構很深。使用LinearLayout時,通常我們喜歡用嵌套的布局來動態設定一個View的Visibility ,由於LinearLayout是線性,因此即使隱藏一個View也不會影響到其它View的排列。而在RelativeLayout中,View的位置都是相對於其它View的,因此,隱藏之後,會導致之前的View沒有參考對象了,導致的相對位置改變,這時你可以使用alignWithParentIfMissing=”true”來處理這種情況。
此外,嵌套使用了 layout_weight 參數的 LinearLayout 的計算量會尤其大,因為每個子項目都需要被測量兩次。這對需要多次重複 inflate 的 Layout 尤其需要注意,比如使用 ListView 或 GridView 時。
1.2 使用merge標籤最佳化層級
在使用了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 的根節點。例如:
| 12345678910111213 |
<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> |
現在,當你要將這個 Layout 包含到另一個 Layout 中時(並且使用了 標籤),系統會直接把兩個 Button 放到 Layout 中,而不會有多餘的 Layout 被嵌套。
#二 使用標籤重用Layout#
如果你的程式 UI 在不同地方重複使用某個 Layout,那本節教你如何建立高效的,可重用的 Layout 組件,並把它們“包含”到 UI Layout 中。
為了高效重用整個的 Layout,你可以使用 和 標籤把其他 Layout 嵌入當前 Layout。
三 按需載入視圖
除了簡單的把一個 Layout 包含到另一個中,你可能還想在程式開始後,僅當你的 Layout 對使用者可見時才開始載入。
3.1 不需要立即載入的布局,設定為GONE,系統會跳過,不載入3.2 使用ViewStub 實現按需載入
ViewStub 是一個輕量的視圖,不需要大小資訊,也不會在被加入的 Layout 中繪製任何東西。每個 ViewStub 只需要設定 android:layout 屬性來指定需要被 inflate 的 Layout 類型。viewstub常用來引入那些預設不會顯示,只在特殊情況下顯示的布局,如進度布局、網路失敗顯示的重新整理布局、資訊出錯出現的提示布局等。
以下 ViewStub 是一個半透明的進度條覆蓋層。功能上講,它應該只在新的資料項目被匯入到應用程式時可見。
| 1234567 |
<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
當你要載入用 ViewStub 聲明的 Layout 時,要麼用 setVisibility(View.VISIBLE) 設定它的可見度,要麼調用其 inflate() 方法。
下面以在一個布局main.xml中加入網路錯誤時的提示頁面network_error.xml為例。main.mxl代碼如下:
| 12345678910111213 |
<?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為只有在網路錯誤時才需要顯示的布局
setVisibility(View.VISIBLE)方式
| 123 |
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 儲存了標籤下的每個視圖。這樣你不用頻繁尋找這個元素:
| 1234567891011121314 |
static class ViewHolder { TextView text; TextView timestamp; ImageView icon; ProgressBar progress; int position;}``` 然後,在 Layout 的類中產生一個 ViewHolder 對象:```javaViewHolder 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等,規範通用的命名可以很方便的移植到其它項目中。
| 123456789101112 |
<color name="list_item_large">#FCA558</color> <color name="list_item_small">#FBA228</color> <dimen name="list_item_large">24dp</dimen> <dimen name="list_item_small">18dp</dimen> <!-- 簡單ListView樣式 --><style name="list_view_style_default"> <item name="android:layout_width">fill_parent</item> <item name="android:layout_height">wrap_content</item> ...</style> |
Android布局最佳化