Android ImageView高效加载大图

官方demo摘录:

计算inSampleSize的方法

 /**
     * 计算inSampleSize,例如, 一个分辨率为2048x1536的图片,如果设置 inSampleSize 为4,那么会产出一个大约512x384大小的Bitmap。
     * @param options
     * @param reqWidth 想要压缩到的宽度
     * @param reqHeight  想要压缩到的高度
     * @return
     */
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

        //图片资源文件的高
        final int height = options.outHeight;

        //图片资源文件的宽
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            //计算最大inSampleSize值是2的幂,并保持高度和宽度大于所要求的高度和宽度。
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth
                    ) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

获取Bitmap

/**
     * 获取Bitmap, 是根据屏幕宽高来压缩的,可自行修改
     * @param imageId 图片资源id
     * @return
     */
    private Bitmap decodeSampledBitmapFromResourse(int imageId) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        //不加载图片,只获取图片的宽高和type
        BitmapFactory.decodeResource(getResources(), imageId, options);

        //获取屏幕宽高
        DisplayMetrics metric = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metric);
        int width = metric.widthPixels;     // 屏幕宽度(像素)
        int height = metric.heightPixels;   // 屏幕高度(像素)

        options.inSampleSize = calculateInSampleSize(options, width ,height);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(getResources(), imageId, options);
    }

使用

  Bitmap bitmap = decodeSampledBitmapFromResourse(R.drawable.zhizhuxia);
        imageView.setImageBitmap(bitmap);

当然这些处理不应该放在ui线程中

是用那个AsyncTask异步处理耗时任务:

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap>{

        /**
         * 为ImageView使用WeakReference确保了AsyncTask所引用的资源可以被垃圾回收器回收。
         * 由于当任务结束时不能确保ImageView仍然存在,因此我们必须在onPostExecute()里面对引用进行检查。
         * 该ImageView在有些情况下可能已经不存在了,例如,在任务结束之前用户使用了回退操作,或者是配置发生了改变(如旋转屏幕等)。
         */
        private final WeakReference imageViewWeakReference;
        private int imageId;

        public BitmapWorkerTask(ImageView imageView){
            imageViewWeakReference = new WeakReference(imageView);
        }

        @Override
        protected Bitmap doInBackground(Integer... params) {
            imageId = params[0];

            return decodeSampledBitmapFromResourse(imageId);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (imageViewWeakReference != null && bitmap != null){
                final ImageView imageView = imageViewWeakReference.get();
                if (imageView != null){
                    imageView.setImageBitmap(bitmap);
                }
            }
        }
    }

如下使用:

BitmapWorkerTask task = new BitmapWorkerTask(imageView);
    task.execute(imageId);

