网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作.

如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题。

我简单分析一下:

当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView.

当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Item8 复用的是

Item1 的 view 如果没有异步不会有任何问题,虽然 Item8 和 Item1 指向的是同一个 view,但滑到

Item8 时刷上了 Item8 的数据,这时 Item1 的数据和 Item8 是一样的,因为它们指向的是同一块内存,

但 Item1 已滚出了屏幕你看不见。当 Item1 再次可见时这块 view 又涮上了 Item1 的数据。

但当有异步下载时就有问题了,假设 Item1 的图片下载的比较慢,Item8 的图片下载的比较快,你滚上去

使 Item8 可见,这时 Item8 先显示它自己下载的图片没错,但等到 Item1 的图片也下载完时你发现

Item8 的图片也变成了 Item1 的图片,因为它们复用的是同一个 view。 如果 Item1 的图片下载的比

Item8 的图片快, Item1 先刷上自己下载的图片,这时你滑下去,Item8 的图片还没下载完, Item8

会先显示 Item1 的图片,因为它们是同一快内存,当 Item8 自己的图片下载完后 Item8 的图片又刷成

了自己的,你再滑上去使 Item1 可见, Item1 的图片也会和 Item8 的图片是一样的,

因为它们指向的是同一块内存。

最简单的解决方法就是网上说的,给 ImageView 设置一个 tag, 并预设一个图片。

当 Item1 比 Item8 图片下载的快时, 你滚下去使 Item8 可见,这时 ImageView 的 tag 被设成了

Item8 的 URL, 当 Item1 下载完时,由于 Item1 不可见现在的 tag 是 Item8 的 URL,所以不满足条件,

虽然下载下来了但不会设置到 ImageView 上, tag 标识的永远是可见 view 中图片的 URL。

关键代码如下:

public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) {holder = new ViewHolder();convertView = LayoutInflater.from(context).inflate(R.layout.list_item, null);holder.img = (ImageView) convertView.findViewById(R.id.userimage);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}User user = list.get(position);// 给 ImageView 设置一个 tagholder.img.setTag(user.getImgUrl());// 预设一个图片holder.img.setImageResource(R.drawable.ic_launcher);final String tmpImageUrl = user.getImgUrl();if (user.getImgUrl() != null && !user.getImgUrl().equals("")) {Bitmap bitmap = imageLoader.loadImage(holder.img, user.getImgUrl(),new ImageDownloadCallBack() {@Overridepublic void onImageDownloaded(ImageView imageView,Bitmap bitmap) {// 通过 tag 来防止图片错位if (imageView.getTag() != null&& imageView.getTag().equals(tmpImageUrl)) {imageView.setImageBitmap(bitmap);}}});if (bitmap != null) {holder.img.setImageBitmap(bitmap);}}return convertView;
}

我参考网上资料写了一个 listview 异步加载图片的 DEMO:

(1) 使用线程池

没有线程池,当图片非常多,快速滑动  listview 时由于下载每个图片都开了一个线程,

可能出现 OOM (out of memory)。

(2) 内存、文件双缓存

这里也使用 SoftReference 软引用  

/*** 图片异步加载类* * @author Leslie.Fang* @company EnwaySoft* */
public class AsyncImageLoader {// 最大线程数private static final int MAX_THREAD_NUM = 10;private Map<String, SoftReference<Bitmap>> imageCaches = null;private FileUtil fileUtil;// 线程池private ExecutorService threadPools = null;public AsyncImageLoader(Context context) {imageCaches = new HashMap<String, SoftReference<Bitmap>>();fileUtil = new FileUtil(context);}public Bitmap loadImage(final ImageView imageView, final String imageUrl,final ImageDownloadCallBack imageDownloadCallBack) {final String filename = imageUrl.substring(imageUrl.lastIndexOf("/") + 1);final String filepath = fileUtil.getAbsolutePath() + "/" + filename;// 先从软引用中找if (imageCaches.containsKey(imageUrl)) {SoftReference<Bitmap> reference = imageCaches.get(imageUrl);Bitmap bitmap = reference.get();// 软引用中的 Bitmap 对象可能随时被回收// 如果软引用中的 Bitmap 已被回收,则从文件中找if (bitmap != null) {Log.i("aaaa", "cache exists " + filename);return bitmap;}}// 从文件中找if (fileUtil.isBitmapExists(filename)) {Log.i("aaaa", "file exists " + filename);Bitmap bitmap = BitmapFactory.decodeFile(filepath);// 重新加入到内存软引用中imageCaches.put(imageUrl, new SoftReference<Bitmap>(bitmap));return bitmap;}// 软引用和文件中都没有再从网络下载if (imageUrl != null && !imageUrl.equals("")) {if (threadPools == null) {threadPools = Executors.newFixedThreadPool(MAX_THREAD_NUM);}final Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 111 && imageDownloadCallBack != null) {Bitmap bitmap = (Bitmap) msg.obj;imageDownloadCallBack.onImageDownloaded(imageView,bitmap);}}};Thread thread = new Thread() {@Overridepublic void run() {Log.i("aaaa", Thread.currentThread().getName()+ " is running");InputStream inputStream = HTTPService.getInstance().getStream(imageUrl);Bitmap bitmap = BitmapFactory.decodeStream(inputStream);// 图片下载成功重新缓存并执行回调刷新界面if (bitmap != null) {// 加入到软引用中imageCaches.put(imageUrl, new SoftReference<Bitmap>(bitmap));// 缓存到文件系统fileUtil.saveBitmap(filepath, bitmap);Message msg = new Message();msg.what = 111;msg.obj = bitmap;handler.sendMessage(msg);}}};threadPools.execute(thread);}return null;}public void shutDownThreadPool() {if (threadPools != null) {threadPools.shutdown();threadPools = null;}}/*** 图片下载完成回调接口* */public interface ImageDownloadCallBack {void onImageDownloaded(ImageView imageView, Bitmap bitmap);}
}

