图案已经被清空,下面却还显示上一轮的点序列,这种不一致很容易被当成控件刷新问题。实际原因通常更简单:连线由组件维护,结果文字由页面维护,清空其中一份并不会自动清空另一份。做图案输入练习时,先把这两种状态的归属想清楚,重置按钮的行为才会完整。

图案输入与状态重置封面

先确定这是练习输入,而不是账号凭据

PatternPad 工程是一个九点连线练习板。用户连接至少四个点,松手后页面显示经过规则检查的点序列,再点击重新开始进入下一轮。它不保存图案、不匹配预设密码,也不承担账号登录或敏感操作确认;这样可以把注意力放在输入、反馈和重置的交互关系上。

工程使用 API 12、Stage 模型,直接使用 ArkUI 的图案组件与控制器,无需额外权限。官方组件参考说明完成事件会返回依次连接的网格点索引,三行依次从零到八。组件回调提供输入结果,业务是否接受仍由页面规则决定。

我更建议先写出一轮练习的三个可观察状态:等待输入、输入通过、输入未通过。通过与未通过都保留刚才的连线,方便用户理解反馈;下一轮统一从重新开始按钮进入。这个选择避免图案瞬间消失,让用户只能凭记忆判断自己刚才连接了哪些点。

自动重置选项容易因名称产生误解。该选项控制完成后再次触摸能否清除组件状态并开始新输入,不是一个“松手立刻清空”的计时器。本例将其设为关闭,明确要求通过按钮开始下一轮;因此完成后的再次触摸不承担重置职责,按钮文案也必须清楚表达重新输入。

页面序列使用一到九显示,方便普通读者按行数点;内部规则仍按零到八判断。两种编号只在显示时转换,不能把显示数字直接回传给按索引工作的逻辑。尤其是右下角,显示为九而索引为八,混用会导致最后一个点被错误判为越界。

把输入规则写成一个只返回结果的函数

新建 common/PatternRules.ets。函数不持有组件控制器,也不写页面状态,只接收索引数组并返回显示文字。长度、取值与重复点分别验证,出错时给出明确原因;虽然正常组件回调应遵循自己的输入约定,独立规则仍便于用固定测试向量检查。

// entry/src/main/ets/common/PatternRules.ets
export function describePattern(input: number[]): string {
  if (input.length < 4 || input.length > 9) {
    throw new Error('请连接四到九个点');
  }
  const seen: Set<number> = new Set();
  for (const point of input) {
    if (!Number.isInteger(point) || point < 0 || point > 8 || seen.has(point)) {
      throw new Error('点序列不符合规则');
    }
    seen.add(point);
  }
  return input.map((point: number): string => String(point + 1)).join(' → ');
}

这里选择四到九个点,是练习板自己的规则,不是对所有图案组件或身份认证场景的通用要求。修改最小点数时,应同步修改初始提示、错误提示和测试向量,避免规则已经变成五个点,页面仍然要求四个,用户无法判断到底哪里不符合要求。

检查每个索引为整数,可以拒绝小数、非数字和越界值。集合用来拒绝重复索引,最后把内部索引转换为人可读编号。函数不保存输入数组,只生成一个新字符串;后续组件内部如何更新自己的数据,不会改变已经生成的页面结果。

测试时可以直接传入零、一、二、五,预期显示一、二、三、六。它是相邻移动组成的合法测试向量,用来核对编号转换与顺序保留。只传三个索引应失败,包含重复索引、负数或九也应失败;这些向量验证的是业务函数,不能被写成某台设备的实际手势记录。

不要只比较经过排序后的集合。图案是一个有顺序的输入,同样四个点按不同顺序连接可能表达不同路径。集合在这里只承担查重,输出仍然读取原数组的顺序;如果先排序再显示,就会把用户动作改写成另一条路径,掩盖实际经过的次序。

输入长度也不能只看最终字符串长度。显示时加入了箭头和空格,字体不同还会改变视觉宽度,这些都不是连接点的数量。规则应始终依据输入数组,页面文字仅承担反馈;把展示格式拿去反推业务值,会使一次文案调整意外改变判断行为。

一次重新开始,同时清理两种状态

页面持有一个稳定的控制器实例,并在图案完成事件里调用规则函数。通过后显示序列与完成提示;未通过时不交付序列,保留错误原因。两条分支都让组件继续显示本轮轨迹,直到用户明确点击重新开始。

// entry/src/main/ets/pages/Index.ets
import { describePattern } from '../common/PatternRules';

@Entry
@Component
struct Index {
  @State sequence: string = '--';
  @State message: string = '请连接至少四个点';
  private controller: PatternLockController = new PatternLockController();

  private complete(input: number[]): void {
    try {
      this.sequence = describePattern(input);
      this.message = `已记录 ${input.length} 个点,点击按钮开始下一轮`;
    } catch (error) {
      this.sequence = '--';
      this.message = `${(error as Error).message},请重新开始`;
    }
  }

  private restart(): void {
    this.controller.reset();
    this.sequence = '--';
    this.message = '请连接至少四个点';
  }

