關(guān)于ViewStub的所有

ViewStub - Optimized Layout
  • 作用:用于優(yōu)化布局,懶加載,運(yùn)行時(shí)才會(huì)加載布局。
  • 使用場(chǎng)景:通常用于有些隱藏的或者特殊情況才會(huì)顯示的布局。
    例如:一個(gè)ListView,數(shù)據(jù)為空時(shí),顯示一個(gè)布局告訴用戶

在xml中是使用:

<ViewStub
android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
/>

之前一直都不知道inflatedId有什么用,怎么用,決定看看源碼:

ViewStub的構(gòu)造器
super(context);
final TypedArray a = context.obtainStyledAttributes(attrs,
              R.styleable.ViewStub, defStyleAttr, defStyleRes);
mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
a.recycle();

setVisibility(GONE);
setWillNotDraw(true);

構(gòu)造很簡(jiǎn)單,讀取布局inflatedId, layout, id的屬性;調(diào)用setVisibility(GONE),所以ViewStub默認(rèn)是隱藏的。
還有在實(shí)際使用中,會(huì)發(fā)現(xiàn)ViewStub除了
有inflatedId, layout, id這三個(gè)屬性,其他View的屬性都沒(méi)有效果。
原因是:ViewStub繼承View, 構(gòu)造器只是調(diào)用super(context),而在View中只有Content一個(gè)參數(shù)
的構(gòu)造器不會(huì)讀取其他屬性.

ViewStub的優(yōu)化原理簡(jiǎn)單粗暴, 重寫onMeasure(), 測(cè)量時(shí)設(shè)置寬高為0,重寫draw()不繪制任何東西.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(0, 0);
}

@Override
public void draw(Canvas canvas) {
}

@Override
protected void dispatchDraw(Canvas canvas) {
}

看看inflate方法源碼:

public View inflate() {
        final ViewParent viewParent = getParent();
        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) {
                    view.setId(mInflatedId);
                }

                final int index = parent.indexOfChild(this);
                parent.removeViewInLayout(this);

                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
                if (layoutParams != null) {
                    parent.addView(view, index, layoutParams);
                } else {
                    parent.addView(view, index);
                }

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

分析源碼得知:
首先調(diào)用getParent()獲取父布局parent,利用LayoutInflater把@layout/mySubTree這個(gè)layout加載,(為加載的layout起名為inflatedView),
把mInflatedId設(shè)置給inflatedView, int index = parent.indexOfChild(this)這一句獲取ViewStub在父布局的位置,
使用parent.removeViewInLayout(this)把ViewStub從parent中移除,最后將inflatedView添加到相應(yīng)的位置并替換ViewStub.

使用ViewStub通??梢圆挥弥苯诱{(diào)用inflate(), 要顯示的時(shí)候直接可以調(diào)用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 {
        super.setVisibility(visibility);
        if (visibility == VISIBLE || visibility == INVISIBLE) {
            inflate();
        }
    }
}

mInflatedViewRef是一個(gè)View的弱引用,調(diào)用inflate()后才不為null,
第一次調(diào)用setVisibility(VISIBLE||INVISIBLE)會(huì)調(diào)用inflate(),之后會(huì)從mInflatedViewRef中
獲取inflactedView的弱引用,直接setVisibility(visibility).

還可以設(shè)置OnInflateListener這個(gè)接口進(jìn)行一些inflatedView的初始化工作,
這個(gè)接口只會(huì)被調(diào)用一次。
public static interface OnInflateListener {
void onInflate(ViewStub stub, View inflated);
}

注意事項(xiàng):

  1. inflate()方法只能調(diào)用一次,由于第一次ViewStub已經(jīng)從parent中移除,parent第二次調(diào)用會(huì)為null,
    接著就會(huì)throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent")
  2. ViewStub在xml中id為stub,第一次使用findViewById()可以找到ViewStub,第二次之后就找不到了。
  3. 當(dāng)設(shè)置了android:inflatedId="@+id/subTree",
    首次可以使用inflacte方法獲取inflactedView, View inflactedView = viewStub.inflacte(),
    接著可以findViewById(R.id.subTree)獲取inflactedView.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容