这两天项目中遇到了关于viewstub相关的crash问题, 刚好可以把相关的知识复习一下.
ViewStub是View的子类, 是一种轻量级的view, 在android性能优化中常常使用的到.
需要注意的地方
1) viewStub属性
使用该控件时, 主要有两个比较重要的属性:
- android:layoutId:设置ViewStub被inflate的布局控件Id;
- android:layout: 填充进viewStub的布局资源id;
2) viewStub特性
- 作为一种按需"延迟化加载"的控件,viewStub本身是不可见的,类似于"占位符"的性质,在
inflate
后会被指定的布局资源替换,ViewStub控件虽然也占据一定的内存,但是相比较于其他控件, 所占用的内存很小;- 当viewStub实例化之前, 调用
setVisibility()
也会间接调用inflate()
进行布局填充;android:layout
属性中指向的布局加载以后会替换掉viewstub本身,但是并不像Androidinclude
标签一样完全替代,viewStub的引用依然存在,并且可以通过viewstub的可视化操作控制view的显示;
2) 重复inflate
viewstub只能初始化一次, 多次inflate
会报
IllegalArgumentException("ViewStub must have a valid layoutResource");
异常, sourceCode如下:
[图片上传失败...(image-4a814e-1552362786773)]
原因是由于调用了inflate之后viewstub就不在相关的布局中了, getParent()
获取不到父View的值了;当然从代码中可以看出包裹ViewStub的父控件也必须是ViewGroup的实现类才可以辣.
解决方案:引入一个boolean值标识当前viewStub是否已被初始化即可
private boolean isInflate = false;//标记viewStub是否初始化,防止重复inflate,造成crash
mVrLoadingViewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override public void onInflate(ViewStub stub, View inflated) {
isInflate = true;
}
});
VrLoadingView mVrLoadingView;
if(!isInflate){
mVrLoadingView = (VrLoadingView) mVrLoadingViewStub.inflate();
} else {
mVrLoadingView = ((Activity) getContext()).findViewById(R.id.vr_loading_view);
}
ps:因为这里context一定是activity实例,所以不需要进行担心强转失败的问题;
3) 关于setvisibility
viewStub一经实例化以后, 如果需要更改布局的显示状态,可以通过viewstub.setvisibility()
修改, 也可以通过设置的layoutId布局的setvisibility()
方法, 替换布局文件的layout params以viewStub为准,如margin,padding等, 其他布局属性以view自身为准;
4) viewstub的使用场景
一次加载的布局,且单个viewstub层级确实有性能资源方面的优势,但在如RecyclerView中使用viewStub的item view时,页面加载极其缓慢,又或是层层嵌套的viewStub(结构略复杂),打开一个页面可能都要3秒左右;
处于此方面的考虑, 不建议高频模块使用,因为反复调用viewstub的inflate方法非常耗时;这种情况下建议采用include
标签;