最近工作中遇到需要使用延迟加载的功能,,就想到了ViewStub,也遇到了一些使用上ViewStub的小问题,遂对ViewStub进行了源码分析了解,其实也就是延迟了调用了inflate,addView而已.
ViewStub是一个不可见,大小为0的视图,只有给它设置成View.Visible或者调用它的inflate()之后才会填充布局资源,所以在不需要这个View的时候占用资源少,当然我们也可以不使用ViewStub来实现某个View的显示或者隐藏,直接动态设置View或者GONE,但是即使你设置GONE,这个布局仍然会被加载,也就会消耗系统资源.
为什么ViewStub是一个不可见,大小为0的,看源码:
初始化View的时候
private void initialize(Context context) {
mContext = context;
setVisibility(GONE);
setWillNotDraw(true);
}
复制代码
在初始化的时候将ViewStub设置成了GONE,所以它是被隐藏的
setWillNotDraw则是不会绘制这个View,也就是ViewStub不会被调用onDraw
我为什么要用ViewStub?
原因1. 在一个页面初始化一个WebView组件的时候监测到上千个初始化crash,而这个WebView并不是一打开此页面就需要的,而是在某些场景下才需要使用到,而且WebView组件并不是我们这边维护的,且这个crash他们也无法马上处理,所以想到个降低crash概率的办法,到了需要加载的时候才加载WebView布局,而不是每次onCreate都初始化一次。
下面是使用示例:
布局detail_activity.xml:
<..............省略
android:layout_width = "match_parent"
android:layout_height="wrap_parent"
android:layout="@layout/webview_stub"//需要延迟加载布局
/>
布局 webview_stub.xml:
android:id = "+id/detail_webview"
android:layout_width="match_parent"
android:layout_height="80dp" />
使用的时候:
ViewStub vs = (ViewStub)findViewById(R.id.webview_stub);
View view = vs.inflate();
WebView webView = (WebView)view.findViewById(R.id.detail_webview);
就可以在这个时候使用WebView了。
```
原因2:当然就是提高这个Activity布局的加载速度了,初始化Activity布局的时候更简洁的布局意味着更快的加载布局速度.
关键的加载阶段:
不管你是调用ViewStub的setVisibility或者inflate函数,都会加载这个布局,其实如果布局还没加载,调用setVisibility()最后也是调用到了inflate(),来看源码
public void setVisibility(int visibility) {
if (mInflatedViewRef != null) {
View view = mInflatedViewRef.get();
if (view != null) {
view.setVisibility(visibility);
} else {
throw new IllegalStateException("setVisibility called on un-referenced view");
}
} else {//如果还没有被加载,不管是VISIBLE还是INVISIBLE都会调用inflate
super.setVisibility(visibility);
if (visibility == VISIBLE || visibility == INVISIBLE) {
inflate();
}
}
}复制代码
下面就来看inflate()
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {//父布局必须钥匙ViewGroup类型
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);
if (mInflatedId != NO_ID) {
view.setId(mInflatedId);
}
final int index = parent.indexOfChild(this);
parent.removeViewInLayout(this);//把自己从布局中移除
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (layoutParams != null) {//如果给ViewStub设置了布局参数则使用
parent.addView(view, index, layoutParams);
} else {
parent.addView(view, index);
}
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");
}
}复制代码
从函数源码可以总结几点写代码时需要注意的:
1. ViewStub必须要被一个布局容器包裹
2.ViewStub调用parent.removeViewInLayout(this);把自己给移除调了,所以加载后它是不存在的,这里印证了第一点1的,如果父布局不是ViewGroup类型,就无法调用removeViewInLayout()把自己移除。
3.如果ViewStub设置了LayoutParams,则会覆盖掉layout引用的布局的LayoutParams,我是在这里出了一个问题,由于我的ViewStub引用的是一个WebView,正好在加载url 404的时候发现WebView页面变得很大,于是查看源码,没想到真是这个问题,也是把ViewStub设置成80dp则解决了在404的情况下WebView过大的问题