灶台菜谱设计手记 02|HarmonyOS 7.0 空间化:菜图滚动实现 【鸿蒙心迹】

1. 前言
番茄炖牛腩进入小火炖煮阶段,用户看一眼手机,确认当前步骤,再找“下一步”。这段阅读很短,却要求界面交代两件事:现在做到哪里,接下来可以做什么。
“灶台菜谱”仍然使用本地菜谱,保留“备菜、烹饪、笔记”三个页签,步骤由用户手动推进。正文说明放在稳定背景上,底部操作区显示当前步骤摘要与操作入口。我们要比较的是这个区域的材质选择。
上一篇讨论配置、作用域和背景遮挡。沿着那条排查路径确认材质生效以后,还需要回答另一个问题:菜图经过底栏背后时,文字是否一直清楚?
一张深色锅底图很容易衬出浅色文字。换成米饭白,或者让番茄与葱花交错的纹理经过同一位置,判断就复杂了。停在某个位置的漂亮截图,只能说明那个位置的表现。
我会从一个有限的假设开始:薄材质加自动反色,可能兼顾操作区层次与文字辨认;如果背景或设备条件使这个假设不成立,就比较较厚材质和实色底。
这里的滚动来自固定的本地图片。米饭、锅底和番茄都是背景样本,菜谱始终是番茄炖牛腩。滚动图片不会推进步骤,切换材质也不会改变“当前步骤:小火炖煮”。这样,画面的变化才有明确来源。

图1:固定菜谱、步骤和操作文字,只改变底栏背后的图片位置。观察点是文字辨认与裁切,背景素材的变化不表示切换了菜谱。
2. 方案
从设计方向看,可以让背景参与操作区的呈现,也可以让操作区拥有稳定的底色。为了看清前一种方向内部的差别,再把材质拆成薄、厚两组,得到三个对照方案。
| 方案 | 配置 | 主要观察点 |
|---|---|---|
| A | THIN,开启自动反色 | 背景变化时,关键文字是否容易辨认 |
| B | THICK,关闭自动反色 | 增加厚度后,背景干扰与层次如何变化 |
| C | 关闭材质,使用实色底 | 深浅主题下能否保持稳定阅读 |
A固定使用THIN。ULTRA_THIN也属于支持反色的样式,但不能在某张截图中悄悄换用它,再把结果算到同一个方案里。B和C同样保持各自配置稳定。
三个方案共用字号、字重、文字、图标、尺寸与位置。文字资源保持一致,C的底色按深浅主题定义,不为某张图片临时配色。这里比较的是完整设计方案;若要解释某个参数的作用,还需要单独控制变量。
背景样本选四类:浅色米饭、深色锅底、番茄红和复杂食材纹理。为每张图片保存固定编号、原始资源、来源与授权信息、缩放方式和停靠位置。同一张图用不同裁切,文字背后的亮度与纹理就可能完全不同。
我倾向于把“易读”和“层次”分开记录。前者关注能否读出“小火炖煮”、能否找到“下一步”;后者描述背景是否干扰、操作区是否容易从内容中辨认。将两者混成一个“视觉评分”,会让漂亮的背景掩盖阅读失败。