  build() {
    Column({ space: 14 }) {
      Text('图案练习板').fontSize(28).fontWeight(FontWeight.Bold)
      Text('完成一轮,再明确重新开始').fontSize(14).fontColor('#64748B')
      Column() {
        PatternLock(this.controller)
          .sideLength(260).circleRadius(7).pathStrokeWidth(6)
          .regularColor('#334155').selectedColor('#4338CA')
          .activeColor('#818CF8').pathColor('#6366F1')
          .autoReset(false)
          .onPatternComplete((input: number[]) => { this.complete(input); })
      }.width('100%').padding(18).backgroundColor('#FFFFFF').borderRadius(22)
      Text(`点序列:${this.sequence}`).fontSize(17).fontWeight(FontWeight.Medium)
      Text(this.message).fontSize(15).fontColor('#4338CA')
        .textAlign(TextAlign.Center)
      Button('重新开始').width('100%').height(44).backgroundColor('#4338CA')
        .onClick(() => { this.restart(); })
      Text('仅用于交互练习,不作为身份认证').fontSize(12).fontColor('#64748B')
    }.width('100%').height('100%').padding(24).backgroundColor('#F4F3FF')
  }
}

图案练习板应用初始页面

重置方法连续执行三件事:控制器清空组件输入,页面清空上轮序列,提示恢复到等待状态。它们属于一次用户动作,因此集中在同一个方法里更容易维护。若未来增加通过次数或错误说明,也应明确哪些值是本轮状态、哪些是整个练习会话的累计状态,避免一律清空。

控制器放在组件实例字段中,而不是在每次构建页面时重新创建。声明式界面可能因状态变化再次构建,控制器的身份应继续对应当前图案组件。随构建创建新控制器,会让“按钮控制的是哪一个输入状态”变得模糊,也增加排查重置异常的难度。

本例没有异步任务,也没有延迟清空。完成事件处理结束后,页面立刻展示当前结果;重新开始同样立即更新。若产品后来要求错误轨迹停留一秒再清空,就会引入计时器、销毁清理与连续输入之间的协调,这应作为新的交互规则设计,而不是在回调里随手加一段延迟。

用一致的反馈检查整轮交互

先不连线直接点击重新开始,界面应仍保持九个普通点、空序列和等待提示。这个操作应当可以重复执行,不因为当前已经为空就制造错误。然后完成一个合法图案,核对显示顺序与实际完成回调一致,点序列不是按照大小排列的编号清单。

组件输入状态与页面反馈的双重重置

再输入不足四点的图案,页面应显示规则原因而不保留上一轮成功序列。组件上的当前连线仍可见,这是为了支持反馈;点击重新开始后连线和错误提示都恢复初始状态。若只清了轨迹却保留错误文字,下一轮开始时用户仍会看到上一轮的否定信息。

完成后再次触摸图案区域,也应按关闭自动重置的设定进行检查。不要一边在文档里告诉用户必须点按钮,一边让页面自动开始第二轮。交互入口保持一致后,测试人员能清楚判断每一次完成回调属于哪一轮,也更容易复现错误反馈。

如果用户的手势跨过中间点,最终回调包含哪些点,应以目标组件实际交付的序列为准。本文规则接收完整回调结果,不从触摸坐标自行猜测路径,也不将示意图中的线段数量当成点数。实际交互验收需要覆盖边缘起点、快速滑动与不同方向。

重新开始后不要自动沿用上一轮通过状态。即使用户连续两次画出相同路径,它们也是两次独立输入;本例没有自动比对功能,因此结果只描述当前一轮。若后续要增加“重复同一图案”的练习,应单独保存目标序列,并将目标状态与本轮反馈区分,不能悄悄借用已经显示的字符串。

页面重新创建时也从空序列开始。当前工程没有恢复历史练习的需求,所以不把显示结果写入持久存储。若将来需要保存练习进度,可以保存完成次数等必要结果,而不一定需要保存每一轮的点轨迹;先明确进度代表什么,再决定保存哪些字段,通常能让数据结构更简单。

状态核对时,初始页面应是九点均未选中、序列为空、提示邀请输入。完成态则检查连线、数字顺序和文字提示是否来自同一组输入;进入下一轮后,这三处反馈一起恢复,不能只看某一个标签就判断重置完成。

界面颜色只帮助区分普通点与选中点,不能成为理解结果的唯一途径。页面始终有文字说明当前是否记录成功、记录了多少点以及如何开始下一轮。调整颜色主题时保留这些文字反馈,会比仅把线条变成红色或绿色更清楚,也便于在截图中核对状态。

图案输入区的尺寸与外围布局同样需要一起看。示例使用固定边长,适合这个单页练习的正常手机布局;迁移到更窄的窗口时,应依据可用宽度约束组件尺寸,并保留按钮可见区域。不能只缩小外层卡片,却继续让内部图案超出卡片范围。

把练习板改成真实身份确认之前,需要重新设计凭据、失败尝试控制以及系统认证能力的接入。组件负责采集图案并展示轨迹,不会因为名称里带有锁就自动提供完整认证体系。本文刻意不保存图案,页面上的序列也是练习反馈,不应复制到真实秘密输入的日志或截图中。

“重置”这个词应对应用户能够观察到的一组变化。连线消失、上轮序列清空、提示重新邀请输入,三者一致时,用户才会认为新一轮真正开始。把组件状态与页面状态分别认领,再在明确动作里一起处理,小控件也能有完整、可解释的交互闭环。

参考资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