美文网首页高级UI
如何通过BitmapFactory.Options优化图片大小

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

作者: 超级绿茶 | 来源:发表于2019-11-08 21:08 被阅读0次

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

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

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

    • decodeFile 通过文件加载图片
    • decodeByteArray 通过二进制数组加载图片
    • decodeResource 通过资源ID加载图片
    • decodeStream 通过输入流加载图片

    这些方法会根据图片来源的不同而需要传入不同的参数,并且这些方法都支持一个类型为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的几重要属性讲解下:

    • inJustDecodeBounds:解码图片尺寸。默认值为false,当设为true的时候只会加载图片的尺寸而不实际加载图片,获取到的图片宽高会存入outWidth和outHeight里面。

    • inSampleSize:图片采样率。类型为Int,默认值是1,表示图片按一比一的原始尺寸加载,如果设为2的话图片就会按原始尺寸的1/2加载,设为4就按1/4的尺寸加载。该值如果小于1则按1处理。

    • inPreferredConfig:设置色彩模式。默认值是ARGB_8888每个像素占4个字节,像素带透明度。这个属于有多个取值范围:ALPHA_8每个像素占1个字节。RGB_565每个像素占2个字节。ARGB_4444每个像素占2个字节,像素带透明度,又不推荐使用。RGBA_F16每个像素占8个字节,带透明度。HAREWARE像素存储在显存中,被称为硬件位图,是Android8.0加入的新功能,使用这个参数必须开启硬件加速。

    • inDither:解码抖动。默认值为false,设为true的话会对一些低清的图片提升画质,但也占用了更多的资源。

    • inPreferQualityOverSpeed:是否使得高品质的JPG,默认为false,这个参数主要是针对JPG文件,设置为true后会提高画质降低解码速度。

    • outWidth和outHeight:图片加载后的宽和高,当图片加载完毕图片的宽高值就会填充到这两个属性。

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

    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

    相关文章

      网友评论

        本文标题:如何通过BitmapFactory.Options优化图片大小

        本文链接:https://www.haomeiwen.com/subject/zogwbctx.html