图2:每行使用同一背景裁切,每列固定一个方案。同组图片保持设备、主题与系统光感设置一致,分别记录阅读表现和层次感。
3. 实现
先明确两个独立的状态:用户正在做哪一步,以及当前用什么背景检查材质。前者属于菜谱,后者属于呈现。把它们写在同一个滚动回调里,会使视觉比较失去基础。
图片可以用一个小数据结构组织。以下片段中的资源名对应工程内固定的四张图片:
interface BackgroundSample {
id: string;
image: Resource;
}
const BACKGROUNDS: BackgroundSample[] = [
{ id: 'light', image: $r('app.media.rice_light') },
{ id: 'dark', image: $r('app.media.pot_dark') },
{ id: 'red', image: $r('app.media.tomato_red') },
{ id: 'texture', image: $r('app.media.food_texture') }
];
在页面组件中保存Scroller,滚动内容只负责展示样本。图片高度和适配方式固定,便于回到相同位置:
private backgroundScroller: Scroller = new Scroller();
@Builder
backgroundStrip() {
Scroll(this.backgroundScroller) {
Column() {
ForEach(BACKGROUNDS, (sample: BackgroundSample) => {
Image(sample.image)
.width('100%')
.height(480)
.objectFit(ImageFit.Cover)
}, (sample: BackgroundSample) => sample.id)
}
.width('100%')
}
.width('100%')
.height('100%')
}
480是这组样本的展示高度,不能当作响应式布局规则。窗口宽度变化后,Cover产生的裁切也会变化,因此窗口尺寸必须进入记录。保存停靠位置后,可用同一个Scroller恢复:
const anchor = this.backgroundScroller.currentOffset();
// 切换方案,待布局稳定后恢复同一停靠位置。
this.backgroundScroller.scrollTo({
xOffset: anchor.xOffset,
yOffset: anchor.yOffset,
animation: false
});
恢复偏移后还要核对画面中的定位特征。偏移值相同不意味着重新布局后的裁切一定相同。整个过程不写入currentStep,功能核对时才通过“下一步”改变进度。
接着处理承载区域。视效指南采用横向Tabs底部TabBar与滚动背景重叠的结构,关键配置如下,连接到现有Tabs声明之后:
.barOverlap(true)
.barFloatingStyle({
adaptToHandedness: true,
systemMaterial: new uiMaterial.ImmersiveMaterial({
style: uiMaterial.ImmersiveStyle.THIN,
colorInvert: true
})
})
其中uiMaterial从@kit.ArkUI导入,Tabs的barPosition为BarPosition.End。barOverlap让内容与底栏区域重叠,为观察背景变化提供条件;它也要求正文为底栏保留必要空间,避免最后一行被遮住。
这里不能省略组件树检查。普通组件的位置约束指向底部TabBar区域,把一个Column推到屏幕下沿并不满足这个条件。摘要和操作入口的具体承载节点,要与三个页签的导航节点分别核对;不能把“烹饪”页签的切换事件改成步骤推进事件。用于比较的底栏子项必须确实落在允许区域内。

图3:背景滚动改变材质下方的内容,步骤状态只由业务操作更新。图中的区域关系以组件树为准,屏幕坐标不能替代作用域判断。
配置材质时,A使用前面的THIN与反色组合,B将材质对象替换为下面的配置,其他条件保持一致:
new uiMaterial.ImmersiveMaterial({
style: uiMaterial.ImmersiveStyle.THICK,
colorInvert: false
})
C则将同一材质入口设为uiMaterial.Material.empty,并在对应背景承载层使用主题实色。不要改成undefined;上一篇已经区分过,undefined恢复默认行为,未必关闭材质。也不要在A、B背后保留C的不透明底色,否则比较的背景已被遮住。
反色的另一个条件藏在颜色来源中。下面两行即使初始画面相近,也不具有相同的反色行为:
// 参与反色的资源颜色
Text('当前步骤:小火炖煮')
.fontColor($r('app.color.recipe_text'))
// 用于核对颜色来源的对照项
Text('当前步骤:小火炖煮')
.fontColor('#FFFFFFFF')
recipe_text需要在默认和深色资源中定义。做颜色来源对照时,应让资源值与硬编码值在当前主题下具有相同基准颜色,避免同时改变颜色值与来源。正式界面中的操作文字和图标也要逐项检查,不能只改摘要。
常见的反色的共同条件:高、中算力设备,THIN或ULTRA_THIN样式,以及支持属性上的资源颜色。系统光感强弱还会影响触发阈值。硬编码颜色不会触发反色;低算力设备上,自动反色不产生视觉效果差异。

