ViewStub是一个轻量级View,它是一个看不见的,并且不占布局位置,占用资源非常小的视图对象。可以为ViewStub指定一个布局,加载布局时,只有ViewStub会被初始化,然后当ViewStub被设置为可见时,或者是调用了ViewStub.inflate()时,ViewStub所指向的布局会被加载和实例化,然后ViewStub的布局属性都会传给它指向的布局。这样就可以使用ViewStub来设置是否显示某个布局。
<ViewStub
android:inflatedId="@+id/inflatedId"
android:id="@+id/viewStub"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="@layout/layout_inflate_view_stub"/>
ViewStub
是继承自View
,设置它的可见性,可以通过ViewStub#setVisibility()
和ViewStub#inflate()
。
先看ViewStub#setVisibility()
方法。
private WeakReference<View> mInflatedViewRef;
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=null。因此会先走else逻辑。先通过super.setVisibility()
调用View
的setVisibility
方法,更改相关的标志。然后无论设置的是View.VISIBLE
或者是View.INVISIBLE
都会去调用ViewStub#inflate()
方法。如果mInflatedViewRef!=null
,就是正常的设置可见性。
再看View#inflate()
方法。在xml布局中ViewStub
节点下android:layout
引入的布局会将ViewStub
替换掉,并且ViewStub
的宽高等属性会被该布局使用。
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
if (mLayoutResource != 0) {
final ViewGroup parent = (ViewGroup) viewParent;
/*
inflateViewNoAdd()方法被填充的布局生成View对象。如果为ViewStub设置了属性inflatedId,那么这里的view的id会被替换成该inflatedId。
*/
final View view = inflateViewNoAdd(parent);
//将ViewStub替换成被填充的布局,并使用了ViewStub的属性。
replaceSelfWithView(view, parent);
mInflatedViewRef = new WeakReference<>(view);
if (mInflateListener != null) {
//监听事件需要在inflate()或者setVisibility()之前调用。
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");
}
}
综上,直接调用ViewStub#inflate()
不会改变ViewStub
包裹的View
的可见性。调用ViewStub#setVisibility()
会先改变ViewStub
包裹的View
的可见性,再去调用ViewStub#inflate()
填充View
。
最后说一下id相关。
<ViewStub
android:inflatedId="@+id/inflatedId"
android:id="@+id/viewStub"
... />
这里定义了id和inflatedId。id是通过findViewById来获取ViewStub的。inflatedId则是获取被填充的布局,前提是调用了ViewStub#setVisibility()或者ViewStub#inflate()方法。inflatedId
是在方法inflateViewNoAdd()
中设置给被填充的布局的。
使用ViewStub需要注意的地方。
ViewStub只能加载一次。这个可以从方法
replaceSelfWithView
看出来。因此ViewStub不适合需要按需隐藏的情况。private void replaceSelfWithView(View view, ViewGroup parent) { final int index = parent.indexOfChild(this); //移除ViewStub parent.removeViewInLayout(this); final ViewGroup.LayoutParams layoutParams = getLayoutParams(); //添加被填充的布局 if (layoutParams != null) { parent.addView(view, index, layoutParams); } else { parent.addView(view, index); } }
- ViewStub不能嵌套
merge
标签
一般使用场景。 - 程序运行期间,某个布局在加载后,就不会有变化,除非销毁该页面再重新加载。
- 想要控制显示与隐藏的是一个布局文件,而非某个view。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。