一、什么是ViewStub
A ViewStub is an invisible, zero-sized View that can be used to lazily inflate layout resources at runtime.
这是官方文档的定义,ViewStub是一个不可见的,大小为0的View,这个View能够用来在运行时懒加载布局资源。这里的懒加载的意思,就是在需要使用的时候才加载布局资源文件,这样的好处就是能够减少内存资源的使用,因为布局资源里的View对象(们)不会刚开始就创建,只有需要的时候才会创建。
二、ViewStub的简单使用
在这里我们简单定义两个布局文件:
(1)activity_view_stub,这个资源文件在setContentView中使用。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Show"
android:gravity="center"/>
<Button
android:id="@+id/btn_hide"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hide"
android:gravity="center"/>
<ViewStub
android:id="@+id/view_stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/view_stub_example"/>
</LinearLayout>
(2)view_stub_example,在ViewStub通过android:layout这个属性来应用该资源文件,具体可以看上面的布局文件。
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="@+id/tv_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="this is the text in layout resource"
android:gravity="center"/>
(3)下面就是一个简单的activity,通过show和hide按钮来操作ViewStub。
public class ViewStubActivity extends Activity implements View.OnClickListener{
private ViewStub mViewStub;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_stub);
mViewStub = (ViewStub) findViewById(R.id.view_stub);
findViewById(R.id.btn_show).setOnClickListener(this);
findViewById(R.id.btn_hide).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_show:
show();
break;
case R.id.btn_hide:
hide();
break;
default:
break;
}
}
private void show() {
mViewStub.inflate();
}
private void hide(){
mViewStub.setVisibility(View.GONE);
}
}
(4)点击show按钮前,通过dump view查看view的结构为
通过这个图可以看出ViewStub并不显示。
(5)点击show按钮后,通过dump view来查看view的结构为
通过这个图,可以看出,点击show之后,定义在view_stub_example.xml资源文件中的TextView已经显示出来。
(6)点击hide按钮后,通过dump view查看view的结构为
通过这个图可以看出,TextView又隐藏起来了,看不见了。
(7)再次点击show按钮之后,发现activity崩溃了,为什么???,查看异常发现报java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent,通过这个异常去查看ViewStub的代码,发现
public View inflate() {
final ViewParent viewParent = getParent();
if (viewParent != null && viewParent instanceof ViewGroup) {
...
} else {
throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
}
}
那么说明ViewStub这个时候的调用getParent()拿到的是null,为什么会这样啊???
这是因为ViewStub通过inflate()懒加载了定义在属性android:layout的布局文件后,ViewStub会从布局文件中删除,所以它的getParent()会拿到null。实际上在inflate()之后再通过findViewById去找ViewStub,此时也找不到ViewStub(为空)。
在这里我们可以验证一下
修改前
private void show() {
mViewStub.inflate();
}
修改后为
private void show() {
mViewStub.setVisibility(View.VISIBLE);
System.out.println(“ViewStub = ” + findViewById(R.id.view_stub));
}
查看log会发现打印 “System.out: ViewStub = null”,说明这个时候的确找不到这个ViewStub了,已经被删除了。
三、setVisibility()和inflate()的异同
相同:通过setVisibitlity()和inflate()都可以懒加载定义在android:layout中的布局文件,实际上setVisibility也是调用inflate()函数来加载布局文件的。
不同:如果通过setVisibility(),那么加载之后就不需要重新调用inflate()函数,直接设置View的Visibility属性即可。注意这里的措辞,是View的Visibility属性,而不是ViewStub的Visibility属性。在ViewStub调用inflate()之前,那么设置的是ViewStub的Visibility属性,调用inflate()函数之后,这个时候设置的是android:layout这个属性指定的布局文件加载后的根View(伪代码中的root)的Visibility属性,使用伪代码如下:
View root = factory.inflate(mLayoutResource, parent,false);
通过这个不同点,我们可以简单修改上述的代码,可以达到点击show和hide来展示和隐藏android:layout中定义的布局资源的目的
修改前
mViewStub.inflate();
System.out.println("ViewStub = " + findViewById(R.id.view_stub));
修改后
mViewStub.setVisibility(View.VISIBLE);
System.out.println("ViewStub = " + findViewById(R.id.view_stub));
这样随便点击显示和隐藏就不会造成崩溃,可以达到展示和隐藏的功能。
四、获取定义在android:layout布局文件中的View
这个实际上就跟include类似,在inflate()之后,就可以直接通过findViewById直接获取了。
private void show() {
mViewStub.setVisibility(View.VISIBLE);
System.out.println("ViewStub = " + findViewById(R.id.view_stub));
TextView content = (TextView) findViewById(R.id.tv_content);
content.setText("This the content after changed");
}
五、总结
ViewStub是一个size为0的View,并且是一个不可见的View。使用ViewStub可以达到懒加载资源文件的目的,从而减少资源使用。在ViewStub使用inflate()函数之后,ViewStub会从布局文件中删除,此时会将定义在android:layout中的资源文件加载后的根view替换ViewStub。
六、典型的应用场景
(1)网络请求的失败展示信息
(2)导航页和欢迎页的切换