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

Jetpack Compose自定义Shadow在API26(Oreo)中模糊效果失效问题

Jetpack Compose自定义Shadow在API26上模糊失效的原因及解决办法

问题原因

BlurMaskFilter本身在API26是支持的,但Jetpack Compose默认启用硬件加速,而Android 8.0/8.1(API26-27)的硬件加速渲染管线对BlurMaskFilter的支持存在bug——硬件加速模式下,mask filter无法正确应用到Canvas绘制中,导致模糊效果丢失,仅显示纯色矩形。从API28开始,谷歌修复了该硬件加速兼容性问题,因此高版本能正常生效。

解决方案

方案1:给当前绘制禁用硬件加速

将绘制逻辑用withLayer包裹,指定使用软件渲染图层,即可让BlurMaskFilter在API26上正常工作:

修改后的修饰符代码:

fun Modifier.shadowCustom(
    color: Color = Color.Black,
    offsetX: Dp = 0.dp,
    offsetY: Dp = 0.dp,
    blurRadius: Dp = 0.dp,
    shapeRadius: Dp = 0.dp,
) = composed  {
    val paint: Paint = remember { Paint() }
    val blurRadiusPx = blurRadius.px(LocalDensity.current)
    val maskFilter = remember {
        BlurMaskFilter(blurRadiusPx, BlurMaskFilter.Blur.NORMAL)
    }
    drawBehind {
        drawIntoCanvas { canvas ->
            // 启用软件渲染图层,兼容API26-27的MaskFilter
            canvas.withLayer(
                layerFlags = Canvas.LAYER_FLAG_SOFTWARE,
                paint = Paint()
            ) {
                val frameworkPaint = paint.asFrameworkPaint()
                if (blurRadius != 0.dp) {
                    frameworkPaint.maskFilter = maskFilter
                }
                frameworkPaint.color = color.toArgb()

                val leftPixel = offsetX.toPx()
                val topPixel = offsetY.toPx()
                val rightPixel = size.width + leftPixel
                val bottomPixel = size.height + topPixel

                if (shapeRadius > 0.dp) {
                    val radiusPx = shapeRadius.toPx()
                    drawRoundRect(
                        left = leftPixel,
                        top = topPixel,
                        right = rightPixel,
                        bottom = bottomPixel,
                        radiusX = radiusPx,
                        radiusY = radiusPx,
                        paint = paint,
                    )
                } else {
                    drawRect(
                        left = leftPixel,
                        top = topPixel,
                        right = rightPixel,
                        bottom = bottomPixel,
                        paint = paint,
                    )
                }
            }
        }
    }
}

private fun Dp.px(density: Density): Float =
    with(density) { toPx() }

提示:软件渲染会有轻微性能开销,但阴影这类小区域绘制基本无感知。

方案2:直接使用Compose官方shadow修饰符(更推荐)

官方shadow修饰符已做好全版本兼容,底层会根据API版本自动切换实现逻辑,无需手动处理版本差异:

Modifier.shadow(
    elevation = blurRadius,
    spotColor = color,
    ambientColor = color,
    shape = if (shapeRadius > 0.dp) RoundedCornerShape(shapeRadius) else RectangleShape,
    offset = Offset(offsetX.toPx(), offsetY.toPx())
)

官方实现经过充分测试,兼容性与稳定性均优于自定义实现。

效果对比

  • API26(Oreo)修复前效果:
    Shadow on Api 26 (oreo)
  • API28+正常效果:
    Shadow on API28+

内容的提问来源于stack exchange,提问作者X jagan X

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:13:16