Android性能优化之ViewStub

http://www.cnblogs.com/lwbqqyumidi/p/4047108.html

ViewStub是Android布局优化中一个很不错的标签/控件,直接继承自View。虽然Android开发人员基本上都听说过,但是真正用的可能不多。

ViewStub可以理解成一个非常轻量级的View,与其他的控件一样,有着自 己的属性及特定的方法。当ViewStub使用在布局文件中时,当程序inflate布局文件时,ViewStub本身也会被解析,且占据内存控件,但是 与其他控件相比,主要区别体现在以下几点:

1.当布局文件inflate时,ViewStub控件虽然也占据内存,但是相相比于其他控件,ViewStub所占内存很小;

2.布局文件inflate时,ViewStub主要是作为一个“占位符”的性 质,放置于view tree中,且ViewStub本身是不可见的。ViewStub中有一个layout属性,指向ViewStub本身可能被替换掉的布局文件,在一定时 机时,通过viewStub.inflate()完成此过程;

3.ViewStub本身是不可见的,对 ViewStub setVisibility(..)与其他控件不一样,ViewStub的setVisibility 成View.VISIBLE或INVISIBLE如果是首次使用,都会自动inflate其指向的布局文件,并替换ViewStub本身,再次使用则是相 当于对其指向的布局文件设置可见性。

这里需要注意的是:

1.ViewStub之所以常称之为“延迟化加载”,是因为在教多数情况下,程序 无需显示ViewStub所指向的布局文件,只有在特定的某些较少条件下,此时ViewStub所指向的布局文件才需要被inflate,且此布局文件直 接将当前ViewStub替换掉,具体是通过viewStub.infalte()或 viewStub.setVisibility(View.VISIBLE)来完成;

2.正确把握住ViewStub的应用场景非常重要,正如如1中所描述需求场景下,使用ViewStub可以优化布局;

3.对ViewStub的inflate操作只能进行一次,因为inflate的 时候是将其指向的布局文件解析inflate并替换掉当前ViewStub本身(由此体现出了ViewStub“占位符”性质),一旦替换后,此时原来的 布局文件中就没有ViewStub控件了,因此,如果多次对ViewStub进行infalte,会出现错误信息:ViewStub must have a non-null ViewGroup viewParent。

4.3中所讲到的ViewStub指向的布局文件解析inflate并替换掉当前 ViewStub本身,并不是完全意义上的替换(与include标签还不太一样),替换时,布局文件的layout params是以ViewStub为准,其他布局属性是以布局文件自身为准。

下面看一下简单的需求场景:在listview显示列表数据时,可能会出现服务端 一条数据都没有的情况,此时显示一个EmptyView,提示用户暂无数据。此时考虑到实际应用中EmptyView显示出来的机会相当小,因此,可以在 布局文件中使用ViewStub站位,然后确实没有数据时才viewStub.infalte()。

相关部分代码如下:

Android性能优化之ViewStub
 1 public void showEmptyView() {
 2     listview.setVisibility(View.GONE);
 3     if (noDataView == null) {
 4         ViewStub noDataViewStub = (ViewStub) view.findViewById(R.id.no_data_viewstub);
 5         noDataView = noDataViewStub.inflate();
 6     } else {
 7         noDataView.setVisibility(View.VISIBLE);
 8     }
 9 }
10 
11 public void showListView(){
12     listview.setVisibility(View.VISIBLE);
13     if(noDataView != null){
14         noDataView.setVisibility(View.GONE);
15     }
16 }
Android性能优化之ViewStub

特别需要注意的是对ViewStub是否已经inflate的判断。

在Listview Item中,有时候可能遇到如下场景:在不同的列表页item的布局一部分不同,但相对于整个item布局来说又不是很多,此时最常见的有如下两种处理:

1.对不同的部分都写出来,放到一个item文件中,然后逻辑分别处理不同部分的显示与否(View.VISIBLE和View.GONE);

2.对这两种不同的item整个部分都分别区分开,完全写成两个item文件,然后结合listView显示不同布局分别做逻辑处理(通过getItemType()等方式)。

以上两种处理方式其实都可以,第一种方式逻辑清晰,非常灵活,只是在一定程度上增 加了内存和资源消耗。第二种方式是的布局文件有重复(虽然相同部分可以通过include,但是逻辑上还是有重复的),包括逻辑上处理的代码实质上的重 复。一般对于有较大不同的item布局推荐采用此种方式。

有时候结合需求,可以在第一种方式的基础上,结合ViewStub“占位符”可以比较好的完成此类需求。也相当于是两种方式的一种折中形式,但同时兼顾了内存和资源消耗以及不同的布局逻辑控件。

---------------------------------------------------------------------------------
笔者水平有限,若有错漏,欢迎指正,欢迎转载以及CV操作,但希注明出处,谢谢!
 
 
 
 
 
 
 
