Android:ViewStub

ViewStub是一個輕量級View,它是一個看不見的,而且不佔佈局位置,佔用資源很是小的視圖對象。能夠爲ViewStub指定一個佈局,加載佈局時,只有ViewStub會被初始化,而後當ViewStub被設置爲可見時,或者是調用了ViewStub.inflate()時,ViewStub所指向的佈局會被加載和實例化,而後ViewStub的佈局屬性都會傳給它指向的佈局。這樣就能夠使用ViewStub來設置是否顯示某個佈局。java

<ViewStub
       android:inflatedId="@+id/inflatedId"
       android:id="@+id/viewStub"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout="@layout/layout_inflate_view_stub"/>

ViewStub是繼承自View,設置它的可見性,能夠經過ViewStub#setVisibility()ViewStub#inflate()
先看ViewStub#setVisibility()方法。android

private WeakReference<View> mInflatedViewRef;
 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 {
            super.setVisibility(visibility);
            if (visibility == VISIBLE || visibility == INVISIBLE) {
                inflate();
            }
        }
    }

初次調用該方法,mInflatedViewRef=null。所以會先走else邏輯。先經過super.setVisibility()調用ViewsetVisibility方法,更改相關的標誌。而後不管設置的是View.VISIBLE或者是View.INVISIBLE都會去調用ViewStub#inflate()方法。若是mInflatedViewRef!=null,就是正常的設置可見性。
再看View#inflate()方法。在xml佈局中ViewStub節點下android:layout引入的佈局會將ViewStub替換掉,而且ViewStub的寬高等屬性會被該佈局使用。佈局

public View inflate() {
        final ViewParent viewParent = getParent();

        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                /*
                inflateViewNoAdd()方法被填充的佈局生成View對象。若是爲ViewStub設置了屬性inflatedId,那麼這裏的view的id會被替換成該inflatedId。
                */
                final View view = inflateViewNoAdd(parent);
                //將ViewStub替換成被填充的佈局,並使用了ViewStub的屬性。
                replaceSelfWithView(view, parent);

                mInflatedViewRef = new WeakReference<>(view);
                if (mInflateListener != null) {
                //監聽事件須要在inflate()或者setVisibility()以前調用。
                    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");
        }
    }

最後說一下id相關。this

<ViewStub
       android:inflatedId="@+id/inflatedId"
       android:id="@+id/viewStub"
       ... />

這裏定義了id和inflatedId。id是經過findViewById來獲取ViewStub的。inflatedId則是獲取被填充的佈局,前提是調用了ViewStub#setVisibility()或者ViewStub#inflate()方法。inflatedId是在方法inflateViewNoAdd()中設置給被填充的佈局的。code

使用ViewStub須要注意的地方。xml

  • ViewStub只能加載一次。這個能夠從方法replaceSelfWithView看出來。所以ViewStub不適合須要按需隱藏的狀況。
private void replaceSelfWithView(View view, ViewGroup parent) {
        final int index = parent.indexOfChild(this);
        //移除ViewStub
        parent.removeViewInLayout(this);
        final ViewGroup.LayoutParams layoutParams = getLayoutParams();
        //添加被填充的佈局
        if (layoutParams != null) {
            parent.addView(view, index, layoutParams);
        } else {
            parent.addView(view, index);
        }
    }
  • ViewStub不能嵌套merge標籤

通常使用場景。對象

  • 程序運行期間,某個佈局在加載後,就不會有變化,除非銷燬該頁面再從新加載。
  • 想要控制顯示與隱藏的是一個佈局文件,而非某個view。
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息