- 浏览: 2144257 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (1240)
- mac/IOS (287)
- flutter (1)
- J2EE (115)
- android基础知识 (582)
- android中级知识 (55)
- android组件(Widget)开发 (18)
- android 错误 (21)
- javascript (18)
- linux (70)
- 树莓派 (18)
- gwt/gxt (1)
- 工具(IDE)/包(jar) (18)
- web前端 (17)
- java 算法 (8)
- 其它 (5)
- chrome (7)
- 数据库 (8)
- 经济/金融 (0)
- english (2)
- HTML5 (7)
- 网络安全 (14)
- 设计欣赏/设计窗 (8)
- 汇编/C (8)
- 工具类 (4)
- 游戏 (5)
- 开发频道 (5)
- Android OpenGL (1)
- 科学 (4)
- 运维 (0)
- 好东西 (6)
- 美食 (1)
最新评论
-
liangzai_cool:
请教一下,文中,shell、C、Python三种方式控制led ...
树莓派 - MAX7219 -
jiazimo:
...
Kafka源码分析-序列5 -Producer -RecordAccumulator队列分析 -
hp321:
Windows该命令是不是需要安装什么软件才可以?我试过不行( ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
hp321:
Chenzh_758 写道其实直接用一下代码就可以解决了:JP ...
ImageIO读jpg的时候出现javax.imageio.IIOException: Unsupported Image Type -
huanghonhpeng:
大哥你真强什么都会,研究研究。。。。小弟在这里学到了很多知识。 ...
android 浏览器
网上找了一张图,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。
关键代码如下:
// 给 ImageView 设置一个 tag holder.img.setTag(imgUrl); // 预设一个图片 holder.img.setImageResource(R.drawable.ic_launcher); // 通过 tag 来防止图片错位 if (imageView.getTag() != null && imageView.getTag().equals(imageUrl)) { imageView.setImageBitmap(result); }
我参考网上资料写了一个listview异步加载图片的DEMO:
(1) AsyncTask下载图片
(2) 实现内存、文件二级缓存
内存缓存使用 LruCache,文件缓存使用 DiskLruCache
/** * 图片异步加载类 * * @author Leslie.Fang * */ public class AsyncImageLoader { private Context context; // 内存缓存默认 5M static final int MEM_CACHE_DEFAULT_SIZE = 5 * 1024 * 1024; // 文件缓存默认 10M static final int DISK_CACHE_DEFAULT_SIZE = 10 * 1024 * 1024; // 一级内存缓存基于 LruCache private LruCache<String, Bitmap> memCache; // 二级文件缓存基于 DiskLruCache private DiskLruCache diskCache; public AsyncImageLoader(Context context) { this.context = context; initMemCache(); initDiskLruCache(); } /** * 初始化内存缓存 */ private void initMemCache() { memCache = new LruCache<String, Bitmap>(MEM_CACHE_DEFAULT_SIZE) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount(); } }; } /** * 初始化文件缓存 */ private void initDiskLruCache() { try { File cacheDir = getDiskCacheDir(context, "bitmap"); if (!cacheDir.exists()) { cacheDir.mkdirs(); } diskCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, DISK_CACHE_DEFAULT_SIZE); } catch (IOException e) { e.printStackTrace(); } } /** * 从内存缓存中拿 * * @param url */ public Bitmap getBitmapFromMem(String url) { return memCache.get(url); } /** * 加入到内存缓存中 * * @param url * @param bitmap */ public void putBitmapToMem(String url, Bitmap bitmap) { memCache.put(url, bitmap); } /** * 从文件缓存中拿 * * @param url */ public Bitmap getBitmapFromDisk(String url) { try { String key = hashKeyForDisk(url); DiskLruCache.Snapshot snapShot = diskCache.get(key); if (snapShot != null) { InputStream is = snapShot.getInputStream(0); Bitmap bitmap = BitmapFactory.decodeStream(is); return bitmap; } } catch (IOException e) { e.printStackTrace(); } return null; } /** * 从 url 加载图片 * * @param imageView * @param imageUrl */ public Bitmap loadImage(ImageView imageView, String imageUrl) { // 先从内存中拿 Bitmap bitmap = getBitmapFromMem(imageUrl); if (bitmap != null) { Log.i("leslie", "image exists in memory"); return bitmap; } // 再从文件中找 bitmap = getBitmapFromDisk(imageUrl); if (bitmap != null) { Log.i("leslie", "image exists in file"); // 重新缓存到内存中 putBitmapToMem(imageUrl, bitmap); return bitmap; } // 内存和文件中都没有再从网络下载 if (!TextUtils.isEmpty(imageUrl)) { new ImageDownloadTask(imageView).execute(imageUrl); } return null; } class ImageDownloadTask extends AsyncTask<String, Integer, Bitmap> { private String imageUrl; private ImageView imageView; public ImageDownloadTask(ImageView imageView) { this.imageView = imageView; } @Override protected Bitmap doInBackground(String... params) { try { imageUrl = params[0]; String key = hashKeyForDisk(imageUrl); // 下载成功后直接将图片流写入文件缓存 DiskLruCache.Editor editor = diskCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(0); if (downloadUrlToStream(imageUrl, outputStream)) { editor.commit(); } else { editor.abort(); } } diskCache.flush(); Bitmap bitmap = getBitmapFromDisk(imageUrl); if (bitmap != null) { // 将图片加入到内存缓存中 putBitmapToMem(imageUrl, bitmap); } return bitmap; } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); if (result != null) { // 通过 tag 来防止图片错位 if (imageView.getTag() != null && imageView.getTag().equals(imageUrl)) { imageView.setImageBitmap(result); } } } private boolean downloadUrlToStream(String urlString, OutputStream outputStream) { HttpURLConnection urlConnection = null; BufferedOutputStream out = null; BufferedInputStream in = null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); out = new BufferedOutputStream(outputStream, 8 * 1024); int b; while ((b = in.read()) != -1) { out.write(b); } return true; } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return false; } } private File getDiskCacheDir(Context context, String uniqueName) { String cachePath; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) { cachePath = context.getExternalCacheDir().getPath(); } else { cachePath = context.getCacheDir().getPath(); } return new File(cachePath + File.separator + uniqueName); } private int getAppVersion(Context context) { try { PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); return info.versionCode; } catch (NameNotFoundException e) { e.printStackTrace(); } return 1; } private String hashKeyForDisk(String key) { String cacheKey; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(key.getBytes()); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(key.hashCode()); } return cacheKey; } private String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } }
代码地址:https://github.com/lesliebeijing/AsyncImageLoader.git
如果使用Volley就简单的多了同一个URL请求的重复发送,退出activity后队列中请求的 cancel,(上面的demo没有处理这两种情况)图片的缓存等都不用自己处理了,Volley都封装好了。
Volley ListView异步加载图片demo:https://github.com/lesliebeijing/VolleyListViewImageDemo.git
- AsyncImageLoader-master.zip (764.8 KB)
- 下载次数: 1
- VolleyListViewImageDemo-master.zip (601.8 KB)
- 下载次数: 0
发表评论
-
带你深入理解 FLUTTER 中的字体“冷”知识
2020-08-10 23:40 565本篇将带你深入理解 Flutter 开发过程中关于字体和文 ... -
Flutter -自定义日历组件
2020-03-01 17:56 1021颜色文件和屏幕适配的文件 可以自己给定 import ... -
Dart高级(一)——泛型与Json To Bean
2020-02-23 19:13 923从 Flutter 发布到现在, 越来越多人开始尝试使用 Da ... -
flutter loading、Progress进度条
2020-02-21 17:03 1029Flutter Progress 1 条形无固定值进度条 ... -
Flutter使用Https加载图片
2020-02-21 01:39 898Flutter使用Https加载图片 使用http加载图片出 ... -
flutter shared_preferences 异步变同步
2020-02-21 00:55 762前言 引用 在开发原生iOS或Native应用时,一般有判断上 ... -
Flutter TextField边框颜色
2020-02-19 21:31 849监听要销毁 myController.dispose(); T ... -
flutter Future的正确用法
2020-02-18 21:55 725在flutter中经常会用到异步任务,dart中异步任务异步处 ... -
记一次Flutter简单粗暴处理HTTPS证书检验方法
2020-02-18 14:13 821最近在做Flutter项目到了遇到一个无解的事情,当使用Ima ... -
flutter 获取屏幕宽度高度 通知栏高度等屏幕信息
2019-07-27 08:39 1221##MediaQuery MediaQuery.of(con ... -
关于flutter RefreshIndicator扩展listview下拉刷新的问题
2019-07-10 19:40 994当条目过少时listview某些嵌套情况下可能不会滚动(条目 ... -
flutter listview 改变状态的时候一直无限添加
2019-07-10 16:01 671setstate的时候会一直无限的调用listview.bui ... -
Flutter Android端启动白屏问题的解决
2019-07-09 00:51 1399问题描述 Flutter 应用在 Android 端上启动时 ... -
Flutter中SnackBar使用
2019-07-08 23:43 676底部弹出,然后在指定时间后消失。 注意: build(Bui ... -
Flutter 之点击空白区域收起键盘
2019-07-08 18:43 1656点击空白处取消TextField焦点这个需求是非常简单的,在学 ... -
Flutter 弹窗 Dialog ,AlertDialog,IOS风格
2019-07-08 18:04 1287import 'package:flutter/mate ... -
flutter ---TextField 之 输入类型、长度限制
2019-07-08 14:30 2196TextField想要实现输入类型、长度限制需要先引入impo ... -
【flutter 溢出BUG】键盘上显示bottom overflowed by 104 PIXELS
2019-07-08 11:13 1442一开始直接使用Scaffold布局,body:new Colu ... -
解决Flutter项目卡在Initializing gradle...界面的问题
2019-07-07 12:53 787Flutter最近很火,我抽出了一点时间对Flutter进行了 ... -
关于android O 上 NotificationChannel 的一些注意事项
2019-07-04 11:47 884最近在适配android O,遇到个问题,应用中原本有设置界面 ...
相关推荐
Android Listview异步加载图片,图片错位解决方案
Android 异步加载图片,对ListView的异步加载图片的功能演示,主要根据url读取图片返回流的方法。为了方便演示,将请求图片的链接先固定,每读取好一个图片就更新,界面比较简单,当然你可以做成比较好的,像很多好...
android listview异步加载图片实例 用到了线程池 下载的图片会保存到本地 并在数据库中保留记录 再次加载时会直接从本地读取
android中ListView异步加载图片时的图片错位问题解决方案
Android实现ListView异步加载图片
NULL 博文链接:https://zjingye.iteye.com/blog/1936268
android中ListView异步加载图片时的图片错位问题解决方案
简洁 明确 listview异步加载图片
Android ListView 异步加载图片,一点也不卡,使用AsyncTask和WeakReference,注释详尽
android listView图片异步加载(拖动时不加载,双缓存)
android listview 异步加载网络图片
AystnPicture_Android ListView异步加载图片.rar
Android ListView异步加载图片,优化滚动效果,不卡顿、流畅显示。主要给新人了解ListView和AsyncTask、Json等使用。
AndroidListView异步加载图片乱序问题,原因分析及解决方案.docx
本文实例讲述了Android实现Listview异步加载网络图片并动态更新的方法。分享给大家供大家参考,具体如下: 应用实例:解析后台返回的数据,把每条都显示在ListView中,包括活动图片、店名、活动详情、地址、电话和...
android ListView如果从网络加载图片,直接getView,肯定会阻塞UI导致滑动很不流畅,本实例优化了getView中图片的获取。