[译]Android调整图像大小的一些方法
https://stackoverflow.com/questions/32121058/most-memory-efficient-way-to-resize-bitmaps-on-android
推荐使用bilibili开源的boxing,旋转图片的代码来源于此。

1. 图片方向和压缩

场景一:
加载一张相册中的bitmap,发现bitmap方向不对,发生了偏转。这是因为系统相机角度问题,需要对bitmap进行旋转。

场景二:
加载一张相册中的bitmap,部分手机上加载不出来。这是因为原始图片分辨率太大,部分手机不支持这么高的内存占用。需要缩放bitmap后才能正常加载。

场景三:
后台要求我们上传的图片分辨率是480X800,并且文件大小不能超过300K。同样需要对bitmap进行缩放,然后判断容量大小后再进行质量缩放。

另外,通过setImageUri()方式设置图片时,内部会自动做旋转和缩放动作。

2. 示例代码

2.1 旋转照片

先获取照片的偏转角度

    /**     * 获取图片的旋转角度。     * 只能通过原始文件获取,如果已经进行过bitmap操作无法获取。     */    private static int getRotateDegree(String path) {        int result = 0;        try {            ExifInterface exif = new ExifInterface(path);            int orientation = exif.getAttributeInt(                    ExifInterface.TAG_ORIENTATION,                    ExifInterface.ORIENTATION_NORMAL);            switch (orientation) {                case ExifInterface.ORIENTATION_ROTATE_90:                    result = 90;                    break;                case ExifInterface.ORIENTATION_ROTATE_180:                    result = 180;                    break;                case ExifInterface.ORIENTATION_ROTATE_270:                    result = 270;                    break;            }        } catch (IOException ignore) {            return 0;        }        return result;    }

之后就是对bitmap的旋转,如:

        // 获取图片旋转角度,旋转图片        int degree = getRotateDegree(file.getAbsolutePath());        Matrix matrix = new Matrix();        matrix.postRotate(degree);        Bitmap outBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);        bitmap.recycle(); // 释放原来的bitmap

2.2 压缩图片

bitmap太大时,无法加载或引发oom,加载图片是需要采样(inSampleSize)和缩放(inDensity,inTargetDensity)加载,节省操作bitmap时的内存。

  1. 通过传入的期望宽高参数获取缩放比例
    destWidth,destHeight可以设置其中一个值为0,即依据宽或高获取缩放值。
    /**     * 获取缩放数值。     * 取值请参考 {@link #evaluateWH(float, float, float, float)}     */    private static float getScale(float srcWidth, float srcHeight, float destWidth, float destHeight) {        int evaluateWH = evaluateWH(srcWidth, srcHeight, destWidth, destHeight);        switch (evaluateWH) {            case 0:                return destWidth / srcWidth;            case 1:                return destHeight / srcHeight;            default:                return 1f;        }    }    /**     * 评估使用宽或者高计算缩放比例     * 以最大缩放比例为准,如宽比例为 0.5, 高比例为0.8,返回宽     *     * @return 0:宽, 1:高, -1:不缩放     */    private static int evaluateWH(float srcWidth, float srcHeight, float destWidth, float destHeight) {        if (srcWidth < 1f || srcHeight < 1f || srcWidth < destWidth && srcHeight < destHeight) {            return -1;        }        int result;        if (destWidth > 0 && destHeight > 0) {            result = destWidth / srcWidth < destHeight / srcHeight ? 0 : 1;        } else if (destWidth > 0) {            result = 0;        } else if (destHeight > 0) {            result = 1;        } else {            result = -1;        }        return result;    }
  1. 加载缩放的Bitmap

这一部分推荐阅读[译]Android调整图像大小的一些方法

