Android 图片如何高效加载与缓存 (4.5) —— 缩略图生成的优化

在之前那篇里面仍有不足之处,比如:

一张4000*3000的图片压缩到了300*200的大小,然后输出到ImageView上。但是ImageView的大小可能只有100*100,这样我们仍然占用了很多不需要的内存。所以我们只需要将图片居中裁剪成ImageView的大小即可。

知道了如何做之后,在考虑一下可能存在的图片尺寸与ImageView尺寸之间的关系:

1.图片的宽 小于 ImageView的宽 (比如:5000*10)
2.图片的高 小于 ImageView的高 (比如:10*5000)
3.图片的宽和高都 小于 ImageView的宽高 (比如:1*1)

考虑好了之后,我们就能动手写代码了!

先来个大合照,然后我在把裁剪的方法一一拿出来说吧 = n =

    /*
    获取图片缩略图任务
     */
    private class GenerateImageThumbnail implements Callable<String>{

        private int scaleTimes;  //缩放倍数
        private String path;  //图片路径
        private String tag;  //图片加载任务唯一TAG
        private ImageView imageView;  //图片接收的ImageView
        private Bitmap bitmap;  //图片的Bitmap对象
        private HandleOnLoaded handleOnLoaded;  //图片效果处理回调

        public GenerateImageThumbnail(String path, String tag, ImageView imageView, HandleOnLoaded handleOnLoaded) {
            this.path = path;
            this.tag = tag;
            this.imageView = imageView;
            this.handleOnLoaded = handleOnLoaded;
        }

        @Override
        public String call() throws Exception {

            try {
                //进行图片剪裁
                bitmap = cropBitmap();
            } catch (IllegalArgumentException e) {
                //如果裁剪出现了异常
                fetherExecutor.removeTag(tag);
                Log.d("OCImageLoader", "Exception on croping bitmap. "+e);
                runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        onError();
                    }
                });
                return null;
            }

            if (handleOnLoaded != null){
                //如果有需要进行图片处理,则使用回调处理
                bitmap = handleOnLoaded.reduce(bitmap,tag);
            }

            if (bitmap != null){
                //图片进行缓存
                imageCacher.putCache(tag,bitmap);
                runOnUIThread(new Runnable() {
                    @Override
                    public void run() {
                        onCompleted(imageView , bitmap);
                    }
                });
            }
            fetherExecutor.removeTag(tag);

            return null;
        }

        //加载完成的操作
        private void onCompleted(ImageView imageView , Bitmap bitmap){
            ...
        }

        //加载失败的操作
        private void onError(){
            ...
        }

        /**
         * 进行 Bitmap 的裁剪缩放操作
         * @return  处理后的Bitmap
         */
        private Bitmap cropBitmap() throws IllegalArgumentException{
            ...
        }

        /**
         * 普通裁剪
         * @return  裁剪后的Bitmap
         */
        private Bitmap normalCropBitmap(int pictureWidth , int pictureHeight , int showHeight , int showWidth , BitmapFactory.Options options)
        throws IllegalArgumentException{
            ...
        }

        /**
         * 高度裁剪
         * @return  裁剪后的Bitmap
         */
        private Bitmap heightCropBitmap(int pictureWidth , int pictureHeight , int showHeight , BitmapFactory.Options options)
        throws IllegalArgumentException{
            ...
        }

        /**
         * 长度裁剪
         * @return  裁剪后的Bitmap
         */
        private Bitmap widthCropBitmap(int pictureWidth , int pictureHeight , int showWidth , BitmapFactory.Options options)
        throws IllegalArgumentException{
            ...
        }
    }

方法 cropBitmap() 用于初始化数据 以及判断应该如何裁剪

private Bitmap cropBitmap() throws IllegalArgumentException{
            //默认起始缩放倍数
            scaleTimes = 2;

            //先仅仅加载图片的尺寸数据
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path,options);

            //裁剪方式
            int cropType;

            //原图尺寸
            int pictureWidth = options.outWidth;
            int pictureHeight = options.outHeight;

            //ImageView尺寸. 先尽可能缩放成这个尺寸
            int showWidth = imageView.getMeasuredWidth();
            int showHeight = imageView.getMeasuredHeight();

            if (showHeight * showWidth == 0){
                //如果有一项ImageView尺寸的数据为 0 , 则赋予默认值. 裁剪为普通状态
                showHeight = 120;
                showWidth = 120;
                cropType = 0;
            }else if ( pictureHeight < showHeight && pictureWidth < showWidth ){
                //如果原图小于要显示的尺寸,则直接返回原图对象
                return BitmapFactory.decodeFile(path);
            }else if ( pictureHeight < showHeight ){
                //如果原图的高度小于要显示的尺寸,就直接将  原图的长度  进行裁剪
                cropType = 1;
            }else if ( pictureWidth < showWidth ){
                //如果原图的宽度小于要显示的尺寸,就直接将  原图的高度  进行裁剪
                cropType = 2;
            }else {
                //普通状态
                cropType = 0;
            }

            switch (cropType){
                case 0:
                    //普通裁剪
                    bitmap = normalCropBitmap(pictureWidth,pictureHeight,showHeight,showWidth,options);
                    break;
                case 1:
                    //长度裁剪
                    bitmap = widthCropBitmap(pictureWidth,pictureHeight,showWidth,options);
                    break;
                case 2:
                    //高度裁剪
                    bitmap = heightCropBitmap(pictureWidth,pictureHeight,showHeight,options);
                    break;
                default:
                    bitmap = normalCropBitmap(pictureWidth,pictureHeight,showHeight,showWidth,options);
                    break;
            }

            return bitmap;
        }

方法 normalCropBitmap() 默认裁剪方式

/**
         * 普通裁剪
         * @return  裁剪后的Bitmap
         */
        private Bitmap normalCropBitmap(int pictureWidth , int pictureHeight , int showHeight , int showWidth , BitmapFactory.Options options)
        throws IllegalArgumentException{

            //临时存储计算得到的上一次结果,预设为图片原始尺寸.
            int reducedWidth = pictureWidth;
            int reducedHeight = pictureHeight;

            int lastWidth;
            int lastHeight;
            while (true){

                lastWidth = pictureWidth/scaleTimes;
                lastHeight = pictureHeight/scaleTimes;

                if ( lastHeight < showHeight || lastWidth < showWidth){
                    //如果计算得到的尺寸小于要得到的尺寸,则跳出
                    break;
                }else {
                    //否则就继续增大缩放倍数,同时记录这次的尺寸
                    reducedWidth = lastWidth;
                    reducedHeight = lastHeight;
                    scaleTimes += 1;
                }

            }

            //用得出的尺寸数据,计算出缩放倍数,然后读取图片
            options.inJustDecodeBounds = false;
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            options.inSampleSize = (pictureHeight / reducedHeight + pictureWidth / reducedWidth) /2;

            //获取等待截取的Bitmap
            Bitmap pictureBitmap = BitmapFactory.decodeFile(path,options);

            //计算裁剪的起点
            int cropX , cropY;
            cropX = (reducedWidth/2)-(showWidth/2);
            cropY = (reducedHeight/2)-(showHeight/2);

            //剪!
            pictureBitmap = Bitmap.createBitmap(pictureBitmap,cropX,cropY,showWidth,showHeight);

            return pictureBitmap;
        }

方法 heightCropBitmap() 高度的裁剪

        /**
         * 高度裁剪
         * @return  裁剪后的Bitmap
         */
        private Bitmap heightCropBitmap(int pictureWidth , int pictureHeight , int showHeight , BitmapFactory.Options options)
        throws IllegalArgumentException{
            //图片不用缩放了,计算下裁剪的居中位置,就能直接裁剪了.下一个方法也一样.
            int cropY = ( pictureHeight / 2 ) - ( showHeight / 2 );
            options.inJustDecodeBounds = false;
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            bitmap = BitmapFactory.decodeFile(path,options);
            bitmap = Bitmap.createBitmap(bitmap,0,cropY,pictureWidth,showHeight);
            return bitmap;
        }

方法 widthCropBitmap() 宽度的裁剪

        /**
         * 宽度裁剪
         * @return  裁剪后的Bitmap
         */
        private Bitmap widthCropBitmap(int pictureWidth , int pictureHeight , int showWidth , BitmapFactory.Options options)
        throws IllegalArgumentException{
            int cropX = ( pictureWidth / 2 ) - ( showWidth / 2 );
            options.inJustDecodeBounds = false;
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            bitmap = BitmapFactory.decodeFile(path,options);
            bitmap = Bitmap.createBitmap(bitmap,cropX,0,showWidth,pictureHeight);
            return bitmap;
        }

下面是正常向的图片处理之后的Log 可以看到无论图片尺寸是如何的,处理后的大小都是统一的
Android 图片如何高效加载与缓存 (4.5) —— 缩略图生成的优化_第1张图片

下面这是特殊情况的log
Android 图片如何高效加载与缓存 (4.5) —— 缩略图生成的优化_第2张图片

你可能感兴趣的:(Android,android,图片,缓存,优化,压缩)