灶台菜谱设计手记 03|HarmonyOS 7.0 空间化:沉浸光感反馈

1. 前言

番茄炖牛腩还在小火炖煮。用户伸手按住"下一步",看见按钮产生形变,随后改变主意,把手指拖到按钮外再松开。现在,界面应该告诉他什么?

我们希望当前步骤保持不变。但按钮已经回应过触碰。如果用户把那次回应理解为"上一步完成了",即使内部状态正确,界面仍然留下了歧义。

前两篇分别讨论材质在哪里生效,以及菜图经过底栏时文字能否读清。本篇沿用同一套"灶台菜谱":番茄炖牛腩、本地步骤数据,以及"备菜、烹饪、笔记"三个页签。问题向前移动了一点:当用户读懂按钮并开始操作,界面怎样说明操作的结果?

我会把这个过程拆成三件事:手指触碰了组件,组件识别出点击,应用接受点击并推进步骤。它们经常接连发生,却不能互相替代。取消操作恰好能暴露这种区别:触碰已经发生,业务变化却不应该发生。

图03-A:按下、拖出、松手的操作路径。四帧使用同一菜谱与初始步骤,关注最后的步骤文字是否保持原值。

本篇要建立的规则很简单:触摸反馈解释正在发生的接触,业务反馈解释已经提交的变化。这里的"完成"仅指用户确认前一步并推进了菜谱,不代表应用识别了锅里的食物,更不代表整道菜已经做好。

2. 方案

先比较两种设计。方案A保留基础菜谱文本,把操作结果的提示交给瞬时光效;方案B在按压反馈之外,持续显示当前步骤,并用文字和符号说明刚刚完成的上一步。两种方案使用相同的推进逻辑,区别在于它们给用户留下多少可供确认的信息。

比较项 方案A 方案B
手指接触入口 出现光效 出现光效与按压形变
成功推进之后 主要依靠刚才的视觉变化提示 当前步骤更新,并显示上一步完成标记
取消之后 需要重新阅读正文判断 当前步骤与已有完成标记保持原值
光效关闭之后 缺少独立的结果提示 仍可从文字与符号确认进度

我更倾向于方案B,因为灶台旁的阅读并不连续。用户可能按完按钮就转身拿锅盖,回头时动画已经结束。持续存在的状态信息让他不必记住一段短暂的视觉变化。

这仍然是需要检验的设计判断。比较时应固定按钮尺寸、文字资源、背景裁切、材质厚薄和初始步骤。若同时换成更清楚的背景,就无法判断改善来自反馈设计还是可读性。

图03-D:两种方案使用同一操作入口和业务规则。对照重点是手指离开后还留下哪些信息,而不是哪一侧的光效更显眼。

在写代码之前,还要约定几个容易被忽略的行为。一次有效点击推进一项;连续有效点击逐次推进,到末步停止。这里没有防抖,也没有"一连串点击只算一次"的隐藏规则。取消不推进,禁用不推进,末步不循环回第一步。

到达最后一步也不等于最后一步已经做完。因此,提示使用"已到最后一步",正文继续展示末步骤操作。若将来需要"完成烹饪",应为它设计独立动作,而不是让索引到达末尾就顺便完成另一件事。

3. 实现

先安放视觉配置。沉浸光感沿用前篇的底栏结构,应确认它处于横向Tabs、barPosition为BarPosition.End的底部TabBar区域。屏幕底部的一块Column并不会因为位置靠下就自动获得这一资格。

三个页签继续负责导航,"下一步"继续负责推进。本文只展开材质配置和业务处理片段,底栏的完整组件树沿用前篇结构;接入时需按工程SDK核对类型与承载方式,不能把以下片段移到任意正文区域就期待相同效果。

沉浸式系统材质两种交互效果:interactive控制按压弹性形变,lightEffect控制触摸时的点光源。后者传入有效对象即启用,null或undefined不启用;对象中的color默认白色。

ts 复制代码
import { uiMaterial } from '@kit.ArkUI';

// 材质配置片段:接入已确认支持的组件区域。
function createStepMaterial(lightEnabled: boolean) {
  return new uiMaterial.ImmersiveMaterial({
    style: uiMaterial.ImmersiveStyle.THIN,
    colorInvert: true,
    interactive: true,
    lightEffect: lightEnabled ? {} : undefined
  });
}

这里保留第2篇薄材质方案的配置,文字继续使用资源颜色。若前篇的可读性选择表要求厚材质或实色回退,就沿用该选择,保持下面的业务逻辑不变。