先根据缩放数值计算inSampleSize,inSampleSize只能是2的次幂,如1,2,4,8,16等。如果我们传入3,其实缩放倍数为4,如果传入5,则实际为8。为了防止这个数字比实际需要的大,导致采样倍数过高导致图片质量模糊,我们需要自己计算一个精确的倍数。
下面这个方法,当你传入3时,返回2, 传入5时,返回4。

    // 参考Hashmap::tableSizeFor,根据传入的缩放比例倍数,获取一个临近的2的次幂的数    private static int inSampleSizeFor(int n) {        int maxNum = 1 << 30;        n |= n >>> 1;        n |= n >>> 2;        n |= n >>> 4;        n |= n >>> 8;        n |= n >>> 16;        n = (n < 0) ? 1 : (n >= maxNum) ? maxNum : n + 1;        return n >>> 1 == 0 ? n : n >>> 1;    }

加载缩放的Bitmap

/** * 加载缩放bitmap。 * 根据期望宽高自动获取合适的缩放比例, 具体看{@link #evaluateWH(float, float, float, float)} * * @param path      图片路径 * @param maxWidth  期望宽度 * @param maxHeight 期望高度 */public static Bitmap loadScaledBitmap(String path, int maxWidth, int maxHeight) {    BitmapFactory.Options options = new BitmapFactory.Options();    options.inJustDecodeBounds = true;    BitmapFactory.decodeFile(path, options);    int srcHeight = options.outHeight;    int srcWidth = options.outWidth;    // decode失败    if (srcHeight == -1 || srcWidth == -1) {        return null;    }    // 当比例差距过大时,先通过inSampleSize加载bitmap降低内存消耗    float scale = getScale(srcWidth, srcHeight, maxWidth, maxHeight);    int evaluateWH = evaluateWH(srcWidth, srcHeight, maxWidth, maxHeight);    options.inSampleSize = inSampleSizeFor((int) (1 / scale));    options.inJustDecodeBounds = false;    if (evaluateWH == 0) {        options.inScaled = true;        options.inDensity = srcWidth;        options.inTargetDensity = maxWidth * options.inSampleSize;    } else if (evaluateWH == 1) {        options.inScaled = true;        options.inDensity = srcHeight;        options.inTargetDensity = maxHeight * options.inSampleSize;    } else {        options.inScaled = false;    }    return BitmapFactory.decodeFile(path, options);}
  1. 判断此时bitmap大小是否符合要求,否则进行进一步的质量压缩,最后将bitmap写回文件
    // 判断最终大小,循环进行质量压缩,直到满足需求        int quality = 100;        ByteArrayOutputStream baos = new ByteArrayOutputStream();        outBitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);        if (maxSize > 0) { // maxSize小于等于0表示不进行质量压缩            while (baos.size() > maxSize) {                baos.reset();                quality -= 10;                outBitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);                if (quality == 0) {                    break;                }            }        }        outBitmap.recycle();
        // 将流写回文件        FileOutputStream outputStream = null;        try {            outputStream = new FileOutputStream(file);            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);            bufferedOutputStream.write(baos.toByteArray());            bufferedOutputStream.flush();            return true;        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                baos.close();            } catch (IOException e) {                e.printStackTrace();            }            if (outputStream != null) {                try {                    outputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return false;

3. 完整的Demo

包含6.0动态权限适配,7.0 FileProvider适配。
包含调用系统相机进行拍照,调用系统相册选取图片,并对图片进行旋转、缩放、压缩等操作。
https://github.com/AItsuki/TestTakePhoto

更多相关文章

  1. Android Launcher7.0首次数据加载逻辑
  2. Android Mediaplayer error (1, -2147483648) Error (-38,0)解决
  3. Android 控件之ImageSwitcher图片切换器
  4. Android编程心得-图片自适应心得
  5. android Webview加载url空白,但浏览器能打开
  6. android WebView, WebChromeClient和WebViewClient加载网页基本
  7. Android 使用Canvas在图片上绘制文字的方法

随机推荐

  1. android progressBar 背景改变
  2. Android 蝈蝈
  3. android需要看的书
  4. 编译android 源码
  5. Android 一个简单的登录界面
  6. android ndk 编译 ffmpeg
  7. Ant 命令行创建一个Android工程
  8. Android 内存笔记
  9. ch09 Android ListView
  10. android firstslide