Android 控件ViewStub

Android ViewStub

引言:一個(gè)可用于性能優(yōu)化的控件。

時(shí)間:2017年09月21日

作者:JustDo23

Github:https://github.com/JustDo23

官網(wǎng):https://developer.android.com/reference/android/view/ViewStub.html

01. 簡(jiǎn)介

A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime.

控件 ViewStub 是一個(gè)不可見的,零尺寸的 View 它可以在運(yùn)行時(shí)進(jìn)行延遲加載。

When a ViewStub is made visible , or when inflate() is invoked, the layout resource is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views.

當(dāng) ViewStubsetVisibility(int) 方法或者 inflate() 方法被調(diào)用,它會(huì)加載被指定的布局在父布局中將自己替換為加載的布局。

Therefore, the ViewStub exists in the view hierarchy until setVisibility(int) or inflate() is invoked.

替換后,控件 ViewStub 會(huì)從布局樹移除

The inflated View is added to the ViewStub's parent with the ViewStub's layout parameters.

控件 ViewStub布局屬性會(huì)傳遞給被加載的布局。

  • 因此,不是必須顯示的布局使用 ViewStub 代替后減少界面首次加載時(shí)資源消耗,提升最初加載速度。

02. 用法

  1. 布局

    <ViewStub
        android:id="@+id/viewStub"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:inflatedId="@+id/titleBar"
        android:layout="@layout/just_title" />
    
    • android:layout 指定被加載替換的布局
    • android:inflatedId 指定被加載替換的布局的 id
  2. 加載

    viewStub = (ViewStub) findViewById(R.id.viewStub);
    View inflated = stub.inflate();
    
    • 官方推薦加載首選方法
    • 調(diào)用 inflate() 方法后布局被加載替換同時(shí)返回布局對(duì)象。避免了使用 findViewById() 方法。
    • inflate() 方法只能調(diào)用一次,調(diào)用被移除而沒(méi)有了父布局。第二次調(diào)用會(huì)拋出異常 ViewStub must have a non-null ViewGroup viewParent

03. 示例

  1. 主界面布局

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
    
      <ViewStub
        android:id="@+id/viewStub"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:inflatedId="@+id/titleBar"
        android:layout="@layout/just_title" />
    
      <Button
        android:id="@+id/bt_show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"
        android:text="Show"
        android:textAllCaps="false" />
    
      <Button
        android:id="@+id/bt_hide"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/bt_show"
        android:layout_marginTop="10dp"
        android:text="Hide"
        android:textAllCaps="false" />
    
    </RelativeLayout>
    
  2. 被替換布局 just_title.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="50dp"
      android:background="@color/colorPrimary">
    
      <TextView
        android:id="@+id/tv_show_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:ellipsize="end"
        android:singleLine="true"
        android:textColor="@android:color/white"
        android:textSize="20sp"
        tools:text="Title" />
    
    </RelativeLayout>
    
  3. 界面加載

    public class ViewStubActivity extends AppCompatActivity implements View.OnClickListener {
    
      private ViewStub viewStub;// 占位控件
      private Button bt_show;// 顯示按鈕
      private Button bt_hide;// 隱藏按鈕
      private TextView tv_show_title;// 標(biāo)題
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_stub);
        viewStub = (ViewStub) findViewById(R.id.viewStub);// 尋找控件
        bt_show = (Button) findViewById(R.id.bt_show);
        bt_hide = (Button) findViewById(R.id.bt_hide);
        bt_show.setOnClickListener(this);
        bt_hide.setOnClickListener(this);
      }
    
      @Override
      public void onClick(View v) {
        switch (v.getId()) {
          case R.id.bt_show:// 顯示
            try {
              View titleBar = viewStub.inflate();// 第二次加載會(huì)拋出異常
              tv_show_title = (TextView) titleBar.findViewById(R.id.tv_show_title);
              tv_show_title.setText("Title");
            } catch (Exception e) {
              viewStub.setVisibility(View.VISIBLE);
            }
            break;
          case R.id.bt_hide:// 隱藏
            viewStub.setVisibility(View.GONE);
            break;
        }
      }
    
    }
    
  4. 隱藏

    ViewStubHide
  5. 顯示

    ViewStubShow
  6. 更換加載方式

    @Override
    public void onClick(View v) {
      switch (v.getId()) {
        case R.id.bt_show:// 顯示
          viewStub.setVisibility(View.VISIBLE);// 方式二
          if (tv_show_title == null) {
            tv_show_title = (TextView) findViewById(R.id.tv_show_title);
            tv_show_title.setText("Title");
          }
          break;
      }
    }
    

