20分钟搞定 Compose 毛玻璃与玻璃拟态效果

4 次阅读 0 点赞 0 评论 11 分钟原创技术教程

本教程带你快速上手 Haze 2 库,20 分钟内为 Compose 页面添加毛玻璃模糊和玻璃拟态效果。涵盖模块化配置、实战代码、性能调优与多平台避坑指南。学完即可直接在项目中集成高级 UI 效果。

#JetpackCompose #ComposeMultiplatform #毛玻璃效果 #UI开发 #Kotlin #移动端开发
20分钟搞定 Compose 毛玻璃与玻璃拟态效果

给你的 Compose 页面加上毛玻璃效果:Haze 2 实战教程

为什么你需要毛玻璃效果?

做 UI 开发时,产品需求里总会出现那句「加个半透明毛玻璃背景,高级感就出来了」。在 Compose 中徒手实现这个效果,过去往往需要自定义 Canvas 绘制或者寻找功能单一的第三方方案。这类方案不仅性能堪忧、边缘容易出现锯齿,而且大多只支持 Android,一旦涉及跨平台交付就非常头疼。

Haze 库由 Android 领域知名开发者 chrisbanes 维护,全面覆盖 Android、iOS、macOS、Desktop 以及 Web 平台。最新的 2.0 版本对底层架构进行了重构,采用模块化设计与全新的「源-效果」分离 API。本教程带你从零接入,用不到二十分钟为你的 Compose 界面集成专业的模糊与拟物效果。

开发环境准备

确保你的项目满足以下基础配置:

  • 运行环境为 Jetpack Compose 或 Compose Multiplatform
  • Kotlin 版本 2.0+(最低支持 1.9+)
  • 熟悉 Modifier 链式调用、Box 布局容器与 remember 状态管理的基本用法
  • 若进行多平台开发,需确认目标平台模块已正确配置

本教程以 Android 为主线演示,涉及多平台差异的地方会单独标注。

接入 Haze 2:按需引入模块

Haze 2 摒弃了传统的单体依赖,改为按需加载的模块化架构。很多新手容易在这个环节踩坑:直接引入所有模块导致包体积膨胀,或者混用不同版本造成编译冲突。

核心模块划分逻辑如下:

  • haze:基建核心,提供 hazeSource 源图层捕获能力与扩展 API
  • haze-blur:毛玻璃模糊渲染引擎
  • haze-blur-materials:强烈建议引入,内置薄、常规、厚三种预制模糊样式
  • haze-glass:玻璃拟态渲染引擎(实验性特性)
  • haze-glass-material3:适配 Material3 设计规范的玻璃样式

在你的 build.gradle.kts 文件中(Compose Multiplatform 项目请放在 commonMain 依赖块),添加如下配置:

kotlin 复制代码
val hazeVersion = "2.0.0-beta03"

dependencies {
    implementation("dev.chrisbanes.haze:haze:$hazeVersion")
    implementation("dev.chrisbanes.haze:haze-blur:$hazeVersion")
    implementation("dev.chrisbanes.haze:haze-blur-materials:$hazeVersion")
    // 如需玻璃拟态效果,取消下面这行的注释
    // implementation("dev.chrisbanes.haze:haze-glass:$hazeVersion")
}

注意保持所有 dev.chrisbanes.haze 开头的 artifact 版本号绝对一致。当前版本处于 Beta 阶段,旧版 1.x 用户迁移时需查阅官方指南,核心变更在于链式调用被替换为 hazeBlur/hazeGlass 配合 HazeInputStyle 的新范式。

场景一:图片上方叠加毛玻璃标题栏

这是 UI 开发中最经典的组合:背景大图,上方覆盖半透明模糊层承载文字。在深入代码之前,理解 Haze 2 的底层图形学机制能帮你避开大量布局错乱问题。Haze 不依赖传统的 View 截图,而是利用 Compose 原生的 GraphicsLayer 进行离屏渲染捕获。这意味着源图层的内容变化会实时同步到效果层,且不会触发额外的 UI Tree 遍历。

kotlin 复制代码
@Composable
fun FrostedGlassImageDemo(painter: Painter) {
    val hazeState = rememberHazeState()

    Box {
        Image(
            painter = painter,
            contentDescription = null,
            modifier = Modifier
                .fillMaxSize()
                .hazeSource(hazeState),
        )

        Box(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .fillMaxWidth()
                .height(200.dp)
                .hazeBlur(
                    input = HazeInput.Sources(hazeState),
                    style = HazeMaterials.thick(),
                )
                .padding(16.dp)
        ) {
            Text(
                text = "这张图的底部标题",
                color = Color.White,
                style = MaterialTheme.typography.headlineSmall,
            )
        }
    }
}

