Android 布局最佳化

來源:互聯網
上載者:User

標籤:簡單   main   使用   attr   utf-8   tor   特殊情況   其他   raw   

在開發過程中我們經常說效能最佳化,但效能最佳化是一個比較寬泛的概念。在Android開發中效能最佳化可能包括:Java代碼最佳化, 演算法最佳化, SQLite最佳化, 布局最佳化等。那麼這篇部落格就來總結並分享下Android開發中的布局最佳化。

布局原則

在Android UI版面配置階段中,通過遵守一些慣用、有效布局原則,我們可以製作出高效且複用性高的UI,概括來說包括如下幾點:

  • 盡量多使用RelativeLayout和LinearLayout, 不要使用絕對布局AbsoluteLayout,在布局層次一樣的情況下, 建議使用LinearLayout代替RelativeLayout, 因為LinearLayout效能要稍高一點,但往往RelativeLayout可以簡單實現LinearLayout嵌套才能實現的布局。

  • 將可複用的組件抽取出來並通過include標籤使用;

  • 使用ViewStub標籤來載入一些不常用的布局;

  • 使用merge標籤減少布局的嵌套層次;

RelativeLayout VS LinearLayout

第一條原則說了布局層次一樣的情況下LinearLayout比RelativeLayout要好, 但往往RelativeLayout可以簡單實現LinearLayout嵌套才能實現的布局。假如需要實現如下布局:

用LinearLayout來實現xml代碼如下:

<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>

而用RelativeLayout實現代碼如下:

<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實現,布局層次明顯少了,所以大多數時候優先推薦使用RelativeLayout。

查看布局層次

如何查看布局層次呢?有兩種辦法:一是通過手機的開發人員選項,4.0及以上Android版本可通過設定->開發人員選項->顯示布局邊界開啟頁面配置顯示,看看是否有不必要的節點和嵌套。第二種就是利用SDK內建的UI效能偵查工具HierarchyViewer。 進入sdk目錄下的tools檔案夾下,找到HierarchyViewer並運行(此時保持你的模擬器或真機正在運行需要進行分析的App),雙擊我們正在顯示的這個App所代表的進程。接下來便會進入hierarchyviewer的介面,我們可以在這裡很清晰看到正在啟動並執行UI的布局階層以及它們之間的關係。大概的顯示如:

通過布局圖我們可以看到根節點DecorView下包含一個LinearLayout, 這個LinearLayout就是包含Activity布局和狀態列的整個螢幕顯示的布局父節點,這個LinearLayout有兩個子節點, 一個是FrameLayout, FrameLayout就是Activity布局中預設的父布局節點, 這個節點下面就包含了我們自己寫的xml布局, 還有一個子節點就是ViewStub,關於這個節點我們在後面會詳細介紹。

< include />的使用

在實際開發中,我們經常會遇到一些共用的UI組件,比如帶返回按鈕的導覽列,如果為每一個xml檔案都設定這部分布局,一是重複的工作量大,二是如果有變更,那麼每一個xml檔案都得修改。還好,Android為我們提供了include標籤,顧名思義,通過它,我們可以將這些共用的組件抽取出來單獨放到一個xml檔案中,然後使用include標籤匯入共用布局,這樣,前面提到的兩個問題都解決了。下面以在一個布局main.xml中用include引入另一個布局header.xml為例。

header.xml檔案

<?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>

然後我們在需要引入footer的布局xml中通過include匯入這個共用布局。

main.xml檔案

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <TextView        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="hello world" />    <RelativeLayout         android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_gravity="center" >        <include layout="@layout/header" />    </RelativeLayout></FrameLayout>

通過這種方式,我們既能提高UI的製作和複用效率,也能保證製作的UI布局更加規整和易維護。

< merge />的使用

merge標籤的作用是合并UI布局,使用該標籤能降低UI布局的嵌套層次。merge標籤可用於兩種典型情況:

  • 布局根結點是FrameLayout且不需要設定background或padding等屬性,可以用merge代替,因為Activity內容布局的parent view就是個FrameLayout,所以可以用merge消除只剩一個,這一點可以從中看到。

  • 某布局作為子布局被其他布局include時,使用merge當作該布局的頂節點,這樣在被引入時頂結點會自動被忽略,而將其子節點全部合并到主布局中。

以第一種情況為例,main.xml布局就可以最佳化如下:

<merge xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <FrameLayout         android:layout_width="match_parent"        android:layout_height="match_parent">        <TextView            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:text="hello world" />        <RelativeLayout             android:layout_width="match_parent"            android:layout_height="match_parent"            android:layout_gravity="center" >            <include layout="@layout/header" />        </RelativeLayout>    </FrameLayout></merge>

以第二種情況為例,header.xml布局可以最佳化如下:

<?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>

這樣就不會有多餘的FrameLayout和RelativeLayout節點了。

ViewStub標籤

viewstub標籤同include標籤一樣可以用來引入一個外部布局,不同的是,viewstub引入的布局預設不會擴張,即既不會佔用顯示也不會佔用位置,從而在解析layout時節省cpu和記憶體。 viewstub常用來引入那些預設不會顯示,只在特殊情況下顯示的布局,如進度布局、網路失敗顯示的重新整理布局、資訊出錯出現的提示布局等。

我們建立一個xml檔案用來顯示一個網路錯誤時提示資訊error.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="wrap_content"    android:layout_height="wrap_content" >   <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:background="@android:color/white"        android:padding="10dip"        android:text="Message"        android:textColor="@android:color/black" /></RelativeLayout>

然後在main.xml裡面加入ViewStub的標籤引入上面的布局:

<merge xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:background="@android:color/darker_gray"    android:layout_height="match_parent" >    ...    <ViewStub        android:id="@+id/error_layout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:layout="@layout/error" /></merge>

在java中通過(ViewStub)findViewById(id)找到ViewStub,通過stub.inflate()展開ViewStub,然後得到子View,如下:

private View errorView; private void showError() {    // not repeated infalte    if (errorView != null) {        errorView.setVisibility(View.VISIBLE);        return;    }     ViewStub stub = (ViewStub)findViewById(R.id.error_layout);    errorView = stub.inflate();} private void showContent() {    if (errorView != null) {        errorView.setVisibility(View.GONE);    }}

在上面showError()中展開了ViewStub,同時我們對errorView進行了儲存,這樣下次不用繼續inflate。

總結

這篇Blog沒有詳細介紹HierarchyViewer工具的使用,相信如果對布局原則比較熟練之後,對工具的依賴大大減少,開發效率也會大大的提升。除這些布局原則之外,還需要大家對Android各個組件的屬性很熟悉,比如如果要做這麼一個布局, 一個圖片和一個文本的布局,新手們往往會用一個Layout嵌套ImageView和TextView來做, 但是當我們知道TextView有drawableLeft, drawableRight等屬性時,那麼實現這樣的一個布局是非常快速高效的。總之,且學且實踐!

Android 布局最佳化

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.