如何用Coil实现ImageView宽适配、高裁剪的图片加载效果?
固定高度ImageView适配宽度并裁剪高度的Coil实现方案
核心思路
要实现宽度填满ImageView、高度按比例缩放后裁剪超出部分的效果,默认ScaleType无法满足需求(CenterCrop会优先保证填满容器而裁剪宽度,CenterFit会优先完整显示图片而无法填满宽度),需要通过Coil的自定义Transformation精准控制图片的缩放与裁剪逻辑。
步骤1:配置ImageView基础属性
给ImageView设置固定高度、自适应宽度,并禁用默认ScaleType的干扰:
<ImageView android:id="@+id/iv_photo" android:layout_width="match_parent" android:layout_height="400dp" android:scaleType="matrix" /> <!-- 使用matrix避免默认ScaleType自动调整 -->
步骤2:自定义Coil Transformation
编写自定义变换类,逻辑如下:
- 以ImageView宽度为基准计算缩放比例,让图片宽度完全匹配ImageView宽度
- 按比例缩放图片高度
- 若缩放后高度超过ImageView固定高度,裁剪中间区域保留目标高度;不足则直接返回缩放后的图片
class FitWidthCropHeightTransformation( private val targetWidth: Int, private val targetHeight: Int ) : Transformation { override suspend fun transform(pool: BitmapPool, input: Bitmap): Bitmap { // 计算缩放比例:让图片宽度适配目标宽度 val scaleFactor = targetWidth.toFloat() / input.width.toFloat() val scaledHeight = (input.height * scaleFactor).toInt() // 生成宽度匹配的缩放Bitmap val scaledBitmap = Bitmap.createScaledBitmap(input, targetWidth, scaledHeight, true) // 裁剪超出目标高度的部分(取中间区域) return if (scaledHeight > targetHeight) { val cropTopOffset = (scaledHeight - targetHeight) / 2 Bitmap.createBitmap(scaledBitmap, 0, cropTopOffset, targetWidth, targetHeight) } else { scaledBitmap } } }
步骤3:加载图片时应用变换
需确保ImageView完成布局后获取实际宽高,再加载图片并应用自定义变换:
val ivPhoto = findViewById<ImageView>(R.id.iv_photo) // 监听View布局完成,获取实际尺寸 ivPhoto.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { ivPhoto.viewTreeObserver.removeOnGlobalLayoutListener(this) val targetWidth = ivPhoto.width val targetHeight = ivPhoto.height // Coil加载图片并应用自定义变换 ivPhoto.load("https://your-image-url.com/image.jpg") { transformations(FitWidthCropHeightTransformation(targetWidth, targetHeight)) scale(Scale.FILL) // 配合确保图片填满容器 } } })
替代简化方案(无需自定义Transformation)
若不想编写自定义类,可利用Coil的size和scale参数结合ImageView的scaleType="matrix",手动计算缩放矩阵:
ivPhoto.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { ivPhoto.viewTreeObserver.removeOnGlobalLayoutListener(this) val viewWidth = ivPhoto.width val viewHeight = ivPhoto.height ivPhoto.load("https://your-image-url.com/image.jpg") { size(viewWidth, Int.MAX_VALUE) // 强制按View宽度缩放,高度不限制 scale(Scale.FILL) listener( onSuccess = { request, result -> val bitmap = result.drawable.bitmap val scale = viewWidth.toFloat() / bitmap.width.toFloat() val scaledHeight = bitmap.height * scale val matrix = Matrix().apply { setScale(scale, scale) // 裁剪顶部偏移,让图片垂直居中 postTranslate(0f, (viewHeight - scaledHeight) / 2) } ivPhoto.imageMatrix = matrix } ) } } })
内容的提问来源于stack exchange,提问作者P1x
相关产品推荐
相关产品推荐