为了适应多种并发操作,可做如下操作

 /**
     * 异步任务为 imageView加载大图, 适用 ListView 和 gridView ,所有方法已封装,在适当的位置调用该方法即可
     * @param imageId
     * @param imageView
     */
    public void loadBitmap(int imageId, ImageView imageView){

        if (cancelPotentialWork(imageId, imageView)){
            final BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(imageView);
            final AsyncDrawable asyncDrawable = new AsyncDrawable(bitmapWorkerTask);
            imageView.setImageDrawable(asyncDrawable);
            bitmapWorkerTask.execute(imageId);
        }
    }

    /**
     * 用来存储 BitmapWorkerTask 与之相应的 imageView 关联, 使用 {@link #getBitmapWorkerTask(ImageView)} 来获取任务
     */
    static class AsyncDrawable extends BitmapDrawable{

        private final WeakReference bitmapWorkerTaskReference;
        public AsyncDrawable(BitmapWorkerTask bitmapWorkerTask) {
            bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask);
        }
        public BitmapWorkerTask getBitmapWorkerTask(){
            return bitmapWorkerTaskReference.get();
        }
    }

    /**
     * 判定当前与imageView绑定的任务
     * @param imageId
     * @param imageView
     * @return
     */
    public static boolean cancelPotentialWork(int imageId, ImageView imageView) {

        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

        if (bitmapWorkerTask != null){
            final int bitmapId = bitmapWorkerTask.imageId;
            if (bitmapId == 0 || bitmapId != imageId){

                // 当前任务不是加载 imageId资源的异步任务,取消该任务
                bitmapWorkerTask.cancel(true);
            }else{

                // 当前任务正在加载 imageId资源,没有其他任务占用
                return false;
            }
        }

        //没有任务与 imageView 绑定
        return true;
    }

    /**
     * 获取与ImageView相关联的 BitmapWorkerTask
     * @param imageView
     * @return
     */
    private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView){
        if (imageView != null){
            final Drawable drawable = imageView.getDrawable();
            if (drawable instanceof AsyncDrawable){
                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
                return asyncDrawable.getBitmapWorkerTask();
            }
        }
        return null;
    }

    /**
     * 异步任务,为ImageView加载大图
     */
    class BitmapWorkerTask extends AsyncTask{

        /**
         * 为ImageView使用WeakReference确保了AsyncTask所引用的资源可以被垃圾回收器回收。
         * 由于当任务结束时不能确保ImageView仍然存在,因此我们必须在onPostExecute()里面对引用进行检查。
         * 该ImageView在有些情况下可能已经不存在了,例如,在任务结束之前用户使用了回退操作,或者是配置发生了改变(如旋转屏幕等)。
         */
        private final WeakReference imageViewWeakReference;
        private int imageId;

        public BitmapWorkerTask(ImageView imageView){
            imageViewWeakReference = new WeakReference(imageView);
        }

        @Override
        protected Bitmap doInBackground(Integer... params) {
            imageId = params[0];

            return decodeSampledBitmapFromResourse(imageId);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (isCancelled()){
                bitmap = null;
            }
            if (imageViewWeakReference != null && bitmap != null){
                final ImageView imageView = imageViewWeakReference.get();
                final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
                if (this == bitmapWorkerTask && imageView != null){
                    imageView.setImageBitmap(bitmap);
                }
            }
        }
    }

使用内存缓存存储已处理的图片(Api 12及以上 )

private LruCache mMemoryCache;

//在onCreate中获取缓存空间
/** 内存缓存 **/
        //获取最大可用的vm内存,超过这个值就会抛出内存溢出异常。
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        //使用1/8 maxMemory来作为缓存空间
        final int cacheSize = maxMemory / 8;

        mMemoryCache = new LruCache(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {

                //最低api 12,以kb为单位
                return value.getByteCount() / 1024;
            }

        };

从缓存添加和获取bitmap方法:

/**
     * 添加 bitmap到缓存中
     * @param key
     * @param bitmap
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap){

        if (getBitmapFromLruMemCache(key) == null){
            mMemoryCache.put(key, bitmap);
        }
    }


    /**
     * 从缓存中获取 Bitmap
     * @param key
     * @return
     */
    public Bitmap getBitmapFromLruMemCache(String key){
        return mMemoryCache.get(key);
    }

修改BitmapWorkerTask

class BitmapWorkerTask extends AsyncTask {
    ...

    @Override
    protected Bitmap doInBackground(Integer... params) {
        imageId = params[0];
            final Bitmap bitmap = decodeSampledBitmapFromResourse(imageId);

            //将处理后的bitmap添加到缓存中
            addBitmapToMemoryCache(String.valueOf(imageId), bitmap);
            return bitmap;
    }
    ...
}

改进loadBitmap方法:

 /**
     * 异步任务为 imageView加载大图, 适用 ListView 和 gridView
     * @param imageId
     * @param imageView
     */
    public void loadBitmap(int imageId, ImageView imageView){

        //首先在缓存中查找
        final String imgKey = String.valueOf(imageId);
        final Bitmap bitmap = getBitmapFromLruMemCache(imgKey);
        if (bitmap != null){
            imageView.setImageBitmap(bitmap);
        }else if (cancelPotentialWork(imageId, imageView)){
            final BitmapWorkerTask bitmapWorkerTask = new BitmapWorkerTask(imageView);
            final AsyncDrawable asyncDrawable = new AsyncDrawable(bitmapWorkerTask);
            imageView.setImageDrawable(asyncDrawable);
            bitmapWorkerTask.execute(imageId);
        }
    }

缓存bitmap是个重点,有待继续研究。

你可能感兴趣的:(Android图像和动画处理)