您的位置:首页 > 移动开发 > Android开发

【Android】ListView、RecyclerView异步加载图片引起错位问题

2015-06-10 14:37 489 查看
今天在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);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: