【Android】ListView、RecyclerView异步加载图片引起错位问题
今天在RecyclerView列表里遇到一个情况,它包含300条数据,每项包含一个图片,发现在首次载入时,由于本地没图,请求网络的时候;快速滑动导致了图片错位、闪烁的问题。
下面是讲讲实际使用中,是怎么解决错位的问题。
一般错位都是发生在耗时的http请求上面,因此,针对每次图片请求
发起前
1:先将图片预设为本地一个占位图片。(重要!很多错位情况在于复用了其他位置的图片缓存,而当前图片迟迟加载不出来,导致当前图片错位。所以解决之道是先用本地占位图片,快速刷新当前图片。等加载完毕之后,就可以替换掉占位图片了。)
2:通过ImageView.setTag,把url记录在图片内部。
3:把url放进一个请求队列,(这是避免图片很大,请求耗时很长,重复发起url请求)
发起中
1:如果请求队列存在url,则将老的url对应图片控件,替换为本次请求图片控件,返回
发起后
如果当前url和第一步ImageView.getTag一致,则用新的数据源更新图片。
否则返回。
如果是重复使用的图片,最好就是访问之后,写入到本地存储上,避免耗时网络请求。
贴上优化过的图片功能类,供各位参考
package zhexian.app.smartcall.lib; import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.os.AsyncTask;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView; import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap; import zhexian.app.smartcall.R;
import zhexian.app.smartcall.base.BaseApplication;
import zhexian.app.smartcall.tools.Utils; public class ZImage { /**
* 超过就不走内存缓存了。可以走磁盘缓存
*/
private static final int MAX_CACHED_IMAGE_SIZE = 200 * 1024;
private static final int CACHED_MEMORY_SIZE = 20 * 1024 * 1024;
private static ZImage mZImage;
LruCache<String, Bitmap> mMemoryCache;
private HashMap<String, ImageView> imageRequestList; private BaseApplication mApp;
private Point screenSize;
private Bitmap placeHolderBitmap; public ZImage(Activity activity) {
mApp = (BaseApplication) activity.getApplication();
screenSize = new Point();
placeHolderBitmap = BitmapFactory.decodeResource(activity.getResources(), R.drawable.user_default);
imageRequestList = new HashMap<>(); mMemoryCache = new LruCache<String, Bitmap>(CACHED_MEMORY_SIZE) {
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}
};
} public static void Init(Activity activity) {
if (mZImage == null) synchronized (ZImage.class) {
if (mZImage == null)
mZImage = new ZImage(activity);
}
} public static ZImage getInstance() {
if (mZImage == null) {
Log.d("图片模块未初始化", "调用Init来初始化");
}
return mZImage;
} public void loadEmpty(ImageView imageView) {
imageView.setImageBitmap(placeHolderBitmap);
} /**
* 加载图片到内存中
*
* @param url 地址
* @param imageView 图片
* @param width 宽度
* @param height 高度
* @param isCache 是否缓存到磁盘与内存中
* @param canQueryHttp 如果找不到本地文件,是否可以通过网络获取
*/
public void load(String url, ImageView imageView, int width, int height, boolean isCache, boolean canQueryHttp) {
//L1 内存
Bitmap bitmap = getFromMemoryCache(url); if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
} loadEmpty(imageView); //L2 磁盘
String cachedUrl = ZString.getFileCachedDir(url, mApp.getFilePath()); if (ZIO.isExist(cachedUrl)) {
bitmap = Utils.getScaledBitMap(cachedUrl, width, height); if (bitmap != null) {
imageView.setImageBitmap(bitmap); if (isCache)
putToMemoryCache(url, bitmap); return;
}
}
//L3 网络
if (canQueryHttp) { if (imageRequestList.containsKey(url)) {
ImageView oldImageView = imageRequestList.remove(url);
oldImageView.setTag("");
imageView.setTag(url);
imageRequestList.put(url, imageView);
return;
} imageView.setTag(url);
imageRequestList.put(url, imageView);
new ImageLoadTask().execute(new ImageEntity(url, imageView, width, height, isCache));
} else
loadEmpty(imageView);
} public void saveToCache(String url) {
saveToCache(url, screenSize.x, screenSize.y);
} public void saveToCache(String url, int width, int height) {
String cachedUrl = ZString.getFileCachedDir(url, mApp.getFilePath()); if (ZIO.isExist(cachedUrl) == false)
new ImageSaveCacheTask().execute(new ImageCachedEntity(url, cachedUrl, width, height));
} public void saveToCache(Bitmap bitmap, String url) {
String cachedUrl = ZString.getFileCachedDir(url, mApp.getFilePath());
saveBitmapToCache(bitmap, cachedUrl);
} private void saveBitmapToCache(Bitmap bitmap, String cachedUrl) {
ZIO.createFile(cachedUrl);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cachedUrl);
Bitmap.CompressFormat format = cachedUrl.toLowerCase().indexOf("jpeg") > 0 ? Bitmap.CompressFormat.JPEG : Bitmap.CompressFormat.PNG; if (fos != null && bitmap != null && bitmap.getByteCount() > 0) {
bitmap.compress(format, 100, fos);
fos.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} Bitmap getFromMemoryCache(String url) {
return mMemoryCache.get(url);
} void putToMemoryCache(String url, Bitmap bitmap) {
if (bitmap != null && bitmap.getByteCount() < MAX_CACHED_IMAGE_SIZE)
mMemoryCache.put(url, bitmap);
} class ImageCachedEntity {
String url;
String cachedUrl;
int width;
int height; public ImageCachedEntity(String url, String cachedUrl, int width, int height) {
this.url = url;
this.cachedUrl = cachedUrl;
this.width = width;
this.height = height;
}
} class ImageEntity {
String url;
ImageView imageView;
int width;
int height;
boolean isCache; public ImageEntity(String url, ImageView imageView, int width, int height, boolean isCache) {
this.url = url;
this.imageView = imageView;
this.width = width;
this.height = height;
this.isCache = isCache;
}
} class ImageSaveCacheTask extends AsyncTask<ImageCachedEntity, Void, Bitmap> {
ImageCachedEntity entity; @Override
protected Bitmap doInBackground(ImageCachedEntity... imageEntities) {
entity = imageEntities[0];
return ZHttp.getBitmap(entity.url, entity.width, entity.height);
} @Override
protected void onPostExecute(Bitmap bitmap) {
saveBitmapToCache(bitmap, entity.cachedUrl);
}
} class ImageLoadTask extends AsyncTask<ImageEntity, Void, Bitmap> {
String url;
boolean isCache = false; @Override
protected Bitmap doInBackground(ImageEntity... imageEntities) {
ImageEntity imageEntity = imageEntities[0];
url = imageEntity.url;
isCache = imageEntity.isCache;
return ZHttp.getBitmap(imageEntity.url, imageEntity.width, imageEntity.height);
} @Override
protected void onPostExecute(Bitmap bitmap) {
ImageView imageView = imageRequestList.remove(url);
String originalUrl = (String) imageView.getTag(); if (originalUrl != null && originalUrl.equals(url))
imageView.setImageBitmap(bitmap); if (isCache) {
saveToCache(bitmap, url); if (getFromMemoryCache(url) == null)
putToMemoryCache(url, bitmap);
}
}
}
}
【Android】ListView、RecyclerView异步加载图片引起错位问题的更多相关文章
- android Listview 软引用SoftReference异步加载图片
首先说一下,android系统加载大量图片系统内存溢出的3中解决方法: (1)从网络或本地加载图片的时候,只加载缩略图.这个方法的确能够少占用不少内存,可是它的致命的缺点就是,因为加载的是缩略图,所以 ...
- android listview 异步加载图片并防止错位
网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertVie ...
- Android学习笔记(二)之异步加载图片
最近在android开发中碰到比较棘手的问题,就是加载图片内存溢出.我开发的是一个新闻应用,应用中用到大量的图片,一个界面中可能会有上百张图片.开发android应用的朋友可能或多或少碰到加载图片内存 ...
- listview异步加载图片并防止错位
android listview 异步加载图片并防止错位 网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 conver ...
- Listview 异步加载图片之优化篇(有图有码有解释)
在APP应用中,listview的异步加载图片方式能够带来很好的用户体验,同时也是考量程序性能的一个重要指标.关于listview的异步加载,网上其实很多示例了,中心思想都差不多,不过很多版本或是有b ...
- Listview异步加载图片之优化篇
在APP应用中,listview的异步加载图片方式能够带来很好的用户体验,同时也是考量程序性能的一个重要指标.关于listview的异步加载,网上其实很多示例了,中心思想都差不多,不过很多版本或是有b ...
- 软引用SoftReference异步加载图片
HashMap<String, SoftReference<Drawable>> imageCache 关于SoftReference这个类多少知道些机制,会用就ok了. 机制 ...
- Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案
我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如L ...
- Android 实现ListView异步加载图片
ListView异步加载图片是非常实用的方法,凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; ...
随机推荐
- 配置gosublime
Installation Sublime Package Control allows you to easily install or remove GoSublime (and many othe ...
- hdu 2234(IDA*)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2234 思路:IDA*可以搞,借鉴的是大牛的启发式函数h(): 可以考虑把每一行上的数转化成相同的,或者 ...
- C# 导出Excel "正在中止线程" 错误
导出Excel相信很多人都用过,但是我却遇到了一个问题 “正在中止线程” 源代码如下: public static void ExportExcel(string fileName, GridView ...
- CPU GPU设计工作原理《转》
我知道这非常长,可是,我坚持看完了.希望有幸看到这文章并对图形方面有兴趣的朋友,也能坚持看完.一定大有收获.毕竟知道它们究竟是怎么"私下勾搭"的.会有利于我们用程序来指挥它们... ...
- 如何隐藏js
前端好像一直会遇到js容易被查看的问题,针对这种情况,如何隐藏js呢? 突发奇想,想到一个办法,如果说一段js只需要执行一次的话 可以尝试在所有js加载操作完毕后把它去掉.看代码 <!DOCTY ...
- 在 Ubuntu Mate 16.04 上通过 PPA 升级 Mate 1.14
导读 Mate 桌面环境 1.14 现在可以在 Ubuntu Mate 16.04 ("Xenial Xerus") 上使用了.根据这个版本的描述,为了全面测试 Mate 1.14 ...
- Android之dip、dp、px、sp和屏幕密度
1. dip: device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA.HVGA和QVGA 推荐使用这 这个,不依 ...
- Math.max得到数组中最大值
Math.max(param1,param2) 因为参数不支持数组. 所以可以根据apply的特点来解决, var max = Math.max.apply(null,array),这样就可以轻易的得 ...
- 深搜———ZOJ 1004:anagrams by stack
细节问题各种虐!! 其实就是简单的一个深搜 看成二叉树来理解:每个节点有两个枝:入栈和出栈. 剪枝操作:只有当栈顶元素和当前位置的目标字符相同时才出栈,否则就不出栈 dfs写三个参数:depth搜索深 ...
- GIF动画录制工具(写教程时用的比较小巧的gif工具)
1 软件小巧实用,只有1m 2 gif效果还可以 3 绿色,无需安装 很多地方能下载,百度就行. 下载地址: http://www.downxia.com/downinfo/41427.html