04. 監(jiān)聽

  1. 加載監(jiān)聽回調(diào)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_view_stub);
      viewStub = (ViewStub) findViewById(R.id.viewStub);// 尋找控件
      // 設(shè)置加載監(jiān)聽回調(diào),成功加載后回調(diào)[只會(huì)回調(diào)一次]
      viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
    
        /**
         * Listener used to receive a notification after a ViewStub has successfully inflated its layout resource.
         *
         * @param stub ViewStub 對(duì)象
         * @param inflated 被加載填充的布局
         */
        @Override
        public void onInflate(ViewStub stub, View inflated) {
          tv_show_title = (TextView) inflated.findViewById(R.id.tv_show_title);
          tv_show_title.setText("ShowTitle");
        }
      });
    }
    
  2. 按需使用

05. 源碼

  1. setVisibility() 方法

    public void setVisibility(int visibility) {
      if (mInflatedViewRef != null) {// 第二次就不空
        View view = mInflatedViewRef.get();
        if (view != null) {
          view.setVisibility(visibility);// 不空直接顯示
        } else {
          throw new IllegalStateException("setVisibility called on un-referenced view");
        }
      } else {// 第一次會(huì)為空
        super.setVisibility(visibility);
        if (visibility == VISIBLE || visibility == INVISIBLE) {
          inflate();// 調(diào)用填充方法
        }
      }
    }
    
  2. inflate() 方法

    public View inflate() {
      final ViewParent viewParent = getParent();// 獲取父布局。第一次加載后從視圖樹中移除。第二次便沒(méi)有了父布局。
    
      if (viewParent != null && viewParent instanceof ViewGroup) {// 父布局不空。第二次為空。
        if (mLayoutResource != 0) {// 被指定的布局
          final ViewGroup parent = (ViewGroup) viewParent;
          final LayoutInflater factory;// 布局填充器
          if (mInflater != null) {
            factory = mInflater;
          } else {
            factory = LayoutInflater.from(mContext);// 獲取布局填充器
          }
          final View view = factory.inflate(mLayoutResource, parent,
              false);// 加載布局
    
          if (mInflatedId != NO_ID) {// 被指定的布局的 ID
            view.setId(mInflatedId);//  即 android:inflatedId 指定的新 ID
          }
    
          final int index = parent.indexOfChild(this);// 獲取位置
          parent.removeViewInLayout(this);// 移除 ViewStub 自己
    
          final ViewGroup.LayoutParams layoutParams = getLayoutParams();// ViewStub 指定的布局參數(shù)
          if (layoutParams != null) {
            parent.addView(view, index, layoutParams);// 指定位置和參數(shù)填充
          } else {
            parent.addView(view, index);
          }
    
          mInflatedViewRef = new WeakReference<View>(view);// 弱引用
    
          if (mInflateListener != null) {// 監(jiān)聽器不空
            mInflateListener.onInflate(this, view);// 監(jiān)聽回調(diào)。所以只回調(diào)一次。
          }
    
          return view;// 加載的布局返回
        } else {// 沒(méi)有指定填充的布局就拋出異常
          throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
        }
      } else {// 父布局為空就拋出異常
        throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
      }
    }
    

06. 注意

  1. ViewStub 支持使用 <include> 標(biāo)簽的布局。
  2. ViewStub 不支持使用 <merge> 標(biāo)簽的布局。

07. 參考

  1. Android--UI之ViewStub
  2. Android中使用ViewStub提高布局性能
  3. Android ViewStub的使用
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容