You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

编写自定义变换类,逻辑如下:

  1. 以ImageView宽度为基准计算缩放比例,让图片宽度完全匹配ImageView宽度
  2. 按比例缩放图片高度
  3. 若缩放后高度超过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 10:03:19