ViewStub简介
public final class ViewStub extends View
ViewStub 是一个宽高都为0,不可见的(GONE),不参与measure与layout(绝大部分情况),不绘制任何东西,可以用来做懒加载的View,常用于布局优化;
PS: 为什么说绝大部分情况不参与测量与布局呢?因为大部分ViewGroup对于GONE的View,都不会让它参与测量与布局流程(自定义的就不一定了,另外可以看一下FrameLayout的源码)。
首先需要说的是,本文涉及到两个角色,一个是 ViewStub本身,另外一个是被用来做懒加载的View,是ViewStub的作用对象,称之为『StubbedView』(本文用此称呼来替代)。
那么 ViewStub 是怎么实现懒加载的呢?
本文通过ViewStub源码来分析。
ViewStub的简单使用教程
ViewStub 的使用非常非常简单,只需要两步~
Step 1. 在XML里配置使用:
1 | <ViewStub |
Step 2. 调用ViewStub的inflate
1 | ViewStub stub = (ViewStub)findViewById(R.id.stub); |
非常简单的两步,就能做到View的懒加载,非常方便,其原因是什么呢?
接下去深入源码分析一下。
构造方法分析
首先分析一下构造方法,了解一下它是如何创建的。
1 | public ViewStub(Context context, @LayoutRes int layoutResource) { |
ViewStub在构造方法里不仅仅获取赋值属性,比较关键的是,还 默认将ViewStub自己设置为不可见(跳过onMeasure与onLayout),不绘制。
这里有一个要点:在XML里配置ViewStub的可见性是没有用的。
测量 与 绘制
1 | @Override |
inflate()方法分析
之前在简单教程里有提到 inflate方法,它是ViewStub实现懒加载的最为关键的方法,接下去去分析一下。
1 | // 返回 StubbedView |
我在每行代码上都加上了详细的注释,主要的操作就是把StubbedView给Inflate出来,然后把它放到自己的位置,代码非常清晰,非常简单。
总结来说,其实inflate方法是做了一个『偷梁换柱』的操作,把 StubbedView动态的添加到自己原来的位置上,也因此实现了懒加载功能。
这里还需要注意的是 ViewStub 必须要有一个 Parent,即必须要有父视图!(谢谢 JangGwa 的提醒)
另外值得一提的是:ViewStub还重写了View的setVisibility方法,让我们来分析一下:
1 | public void setVisibility(int visibility) { |
可以看到setVisibility方法中也可能会调用inflate()方法,所以当我们想让StubbedView被加载进来,而我们不需要StubbedView的实例的时候,可以用setVisibility(View.VISIBLE)。
不过需要注意的是 不要再接着调用inflate方法,因为此时的 ViewStub 已经被移除了!
要点
- 使用ViewStub,必须指定layoutResourceId(必须是布局文件)
- 在XML里配置ViewStub的可见性是没有用的
- ViewStub 主要原理藏在
inflate()方法中,是它把真正要加载的View给加载了进来 inflate()方法只能调用一次- ViewStub调用
inflate()后就不要再用它了(让它功成身退!) - 要小心
setVisibility方法,因为它可能会调用inflate() - 在XML里给ViewStub设置的LayoutParamas(宽高margin等)会传递给StubbedView,所以我们如果要控制StubbedView的LayoutParamas,则需要写在ViewStub里而不是StubbedView!
- 期待补充!
小结
源码分析完毕,可以看到,ViewStub的源码还是非常简单的。
总结来说,它为需要被懒加载的View在布局中占了一个坑,当需要加载时把自己占的坑让给了被加载的 View ,从而实现了懒加载。
推荐阅读
一步一步深入理解CoordinatorLayout
LayoutInflater源码分析(一)之inflate深度分析
LayoutInflater源码分析(二)之include以及merge标签的处理