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

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();
        }

        final String imgUrl = list[position];
        // 给 ImageView 设置一个 tag
        holder.img.setTag(imgUrl);
        // 预设一个图片
        holder.img.setImageResource(R.drawable.ic_launcher);

        if (imgUrl != null && !imgUrl.equals("")) {
            Bitmap bitmap = imageLoader.loadImage(holder.img, imgUrl,
                    new ImageDownloadedCallBack() {

                        @Override
                        public void onImageDownloaded(ImageView imageView,
                                Bitmap bitmap) {
                            // 通过 tag 来防止图片错位
                            if (imageView.getTag() != null
                                    && imageView.getTag().equals(imgUrl)) {
                                imageView.setImageBitmap(bitmap);
                            }
                        }
                    });

            if (bitmap != null) {
                holder.img.setImageBitmap(bitmap);
            }
        }

        return convertView;
    }
复制代码

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

(1) 使用线程池

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

 可能出现 OOM (out of memory)。

(2) 内存、文件双缓存

 内存缓存使用 LruCache, 顾名思义基于 LRU 算法,每次回收最近最少被使用的对象 

复制代码
/**
 * 图片异步加载类
 * 
 * @author Leslie.Fang
 * 
 */
public class AsyncImageLoader {
    // 最大线程数
    private static final int MAX_THREAD_NUM = 10;
    // 一级内存缓存基于 LruCache
    private BitmapCache bitmapCache;
    // 二级文件缓存
    private FileUtil fileUtil;
    // 线程池
    private ExecutorService threadPools = null;

    public AsyncImageLoader(Context context) {
        bitmapCache = new BitmapCache();
        fileUtil = new FileUtil(context);
        threadPools = Executors.newFixedThreadPool(MAX_THREAD_NUM);
    }