图4:colorInvert是请求反色的入口。条件满足后仍需观察实际背景中的文字表现,不能由一个布尔值直接推导阅读结果。
材质厚薄与设备档位也要分开。ImmersiveStyle描述样式,uiMaterial的MaterialLevel由设备决定,可以查询,不能强制设置。低算力设备只支持一种材质样式,不能预期THIN与THICK一定呈现不同厚度。HDS的materialLevel属于另一套接口,不是替这里强制提高档位的开关。
materialColor同样需要克制。它向材质混入颜色,不透明值可能遮挡滤镜;低算力设备上又作为背景色使用。本组A、B保持该参数默认,需要色调时另做带透明度的对照,例如'#80FF0000',不在滚动回调中追着图片改变它。
反色范围只覆盖需要辨认的局部内容。正文继续使用稳定背景,材质不嵌套,也不再叠加backgroundBlurStyle。
4. 检验
测试从一个前提开始:材质已经生效。如果底栏没有进入允许区域,就应回到上一篇排查,而不能把这张截图记成“薄材质可读性失败”。
每个设备与系统设置组有四类背景、两种主题、三个方案,共24个基础样本。换设备或修改系统光感设置,就建立新的组。低档设备的差异单独记录,不能拿另一台设备的一张图替它作答。
每个样本先在固定锚点截图,再按相同方向经过背景交界处,保留滚动录像。浅底字淡、纹理干扰和低档效果差异是重点检查方向,而非预先填写的结果。没有出现的问题也应记录,避免只保留支持设计偏好的画面。

图5:停靠截图方便逐项比较,连续记录覆盖背景变化过程。两者使用相同样本和路径,不能用一个取色点代替全过程。
最小记录表可以从下面这些字段开始:
| 字段 | 记录内容 |
|---|---|
| 标识 | 运行编号、样本ID、方案、工程版本 |
| 环境 | 设备、系统、目标与构建SDK、档位、主题、光感设置 |
| 位置 | 窗口、字号、图片裁切、滚动偏移和定位特征 |
| 配置 | 组件位置、材质样式、反色、颜色来源、背景值 |
| 观察 | 文字辨认、裁切、纹理干扰、层次描述 |
| 证据 | 截图与录像路径、修正项、复测记录 |
关键字是否完整、文字是否读错、入口是否被裁切,应分别填写。截图对比度可以作为辅助信息,但要保留取样位置和方法;字形抗锯齿、局部纹理与不断变化的背景,都使一次取色不足以代表阅读体验。自动反色也不能直接等同无障碍达标。
基础矩阵完成后,再增加大字体和窄窗口检查。此时字号与窗口是新变量,需要另开记录。不能为了通过裁切检查,删掉当前步骤中的重要文字。
若加入扫读任务,就统一提问方式,例如让参与者说出当前动作并指出下一步入口,同时记录参与者、方案顺序、错误与原始计时。只有这类记录才能支持速度上的判断,截图本身不能证明用户读得更快。
失败样本应当成对保存。若怀疑颜色来源,先只替换资源色;若尝试厚材质,保持原背景与位置;若改成实色底,也保留同一套操作内容。修正前后条件相同,才能知道是哪项改动起了作用。

图6:一张卡片对应一个具体问题,修正前后使用同一条件。样本卡保留观察与证据位置,供复测时填写,不预设改善方向。
最后回到菜谱本身。滚动、换样本和换材质都不能重置步骤;“下一步”仍然手动推进,并在末步骤停止。实色回退也必须保留同样的文字、图标含义和操作能力。视觉比较结束后,业务行为仍应完整。
5. 结尾
选择材质需要同时保留配置条件和失败样本。前者解释能力何时可能生效,后者帮助决定这种呈现是否适合眼前的阅读任务。
| 条件 | 选择规则 |
|---|---|
| 反色条件满足,目标背景与主题检查通过 | 保留薄材质,继续覆盖滚动与字体变化 |
| 薄材质存在阅读问题,厚材质同条件复测稳定 | 考虑较厚材质,并记录层次变化 |
| 反色条件不足,或材质仍干扰阅读 | 使用主题实色底,核对功能完整性 |
| 设备与设置覆盖不足 | 保留覆盖缺口,并提供可读的回退 |
固定背景样本集、可读性记录表和这张选择表可以一起维护。增加图片或修改颜色资源时,用相同路径重跑,比凭记忆判断“应该差不多”更可靠。
对灶台菜谱而言,目标一直很具体:用户抬眼时能读清“小火炖煮”,也能辨认“下一步”。材质可以随设备改变,操作含义应当稳定。下一篇沿用这些文字、图标和步骤状态,继续讨论手指触碰、有效操作与业务完成之间的关系。
更多推荐


所有评论(0)