一、什么是ViewStub?
官方介绍:https://developer.android.com/reference/android/view/ViewStub.html
ViewStub是一个不可见的,大小为0的视图,可以在运行过程中延时加载布局资源。当ViewStub被设置成可见,或者它的inflate() 方法被调用的时候,布局资源才会被填充,然后ViewStub本身就会被填充起来的布局资源替换掉。也就是说 ViewStub 被设置成可见或者它的inflate() 方法被调用之后,在视图树中就不存在了。被填充的布局在替换ViewStub的时候会使用ViewStub的布局参数(LayoutParameters),比如 width ,height等。此外,你也可以通过ViewStub的inflateId 属性定义或者重写 被填充布局资源的id。
当ViewStub 的inflate() 方法被调用之后,ViewStub就会被填充起来的布局替换掉,并返回填充起来的View。这样,当我们想使用被填充起来的View时就不再需要调用findViewById () 方法。(实际使用的时候,如果我们需要操作被填充布局里面的数据时用inflate(),否则可以直接使用setVisibility)。
摘自简书
二、你真的会用ViewStub吗?
1、操作ViewStub填充布局内的View
// 键盘提示
ViewStub keyboardHintViewStud = mView.findViewById(R.id.keyboard_hint_layout);
View inflateView = keyboardHintViewStud.inflate();
ImageButton ibtnCloseKeyboardHint = inflateView.findViewById(R.id.iv_call_hint_close);
ibtnCloseKeyboardHint.setOnClickListener(this);
2、简单的控制整个填充布局显/隐
// 没有记录
ViewStub noRecordsLayoutViewStub = mView.findViewById(R.id.no_records_layout);
noRecordsLayoutViewStub.setVisibility(View.VISIBLE);
noRecordsLayoutViewStub.setVisibility(View.GONE);
3、以上2者结合使用
三、源码解析ViewStub
通过这一篇的解析你会发现哇,原来ViewStub没什么神奇,你也可以做到。
带着问题查源码。
1、为什么ViewStub是一个不可见的,大小为0的视图
我们知道ViewStub继承自View,那么也跟普通View一样通过构造方法形成对象,也要走View的绘制流程。
图3.1.a不可见的“罪魁祸首”来了
图3.1.b虽然ViewStub依然在视图树中占有一席之地,但是这两行代码让ViewStub隐藏且不绘制任何内容,也就不消耗资源了。
大小为0的“罪魁祸首”
在ViewStub类的onMeasure方法中setMeasuredDimension(0, 0)这行代码,不管ViewStub宽高设置何种值都被置为0。它的大小自然为0了。
2、为什么inflate() 方法只能被调用一次,第二次调用会报错?
图3.2.a是inflate方法源码,要想填充布局有几个条件:
1、此ViewStub的上层容器必须存在且必须是ViewGroup(viewParent !=null && viewParentinstanceof ViewGroup)。
2、要有布局资源ID。
满足上列条件后
1、调用final View view1 = inflateViewNoAdd(parent);得到一个未加到视图树的view1。
2、调用replaceSelfWithView(view, parent);这里很关键,作用是将ViewStub本身从视图树中移除,同时将刚刚得到的view1
加入原ViewStub的节点,同时让ViewStub持有view1的弱引用。
第二次调用inflate() 方法会报错的原因是,第一次调用已经将ViewStub本身从视图树中移除,不满足viewParent !=null 的条件报错.
3、为什么ViewStub调用setVisibility()也能填充视图且能多次调用?
图3.3.a第一次调用setVisibility()的时候,mInflatedViewRef并没有初始化,那么这时候就会走inflate(),在inflate() 方法中给被填充起来的布局view创建一个WeakReference弱引用,并赋值给mInflatedViewRef,从而完成mInflatedViewRef的初始化(见第二问)。当第二次走setVisibility() 的时候,mInflatedViewRef已经不再是null,就会调用 WeakReference 的父类Reference中的get() 方法获取该引用指向的实体对象,也就是说通过get() 拿到被填充的view对象,然后再走View类的setVisibility()。
网友评论