Android 效能最佳化 三 布局最佳化ViewStub標籤的使用
小黑與小白的故事,通過虛擬這兩個人物進行一問一答的形式來共同學習ViewStub的使用
小白:Hi,小黑,ViewStub是什嗎?聽說可以用來進行布局最佳化。小黑:ViewStub 是一個隱藏的,不佔用記憶體空間的視圖對象,它可以在運行時消極式載入布局資源檔。(更多詳細的API等資訊可以查看官方文檔ViewStub),電腦行業一向是實踐裡面出真知,下面用一個例子示範下效果。
小黑:說說概念只是為了概括性的瞭解下,還是用個執行個體來示範下。先來建立一個Activity中使用的布局檔案,檔案名稱是:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <Button android:id="@+id/show_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="顯示"/> <ViewStub android:id="@+id/viewstub" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout="@layout/sub_layout" /> <Button android:id="@+id/hide_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="隱藏"/> </LinearLayout>
小白:“顯示”、“隱藏”字串沒有放入values/string.xml,控制項的id也沒有一定的命名規則的亂起。
小黑:。。。。。。“你是猴子請來的救兵嗎?”,一切從簡好吧。注意上面的ViewStub的用法,其中android:layout="@layout/sub_layout"引入一個新的布局,sub_layout.xml代碼如下:
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ViewStub中包含的TextVeiw"/>
小白:現在有兩個布局檔案了,一個是Activity setContentView需要的activity_main.xml,一個是其中引入的sub_layout
小黑:下面是一個MainActivity.java,添加些點擊事件
package com.example.viewstub;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.ViewStub;import android.view.View.OnClickListener;public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ViewStub viewStub = (ViewStub) findViewById(R.id.viewstub); View showButton = findViewById(R.id.show_button); showButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { viewStub.inflate(); } }); View hideButton = findViewById(R.id.hide_button); hideButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { viewStub.setVisibility(View.GONE); } }); }}
小黑:下面是代碼運行後,預設並沒有點擊顯示按鈕的情況下,使用“DDMS -> Dump View Hierarchy for UI Automator”工具的
小白:我發現activity_main.xml布局檔案在“顯示”,“隱藏”兩個按鈕之間有一個<ViewStub>而在上面的中並沒有,說明ViewStub在初始化載入時並不會添加到視圖樹上(Android UI Tree)。我也使用“Hierarchy View”工具的如下:
小黑:對,這是ViewStub可以進行布局最佳化的地方“懶載入視圖”,初始化時系統不會初始化ViewStub引用的視圖。再來看下接著看下點擊“顯示”按鈕後的。
小黑:ViewStub使用流程是 1. 布局中添加ViewStub (XML添加、代碼中添加) 2. inflate顯示 3. setVisibility隱藏。
小白:通過在代碼中viewStub.inflate(); ViewStub引用的布局顯示出來了。不過,這不就是動態添加視圖嗎?與View.setVisibility(View.GONE);有啥區別?
小黑:ViewStub是一個沒有尺寸大小並且不會在布局中嵌套或渲染任何東西的輕量級的視圖。因此在視圖層次展現或隱藏它的代價非常小。當ViewStub可見,或者調用 inflate()函數時,才會載入這個布局資源檔。 該ViewStub在載入視圖時在父容器中替換它本身。因此,ViewStub會一直存在於視圖中,直到調用setVisibility(int) 或者inflate()為止。ViewStub的布局參數會隨著載入的視圖數一同被添加到ViewStub父容器。同樣,你也可以通過使用inflatedId屬性來定義或重新命名要載入的視圖對象的Id值。 View.setVisibility(View.GONE); 方式在初始化時就會添加到視圖樹上(Android UI Tree),而使用ViewStub在初始化時不會添加。
小白:使用ViewStub有啥需要注意的嗎?小黑:1. 在要渲染的布局中並不支援<merge/>標籤。 2. ViewStub.infalte方法不能調用兩次,否者會出現以下異常:
java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent at android.view.ViewStub.inflate(ViewStub.java:287) at com.example.viewstub.MainActivity$1.onClick(MainActivity.java:23) at android.view.View.performClick(View.java:4475) at android.view.View$PerformClick.run(View.java:18786) at android.os.Handler.handleCallback(Handler.java:730) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:5419) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025) at dalvik.system.NativeStart.main(Native Method)
小白:在網上看的資料說是ViewStub的一個缺點是引入的布局不能使單獨的視圖,而必須是一個布局才行,是這樣嗎?小黑:這種觀點是錯誤的,在例子sub_layout.xml 中僅有一個TextView視圖。
小白:為什麼ViewStub可以作為一個布局標籤使用?小黑:你可以查看一下ViewStub的源碼,public final class ViewStub extends View, ViewStub本身就是一個View 子類。
小白:我有幾個問題:1. ViewStub添加的視圖可以動態刪除嗎?2. 不能第二次inflate為什嗎?3. 雖然第二次不能直接inflate,可以直接刪除,代碼中添加視圖嗎?4. 除了調用inflate方法還能通過其他方式顯示出來嗎?(找到id 設定visible)
小黑:偶不知道啊。。。
參考資料ViewStub - 官方文檔Loading Views On Demand - 官方教程Android Layout Tricks #3: Optimize with stubs - 官方部落格Android學習筆記31:使用惰性控制項ViewStub實現布局動態載入效能最佳化之布局最佳化
更多最佳化相關的文章詳見:《Android 基礎學習文章匯總》 第三部分 效能最佳化