关闭lightEffect时,interactive仍然为true。因此,"没有流光"不意味着"没有按压形变"。同样,这里的undefined仅表示不启用点光源,不能借用它解释systemMaterial的默认行为;关闭材质与关闭某一种交互效果是不同操作。

视觉配置确定后,步骤推进可以独立表达。下面的函数只接收当前位置、步骤总数与禁用条件,不接收材质或动画参数。

ts 复制代码
interface AdvanceResult {
  next: number;
  accepted: boolean;
  reason: string;
}

function advanceStep(
  current: number,
  count: number,
  disabled: boolean
): AdvanceResult {
  if (count <= 0 || current < 0 || current >= count) {
    return { next: current, accepted: false, reason: 'invalid' };
  }
  if (disabled) {
    return { next: current, accepted: false, reason: 'disabled' };
  }
  if (current === count - 1) {
    return { next: current, accepted: false, reason: 'last-step' };
  }
  return { next: current + 1, accepted: true, reason: 'advanced' };
}

这个函数的价值在于把规则放到了一个能直接检查的位置。切换主题、菜图或光效不需要调用它;收到一次符合交互约定的点击才调用它。函数返回拒绝原因,便于区分"不可操作"和"已经到了末步"。

在页面中,currentStep仍是RecipeData步骤数组的当前索引。以下成员片段中的steps引用同一份非空步骤列表,每项包含title;禁用状态与原因由页面管理。状态应放在三个页签共同使用的页面层,避免切换页签时重新初始化。

ts 复制代码
@State currentStep: number = 0;
@State advanceDisabled: boolean = false;
@State disabledReason: string = '';
private eventSequence: number = 0;

private handleNext(): void {
  const before = this.currentStep;
  const result = advanceStep(
    before, this.steps.length, this.advanceDisabled
  );
  if (result.accepted) {
    this.currentStep = result.next;
  }
  const sequence = ++this.eventSequence;
  console.info(
    `[step] seq=${sequence} time=${Date.now()} ` +
    `before=${before} after=${result.next} ` +
    `accepted=${result.accepted} reason=${result.reason}`
  );
}

日志保存处理时的前后值,不在稍后的异步任务里重新读取currentStep。否则,快速点击留下的多条记录可能全部指向最后一个状态,丢掉我们真正想检查的变化过程。

按钮的onClick绑定handleNext。组件层使用enabled表达可操作性,业务入口仍检查禁用与末步边界。这两层分别负责交互入口和状态规则;以后增加其他调用入口时,也能复用同一份判断。

触摸事件可以用于记录按下、移动、抬起与取消,但不要在其中再次增加步骤。尤其不能把"抬起"直接解释成"推进",或等待光效结束再提交状态。动画不是业务事务的完成通知。

图03-B:上轨记录触摸和视效,下轨记录点击识别、应用判断与步骤更新。正常点击和拖出取消分别成图,不推断系统内部动画时序。

需要特别注意,onClick并不是"任何拖出动作都会被正确取消"的证明。应按实际组件的命中范围与事件规则核对拖出路径。如果外部松手仍产生点击,且违反本篇约定,就需要补充应用侧的取消处理,再执行全部回归。不能靠换一个材质参数解决事件识别问题。

显示内容则尽量由同一状态推导。下面是页面中的文字构建片段,完成标记指向前一个步骤,不维护第二份容易失配的进度列表。

ts 复制代码
@Builder
StepStatus() {
  Column() {
    Text(`当前步骤:${this.steps[this.currentStep].title}`)
    if (this.currentStep > 0) {
      Text(`✓ 上一步已完成:${this.steps[this.currentStep - 1].title}`)
    }
    if (this.currentStep === this.steps.length - 1) {
      Text('已到最后一步')
    } else if (this.advanceDisabled) {
      Text(this.disabledReason)
    }
  }
}

这种推导依赖现有的单向顺序推进规则。若以后加入跳步、撤销或直接定位,当前索引就不足以证明前一步完成,届时应引入独立完成记录。眼下保持模型与功能规模一致,比提前维护多份状态更容易看清错误。

4. 检验

我会从取消路径开始检查,因为它迫使视觉和业务给出不同答案:可以出现触摸反馈,但不能推进步骤。随后再检查正常点击与其他边界。下表列出验收预期,用来逐项填写运行记录。

用例 操作条件 应满足的状态规则
按下拖出 非末步,按下后拖出,在外部松手 步骤与完成标记不变
正常点击 非末步,可操作,完成一次点击 只推进一项,摘要同步更新
快速连续点击 从前部步骤开始连续点击 逐次处理有效点击,末步停止
末步骤 到达最后一项后尝试继续 不越界、不循环、不新增完成结果
禁用态 非末步,设置禁用条件后操作 不推进,并能读到禁用原因
关闭光效 保持其他配置,重复点击、取消和末步路径 业务结果与开启光效时一致

