• 【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);
                }
            }
        }
    }
  • 相关阅读:
    struts1——静态ActionForm与动态ActionForm
    【入门篇】ANDROID开发之BUG专讲
    oracle undo 复杂度--oracle核心技术读书笔记四
    linux高级技巧:rsync同步(二)
    【c语言】数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字
    一维动态数组和二维动态数组的创建和使用
    HDU 1788 Chinese remainder theorem again 中国剩余定理
    直接选择排序
    使用enca进行字符集转码
    拒绝switch,程序加速之函数指针数组
  • 原文地址:https://www.cnblogs.com/kimmy/p/4565995.html
Copyright © 2020-2023  润新知