ViewStub源碼分析

Viewtub源碼分析

A ViewStub is an invisible, zero-sized View that can be used to lazily inflate

ViewStub是一個不可見的0大小的視圖,可以在運行時懶加載資源

When a ViewStub is made visible, or when is invoked, the layout resource is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views.Therefore, the ViewStub exists in the view hierarchy until or is invoked.

當ViewStub可視化或調用時,布局資源會被加載。ViewStub加載view到父視圖中。因此ViewStub存在于視圖層次結構中,直到或被調用。

    <ViewStub android:id="@+id/stub"
    android:inflatedId="@+id/subTree"
    android:layout="@layout/mySubTree"
    android:layout_width="120dip"
    android:layout_height="40dip"/>

ViewStub構造方法

 public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context);
    
    final TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.ViewStub, defStyleAttr, defStyleRes);
    //要被加載的布局id
    mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    //要被加載的布局
    mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    // ViewStub 的 Id
    mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    a.recycle();

    setVisibility(GONE);
    setWillNotDraw(true);
}

核心:復寫setVisibility()方法

 public void setVisibility(int visibility) {
    // mInflatedViewRef 是對布局的弱引用
    if (mInflatedViewRef != null) {
        // 如果不為 null,就拿到懶加載的 View
        View view = mInflatedViewRef.get();
        if (view != null) {
            // 然后就直接對 View 進行 setVisibility 操作
            view.setVisibility(visibility);
        } else {
            throw new IllegalStateException("setVisibility called on un-referenced view");
        }
    } else {
        super.setVisibility(visibility);
        // 之前說過,setVisibility(int) 也可以進行加載布局
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            // 因為在這里調用了 inflate()
            inflate();
        }
    }
}

inflate()關鍵加載方法

  • 獲取父視圖,如果沒有指定布局,就會拋出異常

  • 獲取加載的布局view,并設置view的布局id

  • 計算出 ViewStub 在 parent 中的位置,先將Viewstub移除,并將view加載到布局中

  • 將View進行弱引用

     public View inflate() {
         // 獲取父視圖
         final ViewParent viewParent = getParent();
         // 如果沒有指定布局,就會拋出異常
         if (viewParent != null && viewParent instanceof ViewGroup) {
             if (mLayoutResource != 0) {
                 // viewParent 需為 ViewGroup
                 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);
                 // 為 view 設置 Id
                 if (mInflatedId != NO_ID) {
                     view.setId(mInflatedId);
                 }
                 // 計算出 ViewStub 在 parent 中的位置
                 final int index = parent.indexOfChild(this);
                 // 把 ViewStub 從 parent 中移除
                 parent.removeViewInLayout(this);
                 // 接下來就是把 view 加到 parent 的 index 位置中
                 final ViewGroup.LayoutParams layoutParams = getLayoutParams();
                 if (layoutParams != null) {
                     // 如果 ViewStub 的 layoutParams 不為空
                     // 就設置給 view
                     parent.addView(view, index, layoutParams);
                 } else {
                     parent.addView(view, index);
                 }
                 // mInflatedViewRef 就是在這里對 view 進行了弱引用
                 mInflatedViewRef = new WeakReference<View>(view);
                 // 回調
                 if (mInflateListener != null) {
                     mInflateListener.onInflate(this, view);
                 }
    
                 return view;
             } else {
                 throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
             }
         } else {
             throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
         }
     }
    

ViewStub相關使用

1、特點

  • ViewStub只能Inflate一次,之后ViewStub對象會被置為空。按句話說,某個被ViewStub指定的布局被Inflate后,就不會夠再通過ViewStub來控制它了
  • ViewStub layout布局不能使用merge引入

在xml使用ViewStub,使用layout引入布局文件,inflatedId是引入布局文件之后的id

<ViewStub
    android:id="@+id/stub_import"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"
    android:inflatedId="@+id/panel_import"
    android:layout="@layout/progress_overlay" />

通過java代碼中的一系列判斷可得出相關ViewStub特點

 @Override
public void onClick(View v) {

            //ViewStub只能Inflate一次,之后ViewStub對象會被置為空。按句話說,某個被ViewStub指定的布局被Inflate后,就不會夠再通過ViewStub來控制它了
            if (mStubImport.getParent()!=null) {
                View view = mStubImport.inflate();
                if (view != null) {
                    if (view.getId() == R.id.panel_import) {
                        Log.e(TAG, "layout root id is act_layout_viewstub_new");
                    } else if (view.getId() == R.id.layout_viewstub_old) {
                        Log.e(TAG, "layout root id is layout_viewstub_old");
                    } else {
                        Log.e(TAG, "layout root id is anyone : " + view.getId());
                    }
                    // layoutView的root view布局 和mViewStub的布局保持一致
                    int width = view.getLayoutParams().width;
                    if (width == ViewGroup.LayoutParams.MATCH_PARENT) {
                        Log.e(TAG, "layout width is MATCH_PARENT");
                    } else if (width == ViewGroup.LayoutParams.WRAP_CONTENT) {
                        Log.e(TAG, "layout width is WRAP_CONTENT");
                    } else {
                        Log.e(TAG, "layout width is anyone : " + width);
                    }

                }
            } else {
                Log.e(TAG, "viewStub is inflated");
            }

}

github地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容