ViewStub是一个不可见的,大小为0的View,最佳的用途就是实现View的延迟加载,在需要的时候再加载View。当调用ViewStub的setVisibility方法设置为可见或者调用inflate()方法初始化该View的时候,ViewStub引用的资源开始初始化,然后引用的资源会替代掉ViewStub,把自己填充在ViewStub的原位置。在需要的时候加载View,这样保证了资源的充分利用,并且由于引用的资源会替代掉ViewStub,导致重新加载时会报出异常,后边详细讲解。
首先来说说ViewStub的一些特点:
1. ViewStub只能Inflate一次,之后ViewStub对象会被置为空。按句话说,某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了。
2. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中。
基于以上的特点,那么可以考虑使用ViewStub的情况有:
1. 在程序的运行期间,某个布局需要加载时,在Inflate后,就不会有变化,除非重新启动。
因为ViewStub只能Inflate一次,之后会被置空,所以无法指望后面接着使用ViewStub来控制布局。所以当需要在运行时不止一次的显示和隐藏某个布局,那么ViewStub是做不到的。这时就只能使用View的可见性来控制了。
2. 想要控制显示与隐藏的是一个布局文件,而非某个View。
因为设置给ViewStub的只能是某个布局文件的Id,所以无法让它来控制某个View。
所以,如果想要控制某个View(如Button或TextView)的显示与隐藏,或者想要在运行时不断的显示与隐藏某个布局或View,只能使用View的可见性来控制。这个地方需要区分开来!
下面上一个简单的例子:
main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.hacktwo_prefernece.MainActivity" >
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ViewStub测试"
android:background="@android:color/background_light"
android:textColor="@android:color/holo_green_light"
/>
<Button
android:id="@+id/bt_get"
android:layout_below="@id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="加载"
android:textSize="20dp"
android:gravity="center"
/>
<Button
android:id="@+id/bt_down"
android:layout_below="@id/bt_get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="移除"
android:textSize="20dp"
android:gravity="center"
/>
<ViewStub
android:id="@+id/view_content"
android:layout_width="match_parent"
android:layout_height="200dip"
android:layout="@layout/tv_content"
android:inflatedId="@+id/view_get"
android:layout_below="@id/bt_down"
/>
</RelativeLayout>
需要加载的布局是tv_content.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是内容!"
android:textSize="50sp">
</TextView>
MainActivity.java:
public class MainActivity extends Activity implements OnClickListener {
private ViewStub mViewStub;
private Button btGet;
private Button btDown;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewStub = (ViewStub) findViewById(R.id.view_content);
btGet = (Button) findViewById(R.id.bt_get);
btGet.setOnClickListener(this);
btDown = (Button) findViewById(R.id.bt_down);
btDown.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.bt_get:
mViewStub.setVisibility(View.VISIBLE);
break;
case R.id.bt_down:
mViewStub.setVisibility(View.INVISIBLE);
break;
}
}
}
这个例子的实质是使用两个Button来控制这个ViewStub是否显示,效果如下:初始状态:
在点击加载按钮后的状态:
点击移除按钮时,回到初始状态。下面对其做一下改动,主要是加载按钮处:
case R.id.bt_get:
View viewGet = mViewStub.inflate();
viewGet.setVisibility(View.VISIBLE);
viewGet.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "Click", Toast.LENGTH_SHORT).show();
}
});
break;
这段代码的含义是通过ViewStub加载目标布局,然后显示出来,点击的时候出现一个Toast提示,效果如下:
之后我有个误操作,再次点击加载按钮,发现程序挂掉了,Log提示:java.lang.IllegalStateException: ViewStub must have a non-null ViewGroup viewParent,解释如下:
当调用inflate()方法的时候,ViewStub被引用的资源替代,并且返回引用的View。这样程序可以直接得到引用的View而不用再次调用findViewById()方法来查找了。只能在一个ViewStub对象上调用一次inflate()方法,因为当调用inflate()方法后原来的ViewStub对象已经被引用的资源所代替,ViewStub会从父组件中删除,引起上述异常。
当你获取这个view的实例过后,就可以对其操作了,但是ViewStub将不会再起作用。切记这一点。