ListView优化整理

原创 2016年05月30日 22:02:15
 

ListView为什么要使用convertView和ViewHolder

原因就是:

1. 使用convertView可以避免重复地调用inflate

2. 使用ViewHolder可以避免重复地调用findViewById



首先讲下ListView的原理:ListView中的每一个Item显示都需要Adapter调用一次getView的方法,这个方法会传入一个convertView的参数,返回的View就是这个Item显示的View。如果当Item的数量足够大,再为每一个Item都创建一个View对象,必将占用很多内存,创建View对象(mInflater.inflate(R.layout.lv_item, null);从xml中生成View,这是属于IO操作)也是耗时操作,所以必将影响性能。Android提供了一个叫做Recycler(反复循环器)的构件,就是当ListView的Item从上方滚出屏幕视角之外,对应Item的View会被缓存到Recycler中,相应的会从下方生成一个Item,而此时调用的getView中的convertView参数就是滚出屏幕的Item的View,所以说如果能重用这个convertView,就会大大改善性能。

以下是最简单的使用viewHolder和convertView的代码

  1. /** 
  2.          * Make a view to hold each row. 
  3.          * 
  4.          * @see android.widget.ListAdapter#getView(int, android.view.View, 
  5.          *      android.view.ViewGroup) 
  6.          */  
  7.         public View getView(int position, View convertView, ViewGroup parent) {  
  8.             // A ViewHolder keeps references to children views to<span style="color:#ff0000;"> avoid unneccessary calls  
  9.             // to findViewById()</span> on each row.  
  10.             ViewHolder holder;  
  11.   
  12.             // When convertView is not null, we can reuse it directly, <span style="color:#ff0000;">there is no need  
  13.             // to reinflate it.</span> We only inflate a new View when the convertView supplied  
  14.             // by ListView is null.  
  15.             if (convertView == null) {  
  16.                 convertView = mInflater.inflate(R.layout.list_item_icon_text, null);  
  17.   
  18.                 // Creates a ViewHolder and store references to the two children views  
  19.                 // we want to bind data to.  
  20.                 holder = new ViewHolder();  
  21.                 holder.text = (TextView) convertView.findViewById(R.id.text);  
  22.                 holder.icon = (ImageView) convertView.findViewById(R.id.icon);  
  23.   
  24.                 convertView.setTag(holder);  
  25.             } else {  
  26.                 // Get the ViewHolder back to get fast access to the TextView  
  27.                 // and the ImageView.  
  28.                 holder = (ViewHolder) convertView.getTag();  
  29.             }  
  30.   
  31.             // Bind the data efficiently with the holder.  
  32.             holder.text.setText(DATA[position]);  
  33.             holder.icon.setImageBitmap((position & 1) == 1 ? mIcon1 : mIcon2);  
  34.   
  35.             return convertView;  
  36.         }  
  37. 1、原因分析
    ListView item缓存机制:为了使得性能更优,ListView会缓存行item(某行对应的View)。ListView通过adapter的getView函数获得每行的item。滑动过程中,

    a. 如果某行item已经滑出屏幕,若该item不在缓存内,则put进缓存,否则更新缓存;
    b. 获取滑入屏幕的行item之前会先判断缓存中是否有可用的item,如果有,做为convertView参数传递给adapter的getView。
    更具体可见源代码ListView.obtainView

     

    这样,如下的getView写法就可以充分利用缓存大大提升ListView的性能。即便上万个行item,最多inflate的次数为n,n为一屏最多显示ListView 行item的个数。

    ListView Adapter getView写法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Override
    public View getView ( int position , View convertView , ViewGroup parent ) {
         ViewHolder holder ;
         if ( convertView == null ) {
             convertView = inflater . inflate ( R . layout . list_item , null ) ;
             holder = new ViewHolder ( ) ;
            ……
             convertView . setTag ( holder ) ;
         } else {
             holder = ( ViewHolder ) convertView . getTag ( ) ;
         }
    }

    这样提升了性能,但同时也会造成另外一些问题:

    a. 行item图片显示重复

    这个显示重复是指当前行item显示了之前某行item的图片。

    比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,且滑动过程中该图片加载结束,

    第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,

    造成显示重复。

    b. 行item图片显示错乱

    这个显示错乱是指某行item显示了不属于该行item的图片。

    比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,第14行显示了第2行的View,这时之前的图片加载结束,就会显示在第14行,造成错乱。

    c. 行item图片显示闪烁

    上面b的情况,第14行图片又很快加载结束,所以我们看到第14行先显示了第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。




  38. /**
      * 显示这个图片,解决了列表问题.
      * 列表问题:滑动过程中,getView的imageView会重复利用,导致图片会串位
      * @param imageView 显得的View
      * @param url the url
      * @return
      */
      publicvoiddisplay( finalImageView imageView,String url) {
       if(AbStrUtil.isEmpty(url)){
        if(noImage != null){
         if(loadingView != null){
          loadingView.setVisibility(View.INVISIBLE);
          imageView.setVisibility(View.VISIBLE);
         }
         imageView.setImageDrawable(noImage);
        }
        return;
       }
       //设置下载项
       finalAbImageDownloadItem item = newAbImageDownloadItem();
       //设置显示的大小
       item.width = width;
       item.height = height;
       //设置为缩放
       item.type = type;
       item.imageUrl = url;
       finalString cacheKey = AbImageCache
       .getCacheKey(item.imageUrl, item.width, item.height, item.type);
       item.bitmap = AbImageCache.getBitmapFromCache(cacheKey);
       //if(D) Log.d(TAG, "缓存中获取的"+cacheKey+":"+item.bitmap);
       //设置标记
       imageView.setTag(url);
       if(item.bitmap == null){
        //先显示加载中
        if(loadingView!=null){
         loadingView.setVisibility(View.VISIBLE);
         imageView.setVisibility(View.INVISIBLE);
        }elseif(loadingImage != null){
         imageView.setImageDrawable(loadingImage);
        }
        //下载完成后更新界面
        item.setListener(newAbImageDownloadListener() {
         @Override
         publicvoidupdate(Bitmap bitmap, String imageUrl) {
          //未设置加载中的图片,并且设置了隐藏的View
          if(loadingView != null&& imageUrl.equals(imageView.getTag())){
           loadingView.setVisibility(View.INVISIBLE);
           imageView.setVisibility(View.VISIBLE);
          }
          //要判断这个imageView的url有变化,如果没有变化才set,
          //有变化就取消,解决列表的重复利用View的问题
          if(bitmap!=null&& imageUrl.equals(imageView.getTag())){
           if(D) Log.d(TAG, "图片下载,设置:"+imageUrl);
           imageView.setImageBitmap(bitmap);
          }else{
           if(errorImage != null&& imageUrl.equals(imageView.getTag())){
            imageView.setImageDrawable(errorImage);
           }
          }
         }
        });
        if(D) Log.d(TAG, "图片下载,执行:"+url);
        mAbImageDownloadPool.execute(item);
       }else{
        if(loadingView != null){
         loadingView.setVisibility(View.INVISIBLE);
         imageView.setVisibility(View.VISIBLE);
        }
        imageView.setImageBitmap(item.bitmap);
       }
      }

    但是 在图片加载慢时,会出现listView显示空白现象,那就需要美工将公司logo设计成一个展示的item,默认展示公司logo,加载出图片就正常显示。