连续点击至少要区分三个计数:手指动作次数、识别出的点击次数、应用接受的推进次数。假设初始索引为s,总步数为N,识别出k次符合交互约定的点击,期间没有其他禁用条件,那么最终索引应为min(s+k, N-1)。到达末步后的点击不会再增加推进次数。

只从倒数一步开始连点不够。无论中间漏掉多少操作,最终都可能停在末步。应从前部步骤开始,逐条检查日志中的before、after及拒绝原因,再与录屏中的步骤文字对齐。

图03-E:每条操作独立记录初始条件和状态变化。事件序号关联录屏位置,区分预期与实际结果,不用一张最终截图替代中间过程。

运行记录还需要工程版本、目标与构建SDK、设备、运行系统、主题和系统光感设置。配置栏分别填写interactive、lightEffect及材质方案;事件栏填写时间、事件类型、是否接受和拒绝原因;证据栏保存录屏路径与对应位置。

仅关闭点光源时,先保持interactive不变,再分别检查null和undefined路径。若还要关闭形变,应另开一组配置。一次改变两个参数,失败时就多了一种解释。

图03-C:左右两列使用相同业务条件,只改变点光源配置。比较步骤文字和完成标记是否一致,不要求两侧呈现相同视效。

录屏能帮助确认用户看到了什么,日志能帮助确认应用做了什么。它们都不能单独证明用户理解得更快。如果要评价理解程度,可以让参与者回答"刚才是否推进""现在做到哪一步",记录任务、回答与错误,而不是根据动画是否顺滑估计收益。

拖出仍推进、一次点击更新两次、禁用后状态变化,都应保留完整操作路径。先定位是哪一层违背规则,再修改对应实现,最后重跑六类用例。视觉变化与业务变化分开记录,才能避免把事件错误归咎于材质。

5. 结尾

灶台菜谱需要一套可以在动画消失之后继续成立的解释。手指接触入口时,材质可以回应;应用接受推进时,步骤文字和完成标记必须给出持久的结果。取消、禁用和末步边界则通过保持状态与明确说明,让用户理解为什么没有推进。

语义 判断依据 界面表达
触碰 触摸与可观察视效 正在接触操作入口
有效操作 点击通过应用条件检查 接受本次推进
业务完成 一次合法状态更新已提交 当前步骤更新,前一步出现完成标记
取消或不可推进 取消、禁用或末步条件 状态保持,必要时解释原因

这张语义表、事件与状态记录字段,以及六类回归用例,共同限定了反馈设计的职责。光效能够增强接触感,但不会因此提供振动、湿手识别或更可靠的触控,也不能判断食物是否烹饪完成。

下一篇讨论信息折叠与展开。按钮可能换一个位置,摘要可能进入更紧凑的布局,但逐次推进、取消不推进、禁用守卫与末步停止的规则继续保留。先让操作的含义稳定,再改变它的呈现位置,布局调整就有了明确的回归依据。

相关推荐
2501_946244785 小时前
开源鸿蒙平台 CMP 三方库「compose-continuous-rounded-corner-shape」适配全流程
华为·开源·harmonyos
大雷神5 小时前
HarmonyOS7应用《拼棋派对》实战:折叠屏与电脑的窗口布局适配
harmonyos·arkui·harmonyos 7.0·玩转 arkts
2501_946244786 小时前
开源鸿蒙平台 CMP 三方库「Waterfall」适配全流程
华为·开源·harmonyos
2401_zq136y036 小时前
开源鸿蒙平台 CMP 三方库「Jewel」适配全流程
华为·开源·harmonyos
2601_949480067 小时前
开源鸿蒙平台 KMP 三方库「AutoLinkText」适配全流程
华为·开源·harmonyos
JoyCong19987 小时前
ToDesk鸿蒙PC端又焕新!远控更稳、操作更准,跨端办公如本地
运维·华为·智能手机·harmonyos·远程工作
大雷神7 小时前
HarmonyOS7应用《拼棋派对》实战:深浅主题、控件对比度与统一样式
harmonyos·arkui·harmonyos 7.0·玩转 arkts
OH_TPC8 小时前
HarmonyOS APP开发---"动效君"UI动效素材库App,需要用到这个库
harmonyos
行者-全栈开发8 小时前
【鸿蒙心迹】三方库移植(一)——NAPI 把 C/C++ 库桥接到 ArkTS 的完整链路(HarmonyOS 7.x)
harmonyos·arkts·交叉编译·c/c++·napi·三方库移植