Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android布局優化

Android布局優化

日期:2017/2/23 16:07:29      編輯:關於Android編程

前言

本篇文章為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 的根節點。例如:

    1 2 3 4 5 6 7 8 9 10 "http://schemas.android.com/apk/res/android"> android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/add"/> android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/delete"/>

    現在,當你要將這個 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 是一個半透明的進度條覆蓋層。功能上講,它應該只在新的數據項被導入到應用程序時可見。

    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
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved