Android ListView滑动过程中图片显示重复错位闪烁问题解决

Android ListView滑动过程中图片显示重复错位闪烁问题解决


主要分析Android ListView滚动过程中图片显示重复、错乱、闪烁的原因及解决方法,顺带提及ListView的缓存机制。

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写法
Java
@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(); } } /** * ViewHolder * * @author trinea@trinea.cn 2013-08-01 */ private static class ViewHolder { ImageView appIcon; TextView appName; TextView appInfo; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@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 ( ) ;
}
}
 
/**
* ViewHolder
*
* @author trinea@trinea.cn 2013-08-01
*/
private static class ViewHolder {
 
ImageView appIcon ;
TextView   appName ;
TextView   appInfo ;
}

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

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的标识是否一致,一致则显示,否则不做处理即可。下面以使用 ImageCache为ListView提供图片获取缓存为例,ListView中强烈推荐使用 ImageCache。首先在listview adapter的getView中添加

Java
@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(); } …… // add tag for image, to compare it when image loaded finish imageView.setTag(imageUrl); // if not in cache, restore default if (!Cache.ICON_CACHE.get(imageUrl, imageView)) { imageView.setImageDrawable(null); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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 ( ) ;
}
 
……
// add tag for image, to compare it when image loaded finish
imageView . setTag ( imageUrl ) ;
// if not in cache, restore default
if ( ! Cache . ICON_CACHE . get ( imageUrl , imageView ) ) {
imageView . setImageDrawable ( null ) ;
}
}

其中setTag表示设置标识,方便下面进行标志比对

if (!Cache.ICON_CACHE.get(imageUrl, imageView))
1
if ( ! Cache . ICON_CACHE . get ( imageUrl , imageView ) )

Cache.ICON_CACHE为ImageCache的实例,表示如果不在缓存内则设置drawable为null(当然你可以可以设置为你自己的默认资源),防止显示了之前某个行item的图片,解决了a. 行item图片显示重复问题。

 

在ImageCache的OnImageCallbackListener的onGetSuccess函数中添加

Java
public void onGetSuccess(String imageUrl, Drawable imageDrawable, View view, boolean isInCache) { // can be another view child, like textView and so on if (view != null && imageDrawable != null) { ImageView imageView = (ImageView)view; // add tag judge, avoid listView cache and so on String imageUrlTag = (String)imageView.getTag(); if (ObjectUtils.isEquals(imageUrlTag, imageUrl)) { imageView.setImageDrawable(imageDrawable); } } };
1
2
3
4
5
6
7
8
9
10
11
public void onGetSuccess ( String imageUrl , Drawable imageDrawable , View view , boolean isInCache ) {
// can be another view child, like textView and so on
if ( view != null && imageDrawable != null ) {
ImageView imageView = ( ImageView ) view ;
// add tag judge, avoid listView cache and so on
String imageUrlTag = ( String ) imageView . getTag ( ) ;
if ( ObjectUtils . isEquals ( imageUrlTag , imageUrl ) ) {
imageView . setImageDrawable ( imageDrawable ) ;
}
}
} ;

在上面用String imageUrlTag = (String)imageView.getTag();取得之前设置的tag,然后和当前的url进行比较,如果相等则显示,解决了b. 行item图片显示错乱,c. 行item图片显示错乱的两个问题。其中ObjectUtils可见ObjectUtils@Github.

其他异步加载过程解决原理类似。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值