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

如何在Jetpack Compose中实现全屏模糊并保留清晰矩形?

问题描述

我在使用Kotlin开发Jetpack Compose项目,想要给整个屏幕添加模糊效果,但排除特定矩形区域——也就是让背景(不管是图片还是其他UI元素)整体模糊,只保留一个清晰的矩形区域(效果类似背景整体模糊、中间有一块清晰的矩形区域)。

我找到了一个用透明颜色遮罩实现类似区域排除效果的示例代码,但它只是颜色遮罩而非模糊:

@Preview(showBackground = true)
@Composable
fun BlurExample()
{
    Image(
        painter = painterResource(id = R.drawable.delete1),
        contentDescription = null,
        contentScale = ContentScale.FillBounds,
        modifier = Modifier.fillMaxSize().blur(20.dp)
    )
    TransparentClipLayout(
        modifier = Modifier.fillMaxSize(),
        width = 300.dp,
        height = 200.dp,
        offsetY = 150.dp
    )
}

@Composable
fun TransparentClipLayout(
    modifier: Modifier,
    width: Dp,
    height: Dp,
    offsetY: Dp
) {
    val offsetInPx: Float
    val widthInPx: Float
    val heightInPx: Float

    with(LocalDensity.current) {
        offsetInPx = offsetY.toPx()
        widthInPx = width.toPx()
        heightInPx = height.toPx()
    }

    Canvas(modifier = modifier) {
    with(drawContext.canvas.nativeCanvas) {
            val checkPoint = saveLayer(null, null)

            // Destination
            drawRect(Color(0x77FF0000))

            // Source
            drawRoundRect(
                topLeft = Offset(
                    x = (size.width - widthInPx) / 2,
                    y = offsetInPx
                ),
                size = Size(widthInPx, heightInPx),
                cornerRadius = CornerRadius(30f,30f),
                color = Color.Transparent,
                blendMode = BlendMode.Clear
            )
            restoreToCount(checkPoint)
        }
    }
}

我知道可以用Modifier.blur添加模糊,但它会把包括要保留的矩形在内的整个背景都模糊掉。请问怎么实现模糊背景+保留清晰矩形区域的效果?


解决方案

你可以通过自定义Modifier,结合DrawScope.drawWithContent和Canvas的图层保存/恢复机制来实现。核心思路是:先绘制完整的清晰背景,再在上面叠加一层模糊的"遮罩",并通过BlendMode.Clear将需要保留清晰的区域从模糊遮罩中挖去。

以下是完整实现代码:

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp

@Preview(showBackground = true)
@Composable
fun BlurWithClearAreaExample() {
    // 替换成你的背景内容:图片、布局、文字等均可
    Image(
        painter = painterResource(id = R.drawable.delete1),
        contentDescription = null,
        contentScale = androidx.compose.ui.layout.ContentScale.FillBounds,
        modifier = Modifier
            .fillMaxSize()
            .blurExceptArea(
                clearAreaWidth = 300.dp,
                clearAreaHeight = 200.dp,
                clearAreaOffsetY = 150.dp,
                blurRadius = 20.dp
            )
    )
}

/**
 * 自定义Modifier:给元素添加模糊效果,但排除指定矩形区域
 * @param clearAreaWidth 清晰区域宽度
 * @param clearAreaHeight 清晰区域高度
 * @param clearAreaOffsetY 清晰区域距顶部偏移量
 * @param blurRadius 模糊半径
 */
fun Modifier.blurExceptArea(
    clearAreaWidth: Dp,
    clearAreaHeight: Dp,
    clearAreaOffsetY: Dp,
    blurRadius: Dp
) = this.drawWithContent {
    // 先绘制原始清晰内容
    drawContent()

    val density = this.density
    val blurRadiusPx = blurRadius.toPx()
    val clearWidthPx = clearAreaWidth.toPx()
    val clearHeightPx = clearAreaHeight.toPx()
    val clearOffsetYPx = clearAreaOffsetY.toPx()

    // 计算清晰区域位置:水平居中,垂直按偏移量定位
    val clearAreaTopLeft = Offset(
        x = (size.width - clearWidthPx) / 2,
        y = clearOffsetYPx
    )
    val clearAreaSize = Size(clearWidthPx, clearHeightPx)

    drawIntoCanvas { canvas ->
        val nativeCanvas = canvas.nativeCanvas
        val saveLayer = nativeCanvas.saveLayer(null, null)

        // 绘制覆盖全屏的模糊层
        val blurPaint = Paint().apply {
            asFrameworkPaint().apply {
                maskFilter = android.graphics.BlurMaskFilter(blurRadiusPx, android.graphics.BlurMaskFilter.Blur.NORMAL)
            }
        }
        canvas.drawRect(
            topLeft = Offset.Zero,
            size = size,
            color = Color.Black.copy(alpha = 1f),
            paint = blurPaint
        )

        // 用Clear模式挖掉清晰区域,让下方原始内容透出
        canvas.drawRoundRect(
            topLeft = clearAreaTopLeft,
            size = clearAreaSize,
            cornerRadius = CornerRadius(30f, 30f),
            color = Color.Transparent,
            blendMode = BlendMode.Clear
        )

        nativeCanvas.restoreToCount(saveLayer)
    }
}

实现说明

  1. 先通过drawContent()绘制原始的清晰背景内容(图片、布局、文字等均可)
  2. 创建覆盖全屏的模糊图层,使用BlurMaskFilter实现模糊效果
  3. 利用BlendMode.Clear在模糊图层上挖去指定区域,让下方的清晰内容显示出来
  4. 可通过调整参数适配需求:修改blurRadius调整模糊程度,修改清晰区域的尺寸、偏移量、圆角半径等

内容的提问来源于stack exchange,提问作者Oleksandr Myronchuk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 12:53:11