高级UI

如何通过BitmapFactory.Options优化图片大小

2019-11-08  本文已影响0人  超级绿茶

在Android开发中常用的图片显示控件是ImageView对象,此对象提供有一系列直接加载图片的方法,但实际开发时一般只会直接用到setImageResource方法,因为这个方法是从资源ID中加载图片,而资源ID极少会用到大尺寸的图片。而其它方法在遇到加载大图片时都会遇到OOM(内存溢出)异常。

通常的作法是先获取一个优化过的Bitmap对象或Drawable对象,然后再交由ImageView对象显示。

要获取一个Bitmap对象最直接的办法是通过BitmapFactory提供的几个静态方法获取:

这些方法会根据图片来源的不同而需要传入不同的参数,并且这些方法都支持一个类型为BitmapFactory.Options的对象,而我们要对Bitmap作优化就要用到这个参数。

// 如果不传Options参数一样可以加载图片
// 例如可以从assets目录加载一张图片
val inputStream = assets.open("sample.jpg")
val bmp = BitmapFactory.decodeStream(inputStream)
ivImage.setImageBitmap(bmp)

上面的例子演示了不用Options参数时的用法,这种方式在遇到大尺寸的图片仍旧有可能造成OOM异常。那接下来就说说BitmapFactory.Options的一些用法;

BitmapFactory.Options主要的作用是设置一套图片选项用于图片的加载。因此我们可以通过优化这套选项来对图片的加载进行优化,从而达到节省资源避免OOM的发现目的。首先我们需要对Options的几重要属性讲解下:

根据上述介绍的几个属性,我们的优化步骤大致如下:

  1. 实例化一个Options对象,并将inJustDecodeBounds设为true。
  2. 通过BitmapFactory加载图片,此时可以通过Options获取到图片的原始宽高。
  3. 根据控件的宽高和图片的原始宽高计算出采样率的值。
  4. 将采样率赋给inSampleSize,将inJustDecodeBounds设为false再次加载图片。

我们可以将上述步骤封装成一个工具类以便于调用:

object MyBitmapUtils {
    const val TAG = "MyBitmapUtils"
    /**
     * 根据指定的宽高获取图片的采样率
     * @param inputStream 通过输入流加载图片
     * @param width 图片控件的宽
     * @param height 图片控件的高
     */
    fun calcSampleSize(inputStream: InputStream, width: Int, height: Int): Int {
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeStream(inputStream, null, options) //返回值始终为null
        inputStream.reset() // 重置输入流的指针
        val imgWidth = options.outWidth
        val imgHeight = options.outHeight
        Log.i(TAG, "original size $imgWidth X $imgHeight")
        return if (width > 0 && height > 0) {
            // 计算出原始图片宽高和目标宽高的比率
            val heightRatio = imgHeight / height
            val widthRatio = imgWidth / width
            if (heightRatio < widthRatio) heightRatio else widthRatio
        } else 1
    }

    /**
     * 根据获取到的采样率二次加载图片并获取Bitmap
     */
    fun loadBitmap(inputStream: InputStream, width: Int, height: Int): Bitmap? {
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = false
        options.inSampleSize = calcSampleSize(inputStream, width, height)
        Log.i(TAG, "${options.inSampleSize}")
        return BitmapFactory.decodeStream(inputStream, null, options)
    }
}

在MainActivity中如下实现:

ivImage.doOnPreDraw {
     val inputStream = assets.open("bigPicture.jpg")
     // 在ImageView加载到布局后获取宽高
     val width = ivImage.width
     val height = ivImage.height
     val bmp = MyBitmapUtils.loadBitmap(inputStream, width, height)
     ivImage.setImageBitmap(bmp)
}

下载源代码:https://t00y.com/file/22686471-408583230

点击链接加入群聊【口袋里的安卓】:https://jq.qq.com/?_wv=1027&k=5z4fzdT

上一篇下一篇

猜你喜欢

热点阅读