今天在对项目做优化的时候,了解到了一个控件叫ViewStub。
主要的背景是在一般情况下,在一个布局里,我们对于里面的某部分的布局需要进行隐藏的操作,那么我们会采用View.INVISIBLE或是View.GONE。但它有个缺点就是太过于耗费内存了,虽然表面上你是看不到,但其实当你进入界面的时候,就已经按照常规同样进行绘制。而ViewStub则是由于设计的原因,是耗费内存很低,等到有需要的时候才会去将我们要的布局给显示出来了。
首先看一下代码:
MainActivity.class
public class MainActivity extends Activity {
private Button bt_showButton , bt_hideButton ;
private ViewStub viewStub;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bt_showButton = (Button) findViewById(R.id.bt_show);
bt_hideButton = (Button) findViewById(R.id.bt_hide);
viewStub = (ViewStub) findViewById(R.id.viewstub);
bt_showButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
// try {
// viewStub.inflate();
// } catch (Exception e) {
// // TODO: handle exception
// viewStub.setVisibility(View.VISIBLE);
// }
if(viewStub.getParent() != null){
viewStub.inflate();
Log.d("MainActivity", "inflate");
}else {
viewStub.setVisibility(View.VISIBLE);
Log.d("MainActivity", "visibility");
}
}
});
bt_hideButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
viewStub.setVisibility(View.GONE);
}
});
}
}
<LinearLayout 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:orientation="vertical"
tools:context="com.example.viewstubtest.MainActivity" >
<Button
android:id="@+id/bt_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="显示"/>
<Button
android:id="@+id/bt_hide"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="隐藏"/>
<ViewStub
android:id="@+id/viewstub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/other"/>
</LinearLayout>
other.xml是你想要隐藏的布局,这里就不写了。
在布局文件里,我们利用layout将要隐藏的布局跟ViewStub关联起来,然后在代码里,当需要显示的时候,就通过inflate()方法将它显示出来,不过要注意这里的inflate只能在第一次要显示的时候调用,当我们进行隐藏(GONE)重新显示的时候,则应该是用VISIBLE来显示,如果你再次调用inflate,它就会报错(ViewStub must have a non-null ViewGroup viewParent)。这些主要是我们要通过源码去分析,当然还有官方文档。
接下来通过工具来分析看下:
当一开始还没有进行操作的时候,根据布局的分析是只有四个控件
当我们点击显示的时候,我们发现布局的空间发生了改变,我们要隐藏的布局都显示出来了
接下来当我们点击隐藏的时候,我们发现就与以前的一样,隐藏的布局不会变回ViewStub,而是还在,只是隐藏了而已。
所以当我们第一次进入某个界面的时候,我们通过ViewStub可以节省很大的内存,原因在于根据源码我们可以看到
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(0, 0);
}
在测量都时候就给自己0的设定,所以它是没有尺寸的。
一些小点:
1.ViewStub只能用于绑定一个布局,而不能绑定一个单独的View控件
2.不支持<merge />标签。(这个也是在看布局优化的文章时说到,说有时候我们会用Include来进行一个公共的套用,这样也是优化的一种方法,但它如果根布局与现有的布局其实一样,也就会产生一个重复的布局,如果我们把根布局换成<merge />标签,则它就会自动的嵌套入当前需要的位置的根布局里,就避免了多一个重复的布局。)
3.现在我们看到的textview其实是已经在布局文件里把内容写好了的,我刚才在想如果要是放到有网络请求里的怎么办,它的内容是会变的。就看到了可以这样解决的方法:
if (viewStub.getParent() != null) {
View view = viewStub.inflate();
tv1 = (TextView) view.findViewById(R.id.tv1);
tv1.setText("ok");
Log.d("MainActivity", "inflate");
} else {
viewStub.setVisibility(View.VISIBLE);
Log.d("MainActivity", "visibility");
}
所以我觉得是数据请求后保存下来,一旦ViewStub被要求显示了,就将其内容填充。
参考和扩展: