android listview的优化

listview作为Android中最常用的控件之一,可以以条目的形式展示大量的数据列表,对于每一个item,均要求adapter适配器的getiView()方法返回一个view,因此listview的实现,离不开Adapter。

如果不对adapter的getView()方法进行优化的话,会造成内存被占用,这样如果数据量比较大的话,很容易造成手机内存被消耗,这样app的使用体验就很差。

  1.   @Override  
  2.     public View getView(int position, View convertView, ViewGroup parent) {  
  3.         View view = null;  
  4.         System.out.println("before: "+list.get(position)+"-------  "+convertView);  
  5.         view = inflater.inflate(R.layout.item, null);  
  6.         System.out.println("after: "+list.get(position)+"-------  "+view);  
  7.         TextView textView = (TextView) view.findViewById(R.id.textView);  
  8.         textView.setText(list.get(position));  
  9.         return view;  
  10.     }  

一下是一种没有优化的方式,也是最初的使用,接下来我们来看下控制面板的打印结果;

android listview的优化

可以发现初始状态有10个item显示在界面上,并且他们的before view均是null的,调用inflate方法之后生成了新的view,因此after view非空了;

由于我们目前的数据没有那么多,但是一旦我们有大量数据需要显示的话,直接就会报内存的 。

   通过上面我们会发现一点,在调用getView方法的时候,他的第二个参数可能不会是null的,原因就是RecycleBin缓存帮我们暂存了那些划出屏幕的view,所以我们在convertView非空的情况下我们也没什么必要重新调用inflate方法加载布局了,因为这个方法毕竟也是要解析xml文件的,至少是要花时间的,直接使用从缓存中取出的view即可啦

接下裏我們來看看优化的代码;

  1. @Override  
  2.     public View getView(int position, View convertView, ViewGroup parent) {  
  3.         View view = null;  
  4.         System.out.println("before: "+list.get(position)+"-------  "+convertView);  
  5.         if(convertView == null)  
  6.         {  
  7.             view = inflater.inflate(R.layout.item, null);  
  8.         }else  
  9.             view = convertView;  
  10.         TextView textView = (TextView) view.findViewById(R.id.textView);  
  11.         textView.setText(list.get(position));  
  12.         return view;  
  13.     } 
   不滑动屏幕的时候,程序输出:

                                           android listview的优化

         接着我们滑动屏幕,查看输出:

           android listview的优化

注意红色部分,发现没经过11个item,都会复用之前的view,这也就是说我们的RecycleBin缓存中将只有11个view了,不像前面那样有50个view,这在很大程度上节约了内存,想想如果有上千万条数据需要显示,每个数据条目都有一个view在RecycleBin中是一件多么可怕的事情,进行了convertView是否为null的判断之后,将只会缓存一屏幕的view,当然有可能会多那么几个吧;

         上面我们通过判断convertView是否为空对ListView进行了优化,接下来我们看看getView方法,里面在获取TextView的时候,我们使用了findViewById方法,这个方法是与IO有关的操作,想必也会影响性能吧,他只要的目的是获得某一个view的布局罢了,我们如果有了view的话,其实只需要第一次将该view和其布局绑定到一起就可以了,没必要每次都为view设置布局了,这也就是使用setTag的目的了;

我们来总结一下:

1.通过复用的view的方式来充分利用Android系统本身自带的RecycleBin缓存机制,能够保证即使有再多的item实际中也仅仅会有有限多个item,大大节省内存;

2.使用setTag方式,将view与其对应的控件绑定起来,避免了每次得到view以后都需要通过findViewById的方式来获取控件;

3.对于有多种布局的ListView来说,我们可以通过getViewTypeCount()获得布局的种类,通过getItemViewType(int)获得当前位置上的布局到底是属于哪一类;

另外,使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的 方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView 设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需 要为其显式设置属性(Textview的文本为空也需要设置 setText(""),背景透明也需要 设置),

下面是一个例子:

@Override   
public View getView(int position, View convertView, ViewGroup parent) { 
    ViewHolder myViews; 
    if (convertView == null) { 
        myViews = new ViewHolder(); 
        convertView = mInflater.inflate(R.layout.list_item, null); 
        myViews.mUsername = (TextView)convertView.findViewById(R.id.username); 
        convertView.setTag(myViews); 
    } else { 
    myViews = (ViewHolder)convertView.getTag(); 
    } 
    Info p = infoList.get(position); 
    String dn = p.getDisplayName; 

 myViews.mUsername.setText(StringUtils.isEmpty(dn) ? "" : dn); 
    return convertView; 

static class ViewHolder { 
    private TextView mUsername; 

这样使用之后,你会发现你的app的性能能提高很多,赶快学习下吧