第40篇美颜预设自然、人像、清透如何变成可解释选项相机 App 的效果设置很容易做成一堆参数开关但训练营项目选择了更贴近用户理解的表达自然、人像、清透。它们不直接暴露底层算法参数而是作为拍摄语义进入页面状态、拍摄摘要和后续记录。这一篇我们不讨论美颜算法本身而是看工程上如何把“可理解的预设”变成稳定的状态管理。对于智能相机用户不一定关心每个参数值但他需要知道当前拍出来的照片用了什么风格。这一篇继续围绕 21 天「智能相机开发实战」训练营展开。内容只使用当前项目里的 ArkTS、服务层代码和真实页面截图来讲不把封面图放进正文。阅读时可以先看截图理解用户侧效果再顺着函数名回到工程定位实现。本篇目标把美颜预设设计成稳定枚举而不是散落字符串。理解选项数组、选中态和标签推导之间的关系。掌握效果面板如何复用 chip 组件。让拍摄摘要记录用户选择避免效果不可追踪。对应源码位置entry/src/main/ets/pages/Index.ets一、效果选项要先有产品语义BeautyPresetKey只有三个值natural、portrait、clear。这比直接出现“磨皮 30、提亮 20、锐化 10”更适合训练营项目因为文章、截图和实际体验都能围绕用户能理解的词展开。当选项变成类型以后后续 UI、摘要和记录都可以引用同一组 key。这样改文案不会影响业务判断改状态也不需要全文搜索中文标签。图1 相机页中的美颜预设入口和用户可感知效果设置二、枚举和状态集中定义避免魔法字符串项目把效果分类、美颜预设、补光颜色和水印样式都放在页面顶部的类型定义和状态区域。你可以一眼看出拍摄效果有哪些维度也能知道当前默认值是什么。这种写法适合训练营讲解学员先从状态看功能边界再进入 UI 和拍摄链路不会被分散在多个 Builder 里的字符串干扰。图2 拍摄效果的枚举、状态和选项数组集中定义type BeautyPresetKey natural | portrait | clear; type FillLightColorKey off | warm | cool | rose; type WatermarkStyleKey none | time | place | dual; type CameraEffectCategoryKey beauty | fillLight | watermark; type PhotoOutputReadyKey dualBack | dualFront | single; interface MemoryRecordGroup { key: string; latitude: number; longitude: number;这里建议继续保持 key 的稳定性。后续如果要接入真正的图像效果参数可以在 key 到参数表之间新增映射而不是直接改掉 key。三、标签和摘要从状态推导getBeautyPresetLabel把内部 key 转成用户能看懂的中文标签。拍摄摘要再把美颜、补光、水印拼成一句简短说明。这样用户拍完以后知道“这张照片为什么看起来这样”记录回看时也能知道当时的拍摄设置。摘要不是装饰文案它承担了轻量审计作用。以后做云同步、分享或导出时摘要可以成为照片元信息的一部分。图3 美颜标签和拍摄摘要由当前状态推导private getBeautyPresetLabel(preset: BeautyPresetKey): string { if (preset portrait) { return 人像; } if (preset clear) { return 清透; } return 自然; } private getBeautyOverlayColor(): string { if (this.selectedBeautyPreset portrait) { return #22FFD3C2; } if (this.selectedBeautyPreset clear) { return #1EDDF8FF; } return #0DFFFFFF; } private getBeautyOverlayOpacity(): number { if (this.selectedBeautyPreset portrait) { return 0.54; } if (this.selectedBeautyPreset clear) { return 0.42; } return 0.22; }把标签推导封装成函数还有一个好处UI 多处展示同一个预设时不会出现一个地方叫“清透”、另一个地方叫“通透”的不一致。四、效果面板复用同一个 Chip 组件效果面板根据当前分类渲染对应选项。美颜、补光、水印看起来是三组不同功能但它们在交互上都是“点选一个 chip更新状态刷新选中态”。因此项目用buildCameraEffectChip复用样式和点击行为。如果每一组效果都手写按钮很快会出现圆角、字体、背景和点击逻辑不一致。训练营项目更适合把这类重复 UI 收口让文章可以把重点放在状态流上。图4 buildCameraEffectSelectedOptions 渲染美颜、补光、水印选项private buildCameraEffectSelectedOptions() { Scroll() { Row({ space: 8 }) { if (this.selectedCameraEffectCategory beauty) { ForEach(this.beautyPresetOptions, (preset: BeautyPresetKey) { this.buildCameraEffectChip(this.getBeautyPresetLabel(preset), this.selectedBeautyPreset preset, () { this.selectedBeautyPreset preset; this.cameraEffectOptionRevision 1; this.lastCaptureSummary this.getCameraEffectSummary(); }) }, (preset: BeautyPresetKey) ${preset}-${this.selectedBeautyPreset}-${this.cameraEffectOptionRevision}) } else if (this.selectedCameraEffectCategory fillLight) { ForEach(this.fillLightColorOptions, (color: FillLightColorKey) { this.buildCameraEffectChip(this.getFillLightLabel(color), this.selectedFillLightColor color, () { this.selectedFillLightColor color; this.cameraEffectOptionRevision 1; this.lastCaptureSummary this.getCameraEffectSummary(); }) }, (color: FillLightColorKey) ${color}-${this.selectedFillLightColor}-${this.cameraEffectOptionRevision}) } else { ForEach(this.watermarkStyleOptions, (style: WatermarkStyleKey) { this.buildCameraEffectChip(this.getWatermarkStyleLabel(style), this.selectedWatermarkStyle style, () { this.selectedWatermarkStyle style; this.cameraEffectOptionRevision 1; this.lastCaptureSummary this.getCameraEffectSummary(); }) }, (style: WatermarkStyleKey) ${style}-${this.selectedWatermarkStyle}-${this.cameraEffectOptionRevision}) } }这一段代码也说明了一个 ArkUI 实战习惯列表 key 要包含当前选中状态或修订号避免选项变化后 UI 复用造成显示不刷新。工程检查清单预设 key 使用英文稳定值展示文案通过函数转换。默认值在状态区清晰可见方便排查首次进入页面的效果。拍摄摘要要包含用户选择过的效果信息。效果面板复用 chip 组件减少 UI 分叉。新增效果时先扩展类型和选项数组再补标签和摘要。今日练习新增一个cinematic美颜 key先不实现算法只让 UI 能显示并进入摘要。检查效果面板切换分类时之前选中的美颜状态是否仍然保留。思考如果以后接入真实美颜 SDKkey 到参数表应该放在页面层还是服务层。训练营后面的内容会继续按“真实页面效果 → 源码定位 → 状态闭环 → 可验证结果”的节奏推进。每一篇都尽量让你能拿着代码直接回到项目里复现而不是只停留在概念说明。