美文网首页Android知识
ViewStub源码简解

ViewStub源码简解

作者: dooze | 来源:发表于2016-08-24 00:20 被阅读81次

    ViewStub

    ViewStub是一个未加载时不可见的不会占据实际大小的,且可以用来懒加载布局资源的控件。
    当ViewStub被设置成可见或者是布局资源被inflated,ViewStub就会inflate它自己之中的View或者Views
    到父布局中。因此,ViewStub使用了setVisibility(View.VISIBLE)或者inflate()后才会存在于view的绘制图层中。

    ViewStub中的View根据布局参数inflated后加入到ViewStub的父布局中,同样的,我们还可以定义或者重写inflate view 的id
    通过使用ViewStub中的inflatedId这个属性。
    <pre>
    <ViewStub android:id="@+id/stub"
    android:inflatedId="@+id/subTree"
    android:layout="@layout/mySubTree"
    android:layout_width="120dip"
    android:layout_height="40dip" />
    </pre>

    因此就可以通过id"stub"这个ViewStub了。当"mySubTree"这个布局资源被加载后,这个ViewStub就北从它的父布局中移除了。
    而由ViewStub中指定的布局资源“mySubTree”创建出来的view就可以通过id"subTree"了。
    并且这个最后被加载出来的view就赋予了120dip的宽和40dip高。

    通常地通过一下的方法将ViewStub加载到布局中:
    <pre>
    ViewStub stub = (ViewStub) findViewById(R.id.stub);
    View inflated = stub.inflate();
    </pre>

    当调用ViewStub的inflate()后,该ViewStub就会将其中加载出来的view加载到ViewStub的父布局中,并且返回inflate的到的view.
    这样就可以不通过findViewById()就可以获取一个view的引用了。


    ViewStub 继承自 View 拥有五个构造方法,主要有一下这两个构造方法:

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    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);
    

    从上可知只要是根据AttributeSet获取inflatedId , layout 和 id 这三个属性值。
    关键是在于setVisibility(GONE);setWillNotDraw(true);这样在布局文件中的ViewStub
    就不会在初始化布局的时候加载ViewStub了。

    public ViewStub(Context context, @LayoutRes int layoutResource) {
        this(context, null);
    
        mLayoutResource = layoutResource;
    }
    
    

    这个主要是直接在代码中新建一个ViewStub的时候使用。它就获取了布局资源的id而已。

    还需要注意的一点是: 被加载出来的view是一个弱引用。

    private WeakReference<View> mInflatedViewRef;

    以下是重写了View的setVisibility方法
    public void setVisibility(int visibility) {
       //第一次设置的mInflatedViewRef==null,则调用inflate()去加载view
    
        if (mInflatedViewRef != null) {
        //已经经过加载后直接从mInflatedViewRef中获取该view
            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();
            }
        }
    }
    

    以下便是inflate的方法全部内容,逻辑还是很简单的

    public View inflate() {
        //获取父view
        final ViewParent viewParent = getParent();
        //保证父view必须是一个ViewGroup
        if (viewParent != null && viewParent instanceof ViewGroup) {
           //ViewStub中view的布局id
            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);
    
               //为inflate出来的view设置id
                if (mInflatedId != NO_ID) {
                    view.setId(mInflatedId);
                }
               //找到在父布局中的索引
                final int index = parent.indexOfChild(this);
               //将ViewStub从父布局中中移除
                parent.removeViewInLayout(this);
    
               //根据刚刚得到的布局索引,将inflate得到的view添加到父布局中
                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
                if (layoutParams != null) {
                    parent.addView(view, index, layoutParams);
                } else {
                    parent.addView(view, index);
                }
    
                //更新mInflatedViewRef为新inflate得到的view
                mInflatedViewRef = new WeakReference<View>(view);
    
                //inflate完毕的回调接口
                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中定义的inflated完毕的回调接口,其作用见名就可以知义了。

    public static interface OnInflateListener {
    
        void onInflate(ViewStub stub, View inflated);
    }
    

    相关文章

      网友评论

        本文标题:ViewStub源码简解

        本文链接:https://www.haomeiwen.com/subject/khbnsttx.html