如何在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) } }
实现说明
- 先通过
drawContent()绘制原始的清晰背景内容(图片、布局、文字等均可) - 创建覆盖全屏的模糊图层,使用
BlurMaskFilter实现模糊效果 - 利用
BlendMode.Clear在模糊图层上挖去指定区域,让下方的清晰内容显示出来 - 可通过调整参数适配需求:修改
blurRadius调整模糊程度,修改清晰区域的尺寸、偏移量、圆角半径等
内容的提问来源于stack exchange,提问作者Oleksandr Myronchuk
相关产品推荐
相关产品推荐