2)
ViewStub的应用
 
     在开发应用程序的时候,经常会遇到这样的情况,会在运行时动态根据条件来决定显示哪个View或某个布局。那么最通常的想法就是把可能用到的View都写在上面,先把它们的可见性都设为View.GONE,然后在代码中动态的更改它的可见性。这样的做法的优点是逻辑简单而且控制起来比较灵活。但是它的缺点就是,耗费资源。虽然把View的初始可见View.GONE但是在Inflate布局的时候View仍然会被Inflate,也就是说仍然会创建对象,会被实例化,会被设置属性。也就是说,会耗费内存等资源。

      推荐的做法是使用android.view.ViewStub,ViewStub 是一个轻量级的View,它一个看不见的,不占布局位置,占用资源非常小的控件。可以为ViewStub指定一个布局,在Inflate布局的时候,只有 ViewStub会被初始化,然后当ViewStub被设置为可见的时候,或是调用了ViewStub.inflate()的时候,ViewStub所向 的布局就会被Inflate和实例化,然后ViewStub的布局属性都会传给它所指向的布局。这样,就可以使用ViewStub来方便的在运行时,要还 是不要显示某个布局。

      但ViewStub也不是万能的,下面总结下ViewStub能做的事儿和什么时候该用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的可见性来控制。

下面来看一个实例

在这个例子中,要显示二种不同的布局,一个是用TextView显示一段文字,另一个则是用ImageView显示一个图片。这二个是在onCreate()时决定是显示哪一个,这里就是应用ViewStub的最佳地点。

先来看看布局,一个是主布局,里面只定义二个ViewStub,一个用来控制TextView一个用来控制ImageView,另外就是一个是为显示文字的做的TextView布局,一个是为ImageView而做的布局:

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:orientation="vertical"  
  5.   android:layout_width="fill_parent"  
  6.   android:layout_height="fill_parent"  
  7.   android:gravity="center_horizontal">  
  8.   <ViewStub   
  9.     android:id="@+id/viewstub_demo_text"  
  10.     android:layout_width="wrap_content"  
  11.     android:layout_height="wrap_content"  
  12.     android:layout_marginLeft="5dip"  
  13.     android:layout_marginRight="5dip"  
  14.     android:layout_marginTop="10dip"  
  15.     android:layout="@layout/viewstub_demo_text_layout"/>  
  16.   <ViewStub   
  17.     android:id="@+id/viewstub_demo_image"  
  18.     android:layout_width="wrap_content"  
  19.     android:layout_height="wrap_content"  
  20.     android:layout_marginLeft="5dip"  
  21.     android:layout_marginRight="5dip"  
  22.     android:layout="@layout/viewstub_demo_image_layout"/>  
  23. </LinearLayout>  
为TextView的布局:

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:orientation="vertical"  
  5.   android:layout_width="wrap_content"  
  6.   android:layout_height="wrap_content">  
  7.     <TextView  
  8.         android:id="@+id/viewstub_demo_textview"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:background="#aa664411"  
  12.         android:textSize="16sp"/>  
  13. </LinearLayout>  
为ImageView的布局:

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:orientation="vertical"  
  5.   android:layout_width="wrap_content"  
  6.   android:layout_height="wrap_content">  
  7.     <ImageView  
  8.         android:id="@+id/viewstub_demo_imageview"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"/>  
  11. </LinearLayout>  
下面来看代码,决定来显示哪一个,只需要找到相应的ViewStub然后调用其infalte()就可以获得相应想要的布局:

 

  1. package com.effective;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.ViewStub;  
  6. import android.widget.ImageView;  
  7. import android.widget.TextView;  
  8.   
  9. public class ViewStubDemoActivity extends Activity {  
  10.     @Override  
  11.     public void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.viewstub_demo_activity);  
  14.         if ((((int) (Math.random() * 100)) & 0x01) == 0) {  
  15.             // to show text  
  16.             // all you have to do is inflate the ViewStub for textview  
  17.             ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_text);  
  18.             stub.inflate();  
  19.             TextView text = (TextView) findViewById(R.id.viewstub_demo_textview);  
  20.             text.setText("The tree of liberty must be refreshed from time to time" +  
  21.                     " with the blood of patroits and tyrants! Freedom is nothing but " +  
  22.                     "a chance to be better!");  
  23.         } else {  
  24.             // to show image  
  25.             // all you have to do is inflate the ViewStub for imageview  
  26.             ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_image);  
  27.             stub.inflate();  
  28.             ImageView image = (ImageView) findViewById(R.id.viewstub_demo_imageview);  
  29.             image.setImageResource(R.drawable.happy_running_dog);  
  30.         }  
  31.     }  
  32. }  
运行结果:

Android性能优化之ViewStubAndroid性能优化之ViewStub

使用的时候的注意事项:

