1、RelativeLayout 優於 LinearLayout
Android中最常用LinearLayout來表示UI的框架,而且也是最直觀和方便的方法,例如創建一個UI用於展現Item的基本內容,如圖所示:
線框示意圖:
通過LinearLayout實現以上UI的代碼:
- <LinearLayout xmlns:
- android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:padding="6dip">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_marginRight="6dip"
- android:src="@drawable/icon" />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="fill_parent">
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:text="My Application" />
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:text="Simple application that shows how to use RelativeLayout" />
- </LinearLayout>
- </LinearLayout>
儘管可以通過Linearlayout實現我們所預想的結果,但是在這裏存在一個優化的問題,尤其是針對大量Items時。比較RelativeLayout和LinearLayout,它們在資源利用上,前者會佔用更少的資源而達到相同的效果,以下是用RelativeLayout來實現同樣UI的代碼:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:padding="6dip">
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_alignParentTop="true"
- android:layout_alignParentBottom="true"
- android:layout_marginRight="6dip"
- android:src="@drawable/icon" />
- <TextView
- android:id="@+id/secondLine"
- android:layout_width="fill_parent"
- android:layout_height="26dip"
- android:layout_toRightOf="@id/icon"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:text="Simple application that shows how to use RelativeLayout" />
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/icon"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_above="@id/secondLine"
- android:layout_alignWithParentIfMissing="true"
- android:gravity="center_vertical"
- android:text="My Application" />
- </RelativeLayout>
使用RelativeLayout時有一點需要注意,因爲它內部是通過多個View之間的關係而確定UI框架,那麼當其中某一個View因爲某些需要調用GONE來完全隱藏掉後,會影響與其相關聯的Views。Android爲我們提供了一個屬性 alignWithParentIfMissing 用於解決類似問題,當某一個View無法找到與其相關聯的Views後將依據alignWithParentIfMissing的設定判斷是否與父級View對齊。
你可以通過Hierarchy Viewer查看兩種佈局方案的View層級圖,RelativeLayout明顯優於LinearLayout。
2、UI資源的複用
定義Android Layout(XML)時,有四個比較特別的標籤是非常重要的,其中有三個是與資源複用有關,分別是:<viewStub/>,<merge/>和<include/> ,可是以往我們所接觸的案例或者官方文檔的例子都沒有着重去介紹這些標籤的重要性。
<include/> :可以通過這個標籤直接加載外部的xml到當前結構中,是複用UI資源的常用標籤。使用方法:將需要複用xml文件路徑賦予include標籤的Layout屬性,示例如下:
- <include android:id="@+id/cell1" layout="@layout/ar01"/>
- <include android:layout_width="fill_parent" layout="@layout/ar02"/>
< viewStub/
> :
此標籤可以使UI在特殊情況下,直觀效果類似於設置View的不可見性,但是其更大的意義在於被這個標籤所包裹的Views在默認狀態下不會佔用任何內存空間。viewStub通過include從外部導入Views元素。使用方法:通過android:layout來指定所包含的內容,默認情況下,ViewStub所包含的標籤都屬於visibility=GONE。viewStub通過方法inflate()來召喚系統加載其內部的Views。
示例:
- <ViewStub android:id="@+id/stub"
- android:inflatedId="@+id/subTree"
- android:layout="@layout/mySubTree"
- android:layout_width="120dip"
- android:layout_height="40dip" />
<ViewStub android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
android:layout_width="120dip"
android:layout_height="40dip" />
<merge/> :該標籤在優化UI結構時起到很重要的作用,目的是通過刪減多餘或者額外的層級,從而優化整個UI Layout的結構。以下將通過一個例子來了解這個標籤實際所產生的作用,這樣可以更直觀的瞭解 <merge/>的 用法。
建立一個簡單的Layout,其中包含兩個Views元素:ImageView和TextView,默認狀態下我們將這兩個元素放在FrameLayout中。其效果是在主視圖中全屏顯示一張圖片,之後將標題顯示在圖片上,並位於視圖的下方。以下是xml代碼:
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <ImageView
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:scaleType="center"
- android:src="@drawable/golden_gate" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="20dip"
- android:layout_gravity="center_horizontal|bottom"
- android:padding="12dip"
- android:background="#AA000000"
- android:textColor="#ffffffff"
- android:text="Golden Gate" />
- </FrameLayout>
以上佈局的效果圖如下:
啓動 tools> hierarchyviewer.bat工具查看當前UI結構視圖:
我們可以很明顯的看到由紅色線框所包含的結構出現了兩個framelayout節點,很明顯這兩個完全意義相同的節點造成了資源浪費(這裏可以提醒大家在開發工程中可以習慣性的通過hierarchyViewer查看當前UI資源的分配情況),那麼如何才能解決這種問題呢?(就當前例子是如何去掉多餘的frameLayout節點),這時候就要用到 <merge/> 標籤了,我們將上邊xml代碼中的framLayout替換成merge:
- <merge xmlns:android="http://schemas.android.com/apk/res/android">
- <ImageView
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:scaleType="center"
- android:src="@drawable/golden_gate" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="20dip"
- android:layout_gravity="center_horizontal|bottom"
- android:padding="12dip"
- android:background="#AA000000"
- android:textColor="#ffffffff"
- android:text="Golden Gate" />
- </merge>
運行程序後在Emulator中顯示的效果是一樣的,可是通過hierarchyviewer查看的UI結構是有變化的,當初多餘的 FrameLayout節點被合併在一起了,或者可以理解爲將merge標籤中的子集直接加到Activity的FrameLayout根節點下了。(這裏需要提醒大家注意:所有的Activity視圖的根節點都是FrameLayout)。如果你所創建的Layout並不是用FramLayout作爲根節點(而是應用LinerLayout等定義root標籤),就不能應用上邊的例子 通過merge來優化UI結構了。
除了上邊的例子外,meger還有另外一個用法:當應用Include或者ViewStub標籤從外部導入xml結構時,可以將被導入的xml用merge作爲根節點,這樣當被嵌入父級結構中後可以很好的將它所包含的子集融合到父級結構中,而不會出現冗餘節點。
另外有兩點需要特別注意:
A.<merge/>只可以作爲xml layout的根節點;
B.當需要擴充的xml layout本身是由merge作爲根節點的話,需要將被導入的xml layout置於viewGroup中,同時需要設置attachToRoot爲True。(更多說明請參見inflate()文檔)
3、使用工具 hierarchyViewer 和 Layout Opt.
前邊已經介紹瞭如何通過Layout優化系統資源,減少不必要的資源佔用。基於如何在合理利用資源的前提下,更進一步的提升視覺表現力。這裏所提到的視覺表現力並不是指直觀所看到的視覺效果,而是性能的提升。
這裏將包含兩個主要內容:
- Drawing(繪製的刷新率)
- Startup Time (啓動Activities的時間)
以上兩個性能的優化都將依賴於 Window backGround drawable功能設置。
通過Window backGround標識會對部分人產生一定的誤解,其實每次通過setContentView()來顯示預先配置的界面時,Android僅僅是將你所創建的Views添加到Activiy的Window中。而且這個Window並不僅僅包含你所創建的Views,還包括Android爲Activity預置的元素。通過Emulator運行你的程序,然後通過Hierarchy Viewer查看當前程序UI架構Root節點 DecorView,這個是Android額外添加的最頂層的節點。
實際上Window background drawable是由DecorView決定的。可以在Activity中調用getWindow().setBackgroundDrawable()方法來設置DecorView的background drawable。這裏要特別注意這個方法是針對當前版本的Android平臺,以後可能會因爲版本的更新或不同的硬件設備而改變。(目前我們僅針對 G1,如果在其它環境下測試需要小心驗證)
如果目前正在使用android默認的標準Themes,那麼在調用getWindow().setBackgroundDrawable()方法之後,這個background drawable將會影響你的activities。通過一個例子來直觀的比較性能提升的效果:
通過上邊這個截圖可以看到保持activity的redraw模式下,當前的FPS爲39幀 /每秒,大概相當於25毫秒/每幀。由於這個例子中將ImageView設爲全屏顯示,並且完全覆蓋了activity的背景,所以background drawable佔用了不必要的計算資源。下邊創建一個新的Theme並且應用在Activity中,創建res/values/theme.xml, XML的內容:
- <resources>
- <style name="Theme.NoBackground" parent="android:Theme">
- <item name="android:windowBackground">@null</item>
- </style>
- </resources>
同時也需要修改AndroidMainfest.xml文件,將上邊所創建的Theme應用到Activity中,格式爲:
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".EggImageViewer"
- android:label="@string/app_name"
- android:theme="@style/Theme.NoBackground">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
(也可以將Theme應用於<Application />標籤中)完成上邊的修改後,再來看看當前的FPS發生了什麼變化:
FPS可以增大到60+,性能得到如此大的提升,是不是比較難以置信呢?由此可以明白當應用MapView或者WebView全屏顯示的應用程序時,可以借用這個簡單設置的方法,來提升整體的性能。
通過上邊的例子可以直觀的瞭解window background drawable的優化方法,並且根據比較可以很明顯的看到設置前後的性能對比,希望這個簡單的技巧可以給你的應用帶來幫助。
另外基於這個技巧的應用,還可以延展出另外一個優化功能。對於某些需要全屏顯示的應用程序,可以通過Theme定義Window’s background的圖片,從而提高Activity的啓動速度。畢竟無需建立一個ImageView作爲自定義的背景,而重複疊加在Activity window background。
實現方法:
創建 res/drawable/background_res.xml
- <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/shelf_panel"
- android:tileMode="repeat" />
創建res/values/theme.xml
- <resources>
- <style name="Theme.Shelves" parent="android:Theme">
- <item name="android:windowBackground">@drawable/background_res</item>
- <item name="android:windowNoTitle">true</item>
- </style>
- </resources>
往往一些看似簡單的方法,卻能起到非常大的作用。還希望大家在實踐中能不斷的挖掘出非常實用的技巧,然後與你周圍的人共享你的發現,同時在大家共同的見證下可以完善你的想法!