ListView的四种优化方式

优化方式一: 第一种优化就是重用convertView,这也是最简单的一种优化方式,就是在Adapter类的getView方法中通过判断convertView是否为null,是的话就需要在创建一个...
  • Xk632172748
  • Xk632172748
  • 2016年07月18日 15:35
  • 8569

Android性能优化之提高ListView性能的技巧

ListView优化一直是一个老生常谈的问题,不管是面试还是平常的开发中,ListView永远不会被忽略掉,那么这篇文章我们来看看如何最大化的优化ListView的性能。...
  • nugongahou110
  • nugongahou110
  • 2015年07月29日 15:13
  • 13927

Android性能优化--listview 优化机制

详情参考:http://www.cnblogs.com/Android-xiansheng/p/4627124.html 首先来了解一下ListView的工作原理(可参见http://mobil...
  • u010921385
  • u010921385
  • 2016年09月04日 22:12
  • 4452

Android之——ListView优化

作为客户端,其最主要的任务就是最直观的和用户交互。从服务器拿数据,解析过后显示数据,根据用户操作按照一定的协议传回数据,达到用户想要的结果。这是我自己的理解,所以我们的程序,必须给用户一个良好的体验。...
  • l1028386804
  • l1028386804
  • 2015年08月02日 18:10
  • 2454

【Android优化】最强ListView优化方案

convertView重用,ViewHolder优化,图片加载优化,onClickListener处理,减少Item View的布局层级,adapter中的getView方法尽量少使用逻辑,adapt...
  • s003603u
  • s003603u
  • 2015年08月03日 23:35
  • 6539

ListView进阶之使用技巧和优化策略

1      ListView进阶 1.1    ListView的效率优化 【 1、通过ContentView实现优化,减少创建View的次数 2、通过ViewHolder实现优化,减少遍历...
  • JourneyX
  • JourneyX
  • 2016年10月31日 14:01
  • 1287

ListView终极优化方法,绝对流畅

listview可以说是Android开发中最常见的UI控件了,listview能够以列表的方式显示大量同类的数据,这样问题就产生了,既然是大量数据,就会使用到很多布局,给布局绑定数据,listvie...
  • sweetvvck
  • sweetvvck
  • 2013年10月15日 19:11
  • 17838

非线性优化整理-1.牛顿法

作为非线性优化的基础,先从牛顿法开始 牛顿法Newton’s method,又称牛顿-拉弗森法(Newton-Raphson’s method) 可用来1.求解方程根 2.求解极值 1.求解...
  • boksic
  • boksic
  • 2018年01月22日 16:27
  • 404

ListView优化整理

ListView为什么要使用convertView和ViewHolder 原因就是: 1. 使用convertView可以避免重复地调用inflate 2. 使用ViewHolder可以避免重...
  • zhiyuan0932
  • zhiyuan0932
  • 2015年10月14日 10:38
  • 878

ListView的优化整理

ListView为什么要使用convertView和ViewHolder 原因就是: 1. 使用convertView可以避免重复地调用inflate 2. 使用ViewHold...
  • qq_34542275
  • qq_34542275
  • 2016年04月05日 10:48
  • 183
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ListView优化整理
举报原因:
原因补充:

(最多只允许输入30个字)