某些布局属性要加在ViewStub而不是实际的布局上面,才会起作用,比如上面用的android:layout_margin*系列属性,如果加在 TextView上面,则不会起作用,需要放在它的ViewStub上面才会起作用。而ViewStub的属性在inflate()后会都传给相应的布 局。

ViewStub是Android布局优化中一个很不错的标签/控件,直接继承自View。虽然Android开发人员基本上都听说过,但是真正用的可能不多。

ViewStub可以理解成一个非常轻量级的View,与其他的控件一样,有着自 己的属性及特定的方法。当ViewStub使用在布局文件中时,当程序inflate布局文件时,ViewStub本身也会被解析,且占据内存控件,但是 与其他控件相比,主要区别体现在以下几点:

1.当布局文件inflate时,ViewStub控件虽然也占据内存,但是相相比于其他控件,ViewStub所占内存很小;

2.布局文件inflate时,ViewStub主要是作为一个“占位符”的性 质,放置于view tree中,且ViewStub本身是不可见的。ViewStub中有一个layout属性,指向ViewStub本身可能被替换掉的布局文件,在一定时 机时,通过viewStub.inflate()完成此过程;

3.ViewStub本身是不可见的,对 ViewStub setVisibility(..)与其他控件不一样,ViewStub的setVisibility 成View.VISIBLE或INVISIBLE如果是首次使用,都会自动inflate其指向的布局文件,并替换ViewStub本身,再次使用则是相 当于对其指向的布局文件设置可见性。

这里需要注意的是:

1.ViewStub之所以常称之为“延迟化加载”,是因为在教多数情况下,程序 无需显示ViewStub所指向的布局文件,只有在特定的某些较少条件下,此时ViewStub所指向的布局文件才需要被inflate,且此布局文件直 接将当前ViewStub替换掉,具体是通过viewStub.infalte()或 viewStub.setVisibility(View.VISIBLE)来完成;

2.正确把握住ViewStub的应用场景非常重要,正如如1中所描述需求场景下,使用ViewStub可以优化布局;

3.对ViewStub的inflate操作只能进行一次,因为inflate的 时候是将其指向的布局文件解析inflate并替换掉当前ViewStub本身(由此体现出了ViewStub“占位符”性质),一旦替换后,此时原来的 布局文件中就没有ViewStub控件了,因此,如果多次对ViewStub进行infalte,会出现错误信息:ViewStub must have a non-null ViewGroup viewParent。

4.3中所讲到的ViewStub指向的布局文件解析inflate并替换掉当前 ViewStub本身,并不是完全意义上的替换(与include标签还不太一样),替换时,布局文件的layout params是以ViewStub为准,其他布局属性是以布局文件自身为准。

下面看一下简单的需求场景:在listview显示列表数据时,可能会出现服务端 一条数据都没有的情况,此时显示一个EmptyView,提示用户暂无数据。此时考虑到实际应用中EmptyView显示出来的机会相当小,因此,可以在 布局文件中使用ViewStub站位,然后确实没有数据时才viewStub.infalte()。

相关部分代码如下:

Android性能优化之ViewStub
 1 public void showEmptyView() {
 2     listview.setVisibility(View.GONE);
 3     if (noDataView == null) {
 4         ViewStub noDataViewStub = (ViewStub) view.findViewById(R.id.no_data_viewstub);
 5         noDataView = noDataViewStub.inflate();
 6     } else {
 7         noDataView.setVisibility(View.VISIBLE);
 8     }
 9 }
10 
11 public void showListView(){
12     listview.setVisibility(View.VISIBLE);
13     if(noDataView != null){
14         noDataView.setVisibility(View.GONE);
15     }
16 }
Android性能优化之ViewStub

特别需要注意的是对ViewStub是否已经inflate的判断。

在Listview Item中,有时候可能遇到如下场景:在不同的列表页item的布局一部分不同,但相对于整个item布局来说又不是很多,此时最常见的有如下两种处理:

1.对不同的部分都写出来,放到一个item文件中,然后逻辑分别处理不同部分的显示与否(View.VISIBLE和View.GONE);

2.对这两种不同的item整个部分都分别区分开,完全写成两个item文件,然后结合listView显示不同布局分别做逻辑处理(通过getItemType()等方式)。

以上两种处理方式其实都可以,第一种方式逻辑清晰,非常灵活,只是在一定程度上增 加了内存和资源消耗。第二种方式是的布局文件有重复(虽然相同部分可以通过include,但是逻辑上还是有重复的),包括逻辑上处理的代码实质上的重 复。一般对于有较大不同的item布局推荐采用此种方式。

有时候结合需求,可以在第一种方式的基础上,结合ViewStub“占位符”可以比较好的完成此类需求。也相当于是两种方式的一种折中形式,但同时兼顾了内存和资源消耗以及不同的布局逻辑控件。