ViewStub就是一个轻量级的View,它在布局文件中以<ViewStub>标签的形式存在,在acitivity加载布局的时候并不会实例化这个View,而是当在代码中调用ViewStub的inflate()方法的时候才会实例化这个View。
定义一个ViewStub
ViewStub是一个轻量级的View,它没有大小,也不会执行任何的绘制。所以inflate它和把它放在view树中是非常划算的。每一个ViewStub都需要制定android:layout属性来制定它要inflate的布局文件。
例如:
<ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/panel_import" android:layout="@layout/progress_overlay" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" />
加载ViewStub的布局:
当你想加载ViewStub的布局时,可以通过调用setVisibility(View.VISIBLE)或者调用inflate()方法。
代码如下:
((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);// orView importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
注意:inflate()方法返回了inflated的view。所以你不用再使用findViewById()方法。
当设置了可见性或者inflated之后,ViewStub元素就不再存在于view树中了。它被inflated的布局所替代了,而且inflated的根布局的id属性就是ViewStub所指定的android:inflatedId属性值。
这里值得注意的是:
在调用ViewStub的setVisibility()方法改变它的可见性的时候也会inflated这个ViewStub所指定的布局文件。但是如果在调用了一次这个方法之后再调用这个方法就不会去inflate了。我们看一下ViewStub中setVisibility()方法的源码:
@Override
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 {
super.setVisibility(visibility);
if (visibility == VISIBLE || visibility == INVISIBLE) {
inflate();
}
}
}劵
其中mInflatedViewRef是一个若引用,它是inflated出来的view的一个若引用,上面的代码逻辑可以看出,当我们第一次调用setVisibility()方法时,它先判断需要inflate的view的引用是否为空,如果不为空,就拿到这个view并且设置它的可见性,如果需要inflate的view为空的话,不管调用的setVisibility()方法中传入的是可见还是不可见,它都会执行inflate()方法。
再来看看inflate()这个方法的源码:
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
if (mLayoutResource != 0) {
final ViewGroup parent = (ViewGroup) viewParent;
final LayoutInflater 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) {
parent.addView(view, index, layoutParams);
} else {
parent.addView(view, index);
}
mInflatedViewRef = new WeakReference<View>(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的父控件,一般是一个ViewGroup对象。如果viewStub不是在一个ViewGroup中的话,会报出异常。
2. 然后使用LayoutInflater的inflate方法来加载布局文件,获得viewStub指定的布局所对应的view。
3. parent.removeViewInLayout(this);删除当前viewStub
4. 获得当前viewStub的布局参数,并把inflate出的view添加到parent中。
5. 将inflate出的view用若引用保存起来。