rememberHazeState() 充当数据中转站,负责在源内容与效果层之间传递捕获的图形数据。配合 remember 使用,可避免重组时状态意外重置。
Modifier.hazeSource(hazeState) 标记源图层,Haze 底层会抓取该区域的所有渲染像素。
Modifier.hazeBlur(...) 负责渲染效果。传入 HazeInput.Sources(hazeState) 明确告知渲染器:模糊素材来自前面标记的源图层。

这套设计的精妙之处在于「解耦」。源图层可以是任意 Composable,效果层作为独立视图叠加,生命周期互不干扰。如果希望模糊效果层自身的子组件,只需将输入源替换为 HazeInput.Content,这在制作浮动面板时极为实用。性能调优方面,HazeInput 提供了 scaleFactor 参数,适当降低该值能在几乎不损失视觉观感的前提下,将 GPU 着色器的计算量大幅削减。

场景二:打造玻璃拟态卡片

玻璃拟态在 Blur 基础上引入了折射率、边缘高光与圆角过渡,视觉效果更具空间层次感。使用 haze-glass 模块前,请留意其标注了 @ExperimentalHazeApi

kotlin 复制代码
@OptIn(ExperimentalHazeApi::class)
@Composable
fun GlassCardDemo(painter: Painter) {
    val hazeState = rememberHazeState()

    Box {
        Image(
            painter = painter,
            contentDescription = null,
            modifier = Modifier
                .fillMaxSize()
                .hazeSource(hazeState),
        )

        Box(
            modifier = Modifier
                .align(Alignment.Center)
                .size(280.dp, 160.dp)
                .hazeGlass(
                    input = HazeInput.Sources(hazeState),
                    style = GlassStyle.regular.then {
                        tint(Color.White.copy(alpha = 0.16f))
                        shape(RoundedCornerShape(20.dp))
                    },
                )
                .padding(24.dp)
        ) {
            Column {
                Text("玻璃拟态卡片", color = Color.White, fontSize = 20.sp)
                Spacer(Modifier.height(8.dp))
                Text("带折射感和高光的毛玻璃效果", color = Color.White.copy(0.8f))
            }
        }
    }
}

GlassStyle.regular 提供了开箱即用的高质量默认参数,包含了折射模糊、物理高光与柔和阴影。若背景图色彩丰富且需要更高的通透度,可切换至 clear 变体,让底层纹理更清晰地透射出来。

性能调优策略

默认性能模式 HazePerformanceMode.Default 已针对主流设备做了自适应优化。绝大多数项目无需干预,但一旦在低端设备或复杂列表中遇到掉帧,可按照以下路径排查:

  • 关闭 Debug 包测试:Debug 模式下 GPU 硬件加速受限,务必切换至 Release 包或 Benchmark 环境在真机验证。
  • 动态调整性能档位:尝试切换至 Balanced(平衡模式)或 Performance(流畅优先)。极端场景下可使用 Fixed 锁定模糊半径,牺牲画质换取绝对帧率。配合降采样机制,能进一步缓解长列表滚动时的渲染压力。
  • 收缩源图层范围:hazeSource 捕获的像素面积直接决定计算开销。尽量精确控制被捕获区域,避免标记全屏冗余元素。
  • CameraX 兼容方案:在实时相机预览上叠加模糊时,PreviewView 必须使用 COMPATIBLE 模式。原生 SurfaceView 由于渲染管线隔离,无法被上层捕获。

避坑指南与跨平台注意

  • 版本号强制对齐:运行时依赖混用极易导致类找不到或渲染异常。
  • Beta 期 API 波动:跨大版本升级前务必核对 Release Notes,部分参数签名可能调整。
  • 多平台交付时,Gradle 插件的依赖解析需要特别注意。Android 端通常走 androidMain,而 iOS/Desktop/Web 走对应源码集。如果在 commonMain 中声明了完整依赖但编译报错,检查 haze 对应的 KMP 兼容性标记。对于 Wasm/JS 平台,由于浏览器的 Canvas 2D API 限制,复杂的实时模糊可能回退到静态渲染,建议在对应平台通过 actual 关键字提供轻量级降级实现。
  • 多端视觉一致性:不同平台的栅格化引擎存在差异。iOS 的金属管线与 Android 的渲染器在边缘抗锯齿和高光混合算法上可能产生微小偏差,上线前需对各目标平台进行视觉走查。

总结

通过本教程,你掌握了利用 Haze 2 实现高级 UI 效果的完整链路:从模块化依赖配置,到「源-效果」分离架构的实际编码,再到性能调优与多平台验证。掌握了这些技巧,你可以轻松应对复杂的背景模糊、毛玻璃导航栏以及拟物化卡片需求。

建议前往官方 Sample 仓库探索更多高阶场景,例如对 LazyList 滚动区域的局部模糊处理。合理使用 Haze 能大幅缩减手写 Shader 和调试 Canvas 的时间成本,让你的 UI 评审更加顺利。

最后更新:2026-09-24T10:02:51

评论 (0)

发表评论

blog.comments.form.loading
0/500
加载评论中...