BitmapFactory.Options
按需缩放加载这是最常用的大图加载优化方案,通过降低采样率(inSampleSize
)来减少内存占用。
关键在于 BitmapFactory.Options
类的两个属性:
inJustDecodeBounds = true
true
时,调用 BitmapFactory.decode...()
方法并不会真正解码图片、分配内存,而是只会解析图片的元数据(如宽度、高度、MIME 类型)。inSampleSize
N
表示在解码时,水平和垂直方向上每 N
个像素点中只取一个点进行采样。Bitmap
对象的宽高约为原始的 1/N
,占用的内存则约为原始的 1/N²
。注意:为了获得最佳性能,解码器建议此值始终为 2 的幂(如 1, 2, 4, 8)。整个计算和加载过程分为三步:
第一步:仅解码边界,获取原始尺寸
将 inJustDecodeBounds
设置为 true
,然后调用 BitmapFactory.decode...()
。执行后,从 options
对象中即可获取图片的原始宽高 outWidth
和 outHeight
。
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeResource(resources, R.id.my_image, options)
val imageWidth = options.outWidth
val imageHeight = options.outHeight
第二步:计算最佳 inSampleSize
根据获取到的原始宽高和目标 View
的要求宽高(reqWidth
, reqHeight
),计算出一个合适的 inSampleSize
。
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
// 获取原始图片的宽高
val height = options.outHeight
val width = options.outWidth
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight = height / 2
val halfWidth = width / 2
// 计算最大的 inSampleSize,要求其为 2 的幂,
// 并且缩放后的宽高都大于等于目标的 reqWidth 和 reqHeight。
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
第三步:使用 inSampleSize
加载缩放后的图片
将 inJustDecodeBounds
设回 false
,并设置上一步计算出的 inSampleSize
值。再次调用 BitmapFactory.decode...()
方法,即可得到一个大小合适、内存占用小的 Bitmap
对象。
options.inSampleSize = calculateInSampleSize(options, 100, 100) // 假设目标宽高为 100x100
options.inJustDecodeBounds = false
val scaledBitmap = BitmapFactory.decodeResource(resources, R.id.my_image, options)
BitmapRegionDecoder
加载局部高清图当需要显示一张巨图(如世界地图、清明上河图)的局部时,此方案是最佳选择。
适用场景
核心原理
BitmapRegionDecoder
允许你只解码指定矩形区域(Rect
)内的图像内容,并将其加载为 Bitmap
,而完全无需将整张巨图加载到内存中。Bitmap
即可,内存开销极小且恒定。