将图片裁剪为合适的大小,以适应View的宽高。
1、自适应拉伸填满宽,再保持图片宽高比拉伸高,并垂直居中显示。
private fun ImageView.loadImageByDrawableId(id: Int) {
doOnLayout {
kotlin.runCatching {
decodeBitmap(id, width, height)
}.getOrNull()?.also {
setImageBitmap(it)
}
}
}
private fun decodeBitmap(id: Int, targetWidth: Int, targetHeight: Int): Bitmap {
val options = BitmapFactory.Options()
options.inScaled = false
options.inMutable = true
options.inPreferredConfig = Bitmap.Config.RGB_565
options.inDither = true
options.inJustDecodeBounds = true
BitmapFactory.decodeResource(resources, id, options)
val sourceWidth = options.outWidth
val sourceHeight = options.outHeight
options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight)
options.inJustDecodeBounds = false
var scaleHeight = targetHeight.times(sourceWidth.toFloat().div(targetWidth)).toInt()
var bitmap: Bitmap? = null
if (sourceHeight > scaleHeight) {
val top = sourceHeight.minus(scaleHeight).div(2)
val rect = Rect(0, top, sourceWidth, top + scaleHeight)
bitmap = kotlin.runCatching {
contentResolver.openInputStream(toUri(id))?.use {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
BitmapRegionDecoder.newInstance(it)?.decodeRegion(rect, options)
} else {
BitmapRegionDecoder.newInstance(it, false)?.decodeRegion(rect, options)
}
}
}.getOrNull()
}
val newBitmap = bitmap ?: BitmapFactory.decodeResource(resources, id, options)
if (newBitmap.width == targetWidth) {
return newBitmap
}
val matrix = Matrix()
val sx = targetWidth.toFloat().div(newBitmap.width)
matrix.setScale(sx, sx)
scaleHeight = targetHeight.times(newBitmap.width.toFloat().div(targetWidth)).toInt()
return if (newBitmap.height > scaleHeight) {
Bitmap.createBitmap(newBitmap, 0, newBitmap.height.minus(scaleHeight).div(2), newBitmap.width, newBitmap.height, matrix, false)
} else {
Bitmap.createBitmap(newBitmap, 0, 0, newBitmap.width, newBitmap.height, matrix, false)
}
}
private fun toUri(resID: Int): Uri {
val path = buildString {
append(ContentResolver.SCHEME_ANDROID_RESOURCE)
append("://")
append(resources.getResourcePackageName(resID))
append("/")
append(resources.getResourceTypeName(resID))
append("/")
append(resources.getResourceEntryName(resID))
}
return Uri.parse(path)
}
private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
// Raw height and width of image
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1
if (height > reqHeight || width > reqWidth) {
val halfHeight: Int = height / 2
val halfWidth: Int = width / 2
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2
}
}
return inSampleSize
}
1、首先解码出图片文件的宽高
2、再对比目标view的宽高和源文件宽高,确定是否需要裁剪和计算出采样率。
3、如需裁剪,则使用BitmapRegionDecoder解码图片,否则使用BitmapFactory解码图片。
4、再缩放解码后的图片至和目标view的宽一致即可。