    public Bitmap loadImage(final ImageView imageView, final String imageUrl,
            final ImageDownloadedCallBack imageDownloadedCallBack) {
        final String filename = imageUrl
                .substring(imageUrl.lastIndexOf("/") + 1);
        final String filepath = fileUtil.getAbsolutePath() + "/" + filename;

        // 先从内存中拿
        Bitmap bitmap = bitmapCache.getBitmap(imageUrl);

        if (bitmap != null) {
            Log.i("aaaa", "image exists in memory");
            return bitmap;
        }

        // 从文件中找
        if (fileUtil.isBitmapExists(filename)) {
            Log.i("aaaa", "image exists in file" + filename);
            bitmap = BitmapFactory.decodeFile(filepath);

            // 重新缓存到内存中
            bitmapCache.putBitmap(imageUrl, bitmap);
            return bitmap;
        }

        // 内存和文件中都没有再从网络下载
        if (imageUrl != null && !imageUrl.equals("")) {
            final Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 111 && imageDownloadedCallBack != null) {
                        Bitmap bitmap = (Bitmap) msg.obj;
                        imageDownloadedCallBack.onImageDownloaded(imageView,
                                bitmap);
                    }
                }
            };

            Thread thread = new Thread() {
                @Override
                public void run() {
                    Log.i("aaaa", Thread.currentThread().getName()
                            + " is running");
                    InputStream inputStream = HTTPService.getInstance()
                            .getStream(imageUrl);
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                    // 图片下载成功后缓存并执行回调刷新界面
                    if (bitmap != null) {
                        // 先缓存到内存
                        bitmapCache.putBitmap(imageUrl, 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 ImageDownloadedCallBack {
        void onImageDownloaded(ImageView imageView, Bitmap bitmap);
    }
}
复制代码

 

代码地址: https://github.com/lesliebeijing/AsyncImageLoader.git

如果使用  Volley 就简单的多了

同一个 URL 请求的重复发送,退出 activity 后队列中请求的 cancel,(上面的 demo 没有处理这两种情况)

图片的缓存等 都不用自己处理了,  Volley 都封装好了。

Volley ListView 异步加载图片  demo:  https://github.com/lesliebeijing/VolleyListViewImageDemo.git

关于ListView图片错乱解决方案 listview加载本地图片错乱解决方法 立即下载

相关推荐

伯德图逆向工程:用MATLAB从扫频数据中提取系统特性的5个关键步骤

本文详细介绍了使用MATLAB从扫频数据中提取系统特性的5个关键步骤,包括数据预处理、频响特性提取、相位解缠绕、系统特性反演和结果验证。通过伯德图逆向工程技术,工程师可以有效分析黑箱系统的频率响应特性,解决噪声干扰和相位跳变等实际问题,提升控制系统调试效率。

weixin_29081163的博客 195

Android实现图片下载

/** * 下载服务器网络图片 * */ public class DownBitmap { private DownBitmap() { } private static DownBitmap my = null; public static synchronized DownBitmap getInstance() { if (my == null) my = ne

我是一只小鸟。 893

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

在Android所有系统自带的控件当中,ListView这个控件算是用法比较复杂的了,关键是用法复杂也就算了,它还经常会出现一些稀奇古怪的问题,让人非常头疼。比如说在ListView中加载图片,如果是同步加载图片倒还好,但是一旦使用异步加载图片那么问题就来了,这个问题我相信很多Android开发者都曾经遇到过,就是异步加载图片会出现错位乱序的情况。遇到这个问题时,不少人在网上搜索找到了相应的解决方案,但是真正深入理解这个问题出现的原因并对症解决的人恐怕还并不是很多。那么今天我们就来具体深入分析一下ListV

郭霖的专栏 7万+

RAG 实战:LangChain + 向量数据库构建知识问答系统

从文档加载、分割、嵌入到检索增强生成,完整实现一个基于本地文档的知识问答系统。

kaixin110a的博客 187

详解Linux下使用vscode编译运行和调试C/C++

本教程将具体详解使用Visual Studio Code在Linux环境下使用C/C++方法。 GCC C++ 编译器 (g++) 和 GDB 调试器的方法。 GCC是GNU 编译器集合; GDB 是 GNU 调试器。 1.先决条件 安装Visual Studio Code. 安装C++拓展插件。点击在vscode界面最左侧的Extensions图标(打开快捷键:ctrl+shift+X),搜索“C/C++”,点击进行安装。 确保已安装gcc. 一般ubuntu系统会预装gcc.在终端窗口中输入如下命令

休的博客 6240

Android ListView 异步加载图片错位的问题

 Android ListView 异步加载图片错位的问题 请参考: http://www.cnblogs.com/lesliefang/p/3619223.html http://blog.youkuaiyun.com/guolin_blog/article/details/45586553 http://blog.youkuaiyun.com/zhangtao19931104/article

ModestLearners的博客 1069

《UDS协议从入门到精通》系列——图解0x83:访问时间参数

详细介绍UDS协议0x83服务,目标ECU的时序参数设置有哪些注意点?

车载系统攻城狮 2138

android中ListView异步加载图片时的图片错位问题解决方案(1)

Android中的ListView是一个非常常用的控件,但是它却并不像想象中的那么简单。特别是当你需要在ListView中展示大量网络图片的时候,处理不好轻则用户体验不佳,重则OOM,异步线程丢失或者图片错位。 关于其中的OOM和异步线程丢失的问题,是一个很庞大的话题,本人能力有限,无法说清,只有遇到的时候临时找原因,想办法解决了。但是对于图片错位,却是可以避免的,今天我们就来说一说ListVi

ouyewang的专栏 916

Kafka 性能优化与常见问题排查

Kafka 的高性能依赖于合理的集群设计、参数优化与高效的监控体系。生产环境中,应重点关注I/O 瓶颈、分区规划、Producer/Consumer 参数调优,并通过监控与排查手段快速定位问题,从而保障 Kafka 在实时场景下的稳定运行。

X2829352498的博客 235

YOLOv8改进策略【卷积层】| CVPR-2023 部分卷积 PConv 轻量化卷积,降低内存占用

本文记录的是利用优化的目标检测方法研究。深度可分离卷积可以减少FLOPs,但会导致更高的内存访问,引起延迟并减慢整体计算。部分卷积利用逐点卷积处理通道冗余,减少模型计算量和内存访问量。Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks及其改进的实现代码如下: 四、创新模块 4.1 改进点⭐ 模块改进方法:直接加入(第五节讲解添加步骤)。模块加入如下:模块改进方法:基于的(第五节讲解添加步骤)。第二种改进方法是对中的进行改进,并将

Limiiiing的博客 1015

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

Android listview 异步加载图片并防止错位 网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题。 我简单分析一下: 当重用 convertView 时,最初一

shadowliucs的专栏 477

广东省低应变题库

11.按《建筑地基基础检测规范》DBJ/T15-60-2019的规定,采用低应变法对混凝土预制桩进行检测时,地基基础设计等级为甲级的桩基工程抽检桩数不应少于总桩数的30%,其余桩基工程抽检桩数不应少于总桩数的20%,且不得少于10根;16.按《建筑地基基础检测规范》DBJ/T15-60-2019的规定,当对某桩的桩身完整性判定为III类时,可认为该桩不满足验收条件,不用进一步确定该桩桩身缺陷对桩身结构承载力的影响程度。2.地基基础检测所用计量器具均应在检定(校准、验证)的有效期内,性能符合相关技术要求。

ZShy001238的博客 318
上一篇: 在QQ通讯录,360之前拦截短信
下一篇: android listview 加载图片错乱(错位)(2)
乐由心起
博客等级 码龄16年 61粉丝 295原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值