报错信息:
Caused by:
java.lang.IllegalStateException:ViewStub must have a non-null ViewGroup viewParent
android.view.ViewStub.inflate(ViewStub.java:318)
先给结论:viewStub.inflate()或者setVisibility(int)只能调用一次,多次调用了就会报该错误;通过设置OnInflateListener可以记录一个viewstub是否已经inflate过,避免多次调用
分析原因
查看viewStub.inflate方法
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
//省略...
} else {
//报错了情况走到了这里 说明viewparent=null了
throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
}
}
正常的排查思路是看 viewParent是怎么赋值的,然后调查inflate之前viewParent为什么为空;debug复现时很容易发现 ,异常是在第二次重复调用ViewStub. inflate()时出现的;所以我们看下inflate做了哪些事情
final ViewGroup parent = (ViewGroup) viewParent;
final View view = inflateViewNoAdd(parent);//1
replaceSelfWithView(view, parent);//2
mInflatedViewRef = new WeakReference<>(view);
if (mInflateListener != null) {
//设置了OnInflateListener 就可以记录该stub是否已经inflate过
mInflateListener.onInflate(this, view);
}
return view;
标注1处源码如下:可见是将ViewStub中layout中的布局填充后作为view返回,源码如下
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);//这里对应xml中android:inflatedId
}
return view;
}
标记2处replaceSelfWithView()方法名很准确概括了的作用:用填充的view替换viewStub加入到viewStub的父布局中,viewStub的viewParent就是在移除的时候被设置成了null;所以viewStub.inflate()执行一次后,该view已被从父布局中移除,第二次调用就会报异常
private void replaceSelfWithView(View view, ViewGroup parent) {
final int index = parent.indexOfChild(this);
//3这里将viewstub的parentView设置为了null;
parent.removeViewInLayout(this);
final ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (layoutParams != null) {
parent.addView(view, index, layoutParams);
} else {
parent.addView(view, index);
}
}
网友评论