android listview 异步加载图片并防止错位

网上找了一张图, 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/473283.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 1705. 吃苹果的最大数目(优先队列)

文章目录1. 题目2. 解题1. 题目 有一棵特殊的苹果树&#xff0c;一连 n 天&#xff0c;每天都可以长出若干个苹果。 在第 i 天&#xff0c;树上会长出 apples[i] 个苹果&#xff0c;这些苹果将会在 days[i] 天后&#xff08;也就是说&#xff0c;第 i days[i] 天时&#xff0…

全面系统地总结Linux的基本操作(上)

1、 Linux文件和目录 Windows 和 Linux 文件系统 在 windows 平台下&#xff0c;打开"计算机"&#xff0c;我们看到的是一个个的驱动器盘符&#xff1a;每个驱动器都有自己的根目录结构&#xff0c;这样形成了多个树并列的情形&#xff0c;如图所示&#xff1a; 在 …

Pytorch 神经网络nn模块

文章目录1. nn模块2. torch.optim 优化器3. 自定义nn模块4. 权重共享参考 http://pytorch123.com/ 1. nn模块 import torch N, D_in, Hidden_size, D_out 64, 1000, 100, 10torch.nn.Sequential 建立模型&#xff0c;跟 keras 很像 x torch.randn(N, D_in) y torch.randn…

全面系统地总结Linux的基本操作(下)

4、 Linux命令-系统管理 4.1 查看日历:cal cal 命令用于查看当前日历&#xff0c;-y 显示整年日历&#xff1a; 4.2 显示或设置日期:date 设置时间格式&#xff08;需要管理员权限&#xff09;&#xff1a; date [MMDDhhmm[[CC]YY][.ss]] format CC 为年前两位 yy 为年的后…

免费个人博客:使用hexo+github搭建详细教程

前言 使用github pages服务搭建博客的好处有&#xff1a; 全是静态文件&#xff0c;访问速度快&#xff1b;免费方便&#xff0c;不用花一分钱就可以搭建一个自由的个人博客&#xff0c;不需要服务器不需要后台&#xff1b;可以随意绑定自己的域名&#xff0c;不仔细看的话根…

LeetCode 1235. 规划兼职工作(动态规划+二分查找)

文章目录1. 题目2. 解题1. 题目 你打算利用空闲时间来做兼职工作赚些零花钱。 这里有 n 份兼职工作&#xff0c;每份工作预计从 startTime[i] 开始到 endTime[i] 结束&#xff0c;报酬为 profit[i]。 给你一份兼职工作表&#xff0c;包含开始时间 startTime&#xff0c;结束…

刷新页面,无论点击多少次让Element UI的Message消息提示弹出一个

一、遇到的问题 Element UI的Message消息提示是点击一次触发一次的。在开发的时候经常会作为一些校验提示&#xff0c;但是公司的测试人员在进行测试时会一直点&#xff0c;然后就会出现如下图的情况。虽然客户使用的时候一般来说不会出现这种情况&#xff08;毕竟客户不会闲着…

如何让二维码自适应浏览器的尺寸

一、遇到的问题&#xff1a; 正常浏览网页&#xff0c;二维码正常显示&#xff0c;但是随着浏览器的扩大与缩小&#xff0c;二维码尺寸不会随着屏幕自适应 正常浏览&#xff08;截取部分&#xff09;&#xff1a; 缩小浏览器&#xff08;截取部分&#xf…

E6全部刷机包

此版本号基于R533_G_11.11.10P_GSZMCAUT679DA01B_LP064DA_T679DA_S005_E001_P002_R001_G004_1FF.sbf制作耳机接听或挂机正常内置Loader&#xff08;asmotoe2&#xff09;、Console&#xff08;网上的大侠&#xff09;、showQ&#xff08;bint大侠&#xff09;、SetupPKG&#x…

LeetCode 330. 按要求补齐数组(贪心)

文章目录1. 题目2. 解题1. 题目 给定一个已排序的正整数数组 nums&#xff0c;和一个正整数 n 。 从 [1, n] 区间内选取任意个数字补充到 nums 中&#xff0c;使得 [1, n] 区间内的任何数字都可以用 nums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数…

