# 鸿蒙 HarmonyOS 应用开发实战(第38期)|密码锁(Password Lock)— Grid 网格键盘与四阶段状态机

# 鸿蒙 HarmonyOS 应用开发实战(第38期)|密码锁(Password Lock)— Grid 网格键盘与四阶段状态机
一、应用概述密码锁Password Lock是一款模拟数字密码锁的应用。它模拟了真实密码锁的完整操作流程设置密码 → 确认密码 → 解锁验证 → 错误锁定。用户通过数字键盘输入 4 位密码系统根据当前阶段执行不同逻辑错误次数超过 5 次后触发 5 秒锁定机制。从技术角度看这是系列教程中状态机设计最为复杂的一个应用。它包含四个明确的阶段set/confirm/unlock/locked每个阶段对用户输入的响应完全不同。此外应用还引入了Grid网格布局构建数字键盘、Circle组件作为密码点显示、setTimeout一次性定时器等功能是状态管理和布局设计的集大成者。二、技术架构与组件树2.1 整体架构Entry Component struct index38 ├── Column (根容器) │ ├── Row (顶部导航栏) │ └── Column (主内容区, 居中) │ ├── Text (提示消息, 颜色动态变化) │ ├── Row (密码点, 4个 Circle) │ │ └── ForEach → Circle × 4 │ ├── Grid (数字键盘, columnsTemplate1fr 1fr 1fr) │ │ ├── GridItem × 9 (1 ~ 9) │ │ ├── GridItem () │ │ ├── GridItem (0) │ │ └── GridItem (⌫ 删除键) │ └── Button ( 重置)2.2 状态变量设计State password: string ; // 当前输入的密码 State savedPassword: string 1234; // 已保存的密码 State stage: set | confirm | unlock | locked set; State confirmPassword: string ; // 确认阶段暂存的密码 State message: string 设置4位数字密码; State attempts: number 0; // 错误尝试次数 State locked: boolean false; // 是否锁定 lockTimer: number -1; // 锁定定时器 ID联合类型状态stage使用了 TypeScript/ArkTS 的字符串联合类型set | confirm | unlock | locked将变量的取值范围限制在四个预定义字符串中。这种类型约束可以在编译阶段捕获拼写错误。三、核心代码深度解析3.1 四阶段状态机┌──────────┐ │ set (设置) │ └─────┬────┘ │ 输入4位密码 ▼ ┌──────────┐ │confirm(确认)│ └─────┬────┘ ┌───┴───┐ │ │ 匹配 │ │ 不匹配 │ │ ▼ ▼ ┌────────┐ ┌──────────┐ │ unlock │ │set (重新) │ │ (解锁) │ └──────────┘ └──┬─────┘ ┌───┴───┐ │ │ 正确 │ │ 错误(≥5次) │ │ ▼ ▼ ┌─────────┐ ┌────────┐ │解锁成功 │ │ locked │ └─────────┘ │(锁定5s)│ └────────┘这个状态机是密码锁的核心逻辑。pressNum函数根据当前stage执行不同的分支pressNum(n: string) { if (this.locked) return; if (this.password.length 4) return; this.password n; if (this.password.length 4) { if (this.stage set) { // 阶段1设置密码 this.savedPassword this.password; this.confirmPassword this.password; this.stage confirm; this.message 请再次输入确认; this.password ; } else if (this.stage confirm) { // 阶段2确认密码 if (this.password this.confirmPassword) { this.stage unlock; this.message ✅ 密码设置成功试试解锁; this.savedPassword this.confirmPassword; } else { this.message ❌ 两次密码不一致重新设置; this.stage set; } this.password ; } else if (this.stage unlock) { // 阶段3解锁验证 if (this.password this.savedPassword) { this.message 解锁成功; this.locked false; } else { this.attempts; this.message ❌ 密码错误 (${this.attempts}/5); if (this.attempts 5) { // 阶段4锁定 this.locked true; this.message 尝试次数过多已锁定; this.lockTimer setTimeout(() { this.locked false; this.attempts 0; this.message 可以重新尝试; }, 5000); } } this.password ; } } }每个阶段的分析阶段 set设置密码输入 4 位数字后自动进入 confirm 阶段密码同时保存在savedPassword和confirmPassword中阶段 confirm确认密码再次输入 4 位密码与confirmPassword比对匹配 → 进入 unlock 阶段显示成功消息不匹配 → 回到 set 阶段重新设置显示错误消息阶段 unlock解锁验证输入 4 位密码与savedPassword比对正确 → 解锁成功locked false错误 →attempts达到 5 次进入 locked 阶段阶段 locked锁定locked true输入被阻止if (this.locked) returnsetTimeout5 秒后自动解锁解锁后恢复可输入状态attempts归零3.2 密码点显示Row({ space: 12 }) { ForEach([0, 1, 2, 3], (i: number) { Circle() .width(16).height(16) .fill(this.password.length i ? #333 : #ddd) .stroke(#bbb).strokeWidth(1) }) }.margin({ bottom: 24 })四个Circle圆形代表四位密码的输入状态已输入password.length i填充深色 #333未输入password.length i填充浅色 #ddd这种「圆点填充」的密码视觉反馈是行业标准做法——既让用户知道已输入了几位数字又不会泄露密码内容。3.3 Grid 网格数字键盘Grid() { ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, , 0, ⌫], (n: string, idx: number) { GridItem() { if (n ⌫) { Text(⌫).fontSize(28).fontColor(#F44336) .width(100%).height(60).textAlign(TextAlign.Center) .onClick(() this.delete()) } else if (n ) { Text() } else { Text(n).fontSize(28).fontWeight(FontWeight.Bold) .width(100%).height(60).textAlign(TextAlign.Center) .onClick(() this.pressNum(n)) } } }) } .columnsTemplate(1fr 1fr 1fr) .rowsTemplate(60px 60px 60px 60px) .width(240).rowsGap(4).columnsGap(4)Grid 关键属性属性值说明columnsTemplate1fr 1fr 1fr三列等宽布局rowsTemplate60px 60px 60px 60px四行每行 60pxrowsGap4行间距 4pxcolumnsGap4列间距 4px1fr是 Grid 布局中的弹性系数表示将可用空间按比例分配。1fr 1fr 1fr就是三等分。键盘布局12 个 GridItem 排成 4 行 × 3 列1 2 3 4 5 6 7 8 9 0 ⌫第 4 行第一个位置索引 9是空字符串用于占位因为标准数字键盘是 3×4 布局左下角通常是空位或符号键。删除键⌫使用红色#F44336与数字键区分点击触发delete()方法delete() { this.password this.password.slice(0, -1); }使用slice(0, -1)删除字符串的最后一个字符实现退格功能。3.4 重置功能reset() { clearTimeout(this.lockTimer); this.password ; this.stage set; this.attempts 0; this.locked false; this.message 设置4位数字密码; }重置将应用恢复到最初始的状态set 阶段。注意clearTimeout(this.lockTimer)清除锁定定时器——如果用户在被锁定时点击重置应该立即解除锁定。四、HarmonyOS 特性分析4.1 Grid 网格布局 vs 其他布局布局适用场景本例使用Column/Row线性排列顶部导航、主框架Stack层叠重叠未使用密码锁不需要重叠Grid二维网格数字键盘List滚动列表未使用Grid是最适合键盘布局的容器。相比用Column 多个Row模拟网格Grid的columnsTemplaterowsTemplate可以精确控制行列分布代码也更简洁。4.2 setTimeout 一次性定时器this.lockTimer setTimeout(() { this.locked false; this.attempts 0; this.message 可以重新尝试; }, 5000);与setInterval周期性执行不同setTimeout只在指定延迟后执行一次。它非常适合延迟操作密码锁的定时解锁动画序列先 A 动画500ms 后 B 动画防抖用户停止输入 300ms 后再搜索同样需要在aboutToDisappear或reset中clearTimeout清理。4.3 Circle 组件作为状态指示器密码锁使用 4 个Circle作为密码输入指示器这与第 31 期模拟时钟使用Circle作为表盘和中心点的用法不同。同一个组件在不同场景中扮演不同角色体现了 ArkTS 组件系统的高度灵活性。五、UI/UX 设计与最佳实践5.1 消息提示的动态颜色Text(this.message).fontSize(16) .fontColor( this.message.includes(✅) ? #4CAF50 : this.message.includes(❌) ? #F44336 : #666)消息文字的颜色根据内容自动变化包含 ✅ → 绿色成功包含 ❌ → 红色失败其他 → 灰色中性这种「内容驱动颜色」的模式无需为每种消息单独设置颜色变量简洁且不易出错。5.2 锁定状态的视觉反馈当locked true时所有按键操作被阻止if (this.locked) return消息显示锁定提示用户点击任何数字键都无响应这种「软禁用」比隐藏键盘更合适——用户看到键盘但不能操作明白系统处于锁定状态。5.3 用户体验流程设置密码 → 确认密码 → 解锁验证 → 错误 5 次→ 锁定 → 自动解锁这个流程模拟了真实设备上设置和解锁密码的完整体验让用户在一个页面内完成所有操作无需跳转。六、可扩展思路更长的密码支持 4-8 位可变长度密码。生物识别集成指纹或面部识别ohos.userIAM.userAuth作为解锁方式。安全键盘每次打开键盘时随机排列数字键顺序防止偷窥。应用锁整合将密码锁作为应用锁保护其他应用的安全。密码找回设置安全问题或备用邮箱用于密码找回。七、总结密码锁应用以 124 行代码实现了一个包含四阶段状态机的完整密码管理系统。它的技术亮点包括联合类型状态机set | confirm | unlock | locked精确控制应用状态Grid 网格键盘columnsTemplaterowsTemplate构建标准数字键盘Circle 密码指示器圆点填充状态反映密码输入进度setTimeout 锁定机制5 次错误后自动锁定 5 秒动态消息颜色根据消息内容自动匹配颜色slice 删除字符串退格操作的简洁实现这个应用的状态机架构是整个系列的亮点——四个阶段、每个阶段对输入的不同响应、错误阈值、定时解锁——这些概念可以直接应用于登录页面、支付验证、权限控制等真实场景。下一期我们将从安全领域转向创意表达探讨一个充满趣味性的应用——手势绘制Drawing Canvas看如何用 onTouch 事件构建涂鸦画板。本文是 HarmonyOS 应用开发实战系列第 38 期更多内容请关注专栏。