天天看點

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);
               }
          });
     }
}      

小黑:以下是代碼執行後,預設并沒有點選顯示button的情況下。使用“DDMS -> Dump View Hierarchy for UI Automator”工具的截圖

Android 性能優化 三 布局優化ViewStub标簽的使用

小白:我發現activity_main.xml布局檔案在“顯示”,“隐藏”兩個button之間有一個<ViewStub>而在上面的截圖中并沒有。說明ViewStub在初始化載入時并不會加入到視圖樹上(Android UI Tree)。

我也使用“Hierarchy View”工具的截圖例如以下:

Android 性能優化 三 布局優化ViewStub标簽的使用

小黑:對,這是ViewStub能夠進行布局優化的地方“懶載入視圖”,初始化時系統不會初始化ViewStub引用的視圖。

再來看下接着看下點選“顯示”button後的截圖。

Android 性能優化 三 布局優化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顯示有兩種辦法。上面介紹的是使用inflate方法,還能夠直接在ViewStub.setVisibiltity(View.Visible)

異常情況:

<ViewStub
        android:id= "@+id/view_stub_text"
        android:layout_width= "wrap_content"
        android:layout_height="wrap_content"
        >
        <TextView
            android:layout_width= "wrap_content"
            android:layout_height="wrap_content"
            android:text="love_world_"
            />
    </ViewStub >      

出現下面異常:

java.lang.ClassCastException: android.view.ViewStub cannot be cast to android.view.ViewGroup

使用場景:

ListView ItemView

     個人評估是否有必要

     Gone是否也已經優化?