系统总结vue组件间通信、数据传递(父子组件,同级组件)

总结一下对vue组件通信的理解和使用。一、组件目录结构 父组件&#xff1a;app.vue子组件&#xff1a;page1.vue子组件&#xff1a;page2.vue 父组件 app.vue <template><div id"app"><p>请输入单价: <input type"text" v-model&qu…

LeetCode 1224. 最大相等频率(哈希)

文章目录1. 题目2. 解题1. 题目 给出一个正整数数组 nums&#xff0c;请你帮忙从该数组中找出能满足下面要求的 最长 前缀&#xff0c;并返回其长度&#xff1a; 从前缀中 删除一个 元素后&#xff0c;使得所剩下的每个数字的出现次数相同。 如果删除这个元素后没有剩余元素…

从零开始,手把手交给你vue如何新建一个项目

vue创建项目&#xff08;npm安装→初始化项目&#xff09; 第一步npm安装 首先&#xff1a;先从nodejs.org中下载nodejs 图1 双击安装&#xff0c;在安装界面一直Next 图2 图3 图4 直到Finish完成安装。 打开控制命令行程序&#xff08;CMD&#xff09;,检查是否正常 图5 …

数学图形(1.33) 棕子曲线

#http://www.mathcurve.com/courbes2d/vasques/vasques.shtml vertices 10000 t from 0 to (8*PI) a rand_int2(1, 30) b rand_int2(1, 4) n 8 x cos(n*t - t)*cos(n*t) y cos(n*t)^2 a 10 x x*a y y*a 相关软件参见:数学图形可视化工具,使用自己定义语法的脚本代码生…

LeetCode 1278. 分割回文串 III(区间DP)

文章目录1. 题目2. 解题1. 题目 给你一个由小写字母组成的字符串 s&#xff0c;和一个整数 k。 请你按下面的要求分割字符串&#xff1a; 首先&#xff0c;你可以将 s 中的部分字符修改为其他的小写英文字母。接着&#xff0c;你需要把 s 分割成 k 个非空且不相交的子串&…

LeetCode 1187. 使数组严格递增(DP)*

文章目录1. 题目2. 解题1. 题目 给你两个整数数组 arr1 和 arr2&#xff0c;返回使 arr1 严格递增所需要的最小「操作」数&#xff08;可能为 0&#xff09;。 每一步「操作」中&#xff0c;你可以分别从 arr1 和 arr2 中各选出一个索引&#xff0c;分别为 i 和 j&#xff0c…

用Python进行屏幕截图,只用两行代码搞定

一、计算机中如何进行屏幕截图呢&#xff1f; 1、全屏截图 按下键盘中的‘PRTSC’或者‘Print Screen’键&#xff0c;即可实现全屏截图&#xff08;不同键盘位置和名称可能不同&#xff09;。此时&#xff0c;并不能看到效果&#xff0c;只是将截图保存在粘贴板中&#xff0…

利用nginx建立windows软连,实现IP访问文件

一、运行nginx 1、首先下载nginx&#xff0c;下载地址&#xff1a;https://www.lanzous.com/ianm7tg 2、解压文件如图&#xff1a; 3、运行nginx.exe&#xff0c;浏览器运行电脑ip地址&#xff0c;如图&#xff1a; 二、cmd管理员权限 运行中输入“cmd”&#xff0c;按住shi…

LeetCode 1263. 推箱子(BFS+DFS / 自定义哈希set)

文章目录1. 题目2. 解题2.1 超时解2.2 BFS DFS1. 题目 「推箱子」是一款风靡全球的益智小游戏&#xff0c;玩家需要将箱子推到仓库中的目标位置。 游戏地图用大小为 n * m 的网格 grid 表示&#xff0c;其中每个元素可以是墙、地板或者是箱子。 现在你将作为玩家参与游戏&a…

深入浅出Java回调机制

前几天看了一下Spring的部分源码&#xff0c;发现回调机制被大量使用&#xff0c;觉得有必要把Java回调机制的理解归纳总结一下&#xff0c;以方便在研究类似于Spring源码这样的代码时能更加得心应手。 注&#xff1a;本文不想扯很多拗口的话来充场面&#xff0c;我的目的是希望…