今天在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); } } } }