Android listView异步下载和convertView复用产生的错位问题

1:Item图片显示重复

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

比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中ListView已经滑动到了第14行,且滑动过程中该图片加载结束。第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的View对象可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,造成显示重复。

3. Item图片显示闪烁

上面介绍的另外一种情况,如果第14行图片又很快加载结束,所以我们看到第14行先显示了复用的第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。

解决方案:

通过上面的分析我们知道了出现错乱的原因是异步加载及对象被复用造成的,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行Item的标识是否一致,一致则显示,否则不做处理即可。

原理:首先给ImageView设置一个Tag,这个Tag中设置的是图片的url,然后在加载的时候取得这个url和要加载那position中的url对比,如果不相同就加载,相同就是复用以前的就不加载了。

滑动过程中

a. 如果某行item已经滑出屏幕,若该item不在缓存内,则put进缓存,否则更新缓存;

b. 获取滑入屏幕的行item之前会先判断缓存中是否有可用的item,如果有,做为convertView参数传递给adapter的getView。

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

n为一屏最多显示ListView 行item的个数。

 @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行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。

2、解决方法

通过上面的分析我们知道了出现错乱的原因是异步加载及对象被复用造成的,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行item的标识是否一致,一致则显示,否则不做处理即可。

andbase中的实现代码:

/**

  *
 显示这个图片,解决了列表问题.

  *
 列表问题:滑动过程中,getView的imageView会重复利用,导致图片会串位

  *
 @param imageView 显得的View

  *
 @param url the url

  *
 @return

  */

  public

void 
display( final

ImageView 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

;

   }

   //设置下载项

   final

AbImageDownloadItem item = new

AbImageDownloadItem();

   //设置显示的大小

   item.width
 = width;

   item.height
 = height;

   //设置为缩放

   item.type
 = type;

   item.imageUrl
 = url;

   final

String 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);

    }
else

if 
(loadingImage != null

){

     imageView.setImageDrawable(loadingImage);

    }

    //下载完成后更新界面

    item.setListener(
new

AbImageDownloadListener() {

     @Override

     public

void 
update(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);

   }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货

Android项目实战(二):安卓应用程序退出的三种方法

2915
来自专栏Android机器圈

Android图片处理--缩放

把ImageView或者PhotoView的对象名直接添加到display中就OK 了。

2596
来自专栏向治洪

ViewPagerIndicator+viewpager指示器详解

前几天学习了ViewPager作为引导页和Tab的使用方法。后来也有根据不同的使用情况改用Fragment作为Tab的情况,以及ViewPager结合Fragm...

1.1K9
来自专栏向治洪

解决在onCreate()过程中获取View的width和Height为0的方法

最近在看Android底层代码的view绘制原理的时候讲到一个很有意思的事情,也是我几年前刚开始学习Android开发的时候比较纳闷的一个问题,如果你不理解An...

2048
来自专栏自动化测试实战

Appium定位 5 ——xpath

3465
来自专栏Android干货

关于安卓开发通过Toast显示消息提示框

2346
来自专栏7号代码

Android应用界面开发——简单控件和Activity间传递数据

要想开发一个Android App,开发环境是必不可少的,所以学习之前应该先搭建环境,环境如下:

3303
来自专栏上善若水

029android初级篇之android应用的状态栏的颜色

在代码中实现对状态栏颜色的控制,(只对系统版本在android4.4及以上的有效)

863
来自专栏Hongten

android开发_SimpleAdapter适配器

drawable-hdpi文件夹中的图片是自己加入的。主要是在菜单选项中显示的图片:

1022
来自专栏codelang

打造万能的EmptyLayout

1452

扫码关注云+社区

领取腾讯云代金券