DEMO 下载地址:http://pan.baidu.com/s/1sjttuFj

博客转载自:

http://www.cnblogs.com/lesliefang/p/3619223.html

转载于:https://www.cnblogs.com/ycxyyzw/p/3813743.html

android listview 异步加载图片并防止错位相关推荐

  1. 又优化了一下 Android ListView 异步加载图片

    写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好: http://www.iteye.com/topic/685986 我也是因为看 ...

  2. Android ListView异步加载图片乱序问题,原因分析及解决方案

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/45586553 在Android所有系统自带的控件当中,ListView这个控件算是 ...

  3. Android ListView 异步加载图片

    使用ListView.GridView来展示图片是项目中经常遇到的情况,这里使用官方文档的BitmapFun稍作修改实现ListView 异步加载图片效果. 实现原理:给ListView 注册一个 滚 ...

  4. Android实现ListView异步加载图片

    转: http://www.iteye.com/topic/685986 ListView异步加载图片是非常实用的方法,凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法 ...

  5. android开发小技巧:实现listview异步加载图片

    2019独角兽企业重金招聘Python工程师标准>>> 针对listview异步加载图片这个问题,麦子学院android开发老师讲了一种非常实用的方法,麦子学院android开发老师 ...

  6. Android实现ListView异步加载图片总结

    参考自http://blog.csdn.net/wanglong0537/article/details/6334005# http://www.cnblogs.com/slider/archive/ ...

  7. Android实现异步加载图片 ListView

    转自:http://www.congci.com/item/android-listview-load-image ListView异步加载图片是非常实用的方法,凡是是要通过网络获取图片资源一般使用这 ...

  8. Android开发之ListView异步加载图片

    ListView这个控件对于大家肯定不会陌生,即使你是初学者相信也会用ListView.因为ListView这个控件实在是太常用,可以说基本上每一个项目开发都会用到它,今天这篇博客主要讲解,ListV ...

  9. android listview 异步加载问题

    ============问题描述============ 学做android,自己想模仿QQ空间做一个小demo listview异步加载图片的时候遇到了一个问题 异步加载用到了SoftReferen ...

最新文章

  1. [hdu 1561] The more, The Better
  2. 大华嵌入式硬盘录像机数据恢复工具
  3. 手机cpu排行_鲁大师手机芯片排行榜:麒麟990第四,骁龙855第五!
  4. git之you can‘t overwrite the remote branch问题解决
  5. 关于数组的 slice() 和 splice() 方法
  6. 纵观计算机网络发展历程,人工智能在计算机网络技术中的应用分析
  7. 会声会影编辑html,网页视频制作使用会声会影剪辑
  8. 二维码生成器如何制作二维码
  9. 使用代码查重工具sim 为LDUOnlineJudge增加代码查重功能
  10. 捣鼓车间 | 学生获奖作品:戒烟帽
  11. Code First开发系列之领域建模和管理实体关系
  12. windows家族介绍
  13. vue内使用 cytoscape(数据可视化)
  14. 计算机网络笔记1 计算机网络概述
  15. siliconc8051f Silicon C8051F编程器使用出错解决办法
  16. Vs调试报错:不能将 “const char *“ 类型的值分配到 “char *“ 类型的实体
  17. 联想电脑(win10)如何保存高清锁屏壁纸
  18. 如何邀请好友注册您的网站(模拟百度网盘)
  19. 单元话题写作-Unit 1 英语学习
  20. CSDN博客第一期订阅专栏:跟“风云卫星”数据工程师学Python

热门文章

  1. UDP和TCP协议包大小的计算-转
  2. STL之map和multimap容器
  3. 稳定和性能如何兼顾?58大数据平台的技术演进与实践
  4. gson-2.2.api简单
  5. IT综合管理 新时期的运维管理思路
  6. SQL Server 数据库构架
  7. Android系统中通过shell命令实现wifi的连接控制
  8. 外存中的对换区和文件区
  9. pycharm 离线安装插件
  10. Django/Flask/Tornado三大web框架性能分析