ViewStub的實(shí)現(xiàn)深入解析

布局優(yōu)化是性能優(yōu)化中一項(xiàng)不可缺失的工作,而ViewStub是性能布局優(yōu)化中很有必要的一項(xiàng),使用ViewStub可以把類似空白頁(yè)、錯(cuò)誤頁(yè)等不需要馬上顯示的View實(shí)現(xiàn)懶加載的效果,而且內(nèi)存占有量非常的少,它是一個(gè)寬高為0、不執(zhí)行draw方法且本身設(shè)置了View.GONE所以基本上不參與layout,非常適合用于做懶加載的布局優(yōu)化。

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    // 在構(gòu)造函數(shù)中就設(shè)置成了GONE
        setVisibility(GONE);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(0, 0);
    }

    @Override
    public void draw(Canvas canvas) {
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
    }

是吧,這些常見的方法都是空實(shí)現(xiàn)的且在初始化中就設(shè)置成GONE了、所以這就很好奇是怎么實(shí)現(xiàn)懶加載的。

ViewStub簡(jiǎn)單用法

先看下如何使用ViewStub:

    <ViewStub
        android:id="@+id/id_vs"
        android:layout="@layout/layout_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

ViewStub mViewStub = (ViewStub) findViewById(R.id.id_vs);
        View mRootView = mViewStub.inflate();
        // 接下來(lái)就可以各種的findViewById了...
        mRootView.findViewById(R.id.xxx);

在布局的時(shí)候我們需要給android:layout傳入一個(gè)我們定義好的布局文件,這個(gè)文件只有在調(diào)用了mViewStub.inflate()才會(huì)被顯示出來(lái)。對(duì)于傳入的的布局文件會(huì)賦值給ViewStub的成員變量mLayoutResource,Ok,一切的準(zhǔn)備就緒了。

ViewStub實(shí)現(xiàn)的原理

原理其實(shí)不難,只要看下下面這個(gè)方法就一目了然了。

    public View inflate() {
        final ViewParent viewParent = getParent();
        // 首先要求父控件是ViewGroup才可以
        if (viewParent != null && viewParent instanceof ViewGroup) {
            // 其次要給mLayoutResource賦值,因?yàn)閙LayoutResource就是要懶加載顯示的界面對(duì)應(yīng)的布局
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                final LayoutInflater factory;
                if (mInflater != null) {
                    factory = mInflater;
                } else {
                    factory = LayoutInflater.from(mContext);
                }
                // 這就是重點(diǎn)了,直接調(diào)用常見的LayoutInflater.from().inflate系列方法來(lái)初始化需要懶加載的View
                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);
                }
                // 通過(guò)返回的這個(gè)View  我們就可以拿來(lái)各種findViewById 就能顯示需要顯示的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這個(gè)類很短去掉注釋估計(jì)也就一百行的代碼,原理其實(shí)就是把對(duì)應(yīng)的布局文件當(dāng)做值傳入給mLayoutResource變量,當(dāng)調(diào)用inflate()的時(shí)候調(diào)用LayoutInflater.from(xx).inflate(xx)方法把對(duì)應(yīng)懶加載的View初始化出來(lái),沒錯(cuò)、這樣子之后就能顯示了。

最后有一個(gè)疑問(wèn)

android:visibility="gone"ViewStub 之間的區(qū)別,為什么GONE就沒有ViewStub的功效呢,因?yàn)橥瑯釉诮缑嫔鲜遣伙@示的。

setContentView(R.layout.activity_main);

這一切還得從這個(gè)方法說(shuō)起,都知道這個(gè)方法最后是會(huì)跳轉(zhuǎn)到LayoutInflater 這個(gè)類中去然后同樣執(zhí)行inflate方法。

final XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }

在inflate中有這么一段方法,會(huì)根據(jù)傳入的布局資源然后調(diào)用XmlResourceParser實(shí)現(xiàn)xml文件解析從而得到一個(gè)個(gè)的View,而這時(shí)候如果你把不需要馬上顯示的View設(shè)置成GONE一樣會(huì)被解析一樣會(huì)被加載到內(nèi)存中去,當(dāng)然你放到ViewStub中去那么就只會(huì)加載ViewStub并不會(huì)把相對(duì)應(yīng)的View也加載進(jìn)去,所以從而可以起到懶加載的效果。

所以、ViewStub用起來(lái)..

最后編輯于
?著作權(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)容