【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);
            }
        }
    }
}

 

转载于:https://www.cnblogs.com/kimmy/p/4565995.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值