Day5.ViewStub的使用与理解

本篇博客主要解决以下问题:

         1、ViewStub是什么?怎么用?

         2、为什么ViewStub是懒加载的?它和把View设置为GONE有什么区别?

一、ViewStub使用

           先说一下ViewStub的应用场景及使用:

           假如页面上有一个数据显示的View(contentView),还有一个针对异常处理的View(errorView),正常情况下,我们在打开这个界面的时候只有contentView是可见的,errorView隐藏,当出现异常情况时,会把errorView设置为可见,而contentView隐藏。这里就有一个问题了,errorView和contentView都是在一个布局文件里,android的View加载机制告诉我们,LayoutInflater会同时加载这两个View,造成了资源的浪费,能不能有一种办法,使得errorView在一开始的时候不加载,只在需要的时候再加载呢,于是ViewStub横空出世,它可以实现初始时不加载,需要用到某个view或views时才被加载并使用!

           以上面的场景为例,用法如下:

           1、在Activity的布局文件里声明一个ViewStub,其中的layout属性指向errorView的布局文件:

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

                

           2、在Activity中通过StubView的inflate()方法加载我们的errorView,同时对其中的控件进行初始化:

     ViewStub viewStub = findViewById(R.id.view_stub);
     //此时的view相当于被加载布局的根部局
     View view = viewStub.inflate();
     TextView tvInfo = view.findViewById(R.id.tv_error);
     tvInfo.setOnClickListener(this);

            3、加载完成后,就可以跟正常的View一样进行处理了~

        注意:

              1、ViewStub的 inflate()只能使用一次。

              2、ViewStub所要替代的layout文件中不能有<merge>标签。

二、源码解析

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

            可以看到,构造ViewStub的时候设置了Visibility为GONE,同时读取layout属性,将布局的线上服务存到 mLayoutResource中。然后看onMeasure()和onDraw()方法:            

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

    @Override
    public void draw(Canvas canvas) {
    }

           测量的时候将自身设置为大小为0的view,且不绘制。

           它只是一个大小为0,不显示、不绘制的View。

           接着看它的inflate()方法:           

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

        if (viewParent != null && viewParent instanceof ViewGroup) {
            if (mLayoutResource != 0) {
                final ViewGroup parent = (ViewGroup) viewParent;
                final View view = inflateViewNoAdd(parent);
                replaceSelfWithView(view, parent);

                mInflatedViewRef = new WeakReference<>(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");
        }
    }

         查看inflateViewNoAdd()方法:

 private View inflateViewNoAdd(ViewGroup parent) {
        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);
        }
        return view;
    }

           原来,ViewStub是通过 LayoutInflater将指定的布局加载进来,那它是怎么在界面上显示呢:          

private void replaceSelfWithView(View view, ViewGroup parent) {
        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);
        }
    }

            replaceSelfWithView()方法给出了答案:

            它是获取到parentView,将自身从parentView中移除,然后通过addView()将指定的layout动态添加进去的。相当于一开始它只占了个坑,没有内容。有人要用到这个坑时,就把自己的坑让出来~同时也可以明白,它只能inflate()一次,是因为inflate()之后会把自己移除,在获取不到parentView的时候会抛出异常。

三、ViewStub和INVISIBLE、GONE的区别            

    1、invisible

          view设置为invisible时,view在layout布局文件中会占用位置,但是view为不可见,该view还是会创建对象,会被初始化,会占用资源。

    2、gone

           view设置gone时,view在layout布局文件中不占用位置,但是该view还是会创建对象,会被初始化,会占用资源。

    3、viewstub

           viewstub是一个轻量级的view,它不可见,不用占用资源,只有设置viewstub为visible或者调用其inflater()方法时,其对应的布局文件才会被初始化。但是viewstub的引用对象需要是一个布局layout文件,如果要是单个的view的话,viewstub就不能满足要求,就需要调用view的gone或者invisible属性了。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值