鸿蒙新特性——PatternLock 图案锁屏深度解析
一、引言
手机锁屏是与用户接触最频繁的交互界面之一。从传统的四位 PIN 码到指纹识别再到面容解锁,锁屏方式在不断演进。在所有这些方案中,图案锁(Pattern Lock) 凭借其简单直观的"连线"交互,在全球范围内拥有广泛的用户基础——你不需要记忆数字,只需要在 3×3 的网格上滑出一个图案。
ArkUI 提供了 PatternLock 组件,将这种经典的图案解锁交互原生封装为一个声明式组件。开发者无需自己管理触摸事件、绘制路径、计算网格坐标——PatternLock 已经处理了从触摸追踪到路径绘制、从视觉反馈到状态管理的全部底层逻辑。
PatternLock 的典型应用场景远不止锁屏:
- 儿童模式验证:防止儿童访问敏感设置
- 隐私相册入口:为私密内容增设第二道防线
- 支付确认:用图案代替密码确认交易
- 家长控制:为特定功能设置访问限制
本文将通过一个完整的**“图案锁屏创建-确认-验证”**三阶段流程,深入解析 PatternLock 的每一个核心 API。阅读完本文,你将能够:
- 掌握 PatternLock 组件的构造、样式配置和事件回调
- 理解
onPatternComplete回调中的 pattern 数组结构 - 学会使用 PatternLockController 控制组件状态
- 实现创建→确认→验证的完整锁屏流程
- 运用
setChallengeResult实现正确/错误的视觉反馈
二、PatternLock 组件核心 API 详解
2.1 组件概述
PatternLock 在屏幕上渲染一个正方形的 3×3 点阵。用户通过手指在点之间滑动来"绘制"图案——每经过一个点,该点被激活并加入当前的图案序列,同时点与点之间绘制一条连接线。当用户手指离开屏幕时,图案输入完成。
网格中 9 个点的编号如下:
0 1 2
3 4 5
6 7 8
一个从左上角开始、经过中心再到右下角的图案,其 input 数组为 [0, 4, 8]。一个 Z 字形的图案为 [0, 1, 2, 4, 6, 7, 8]。
PatternLock 的构造函数接受一个可选参数:
PatternLock(controller?: PatternLockController)
PatternLockController 是图案锁的控制器,提供两个关键方法:
reset():重置组件状态,清除当前绘制的图案,使组件回到初始状态。setChallengeResult(result: PatternLockChallengeResult):设置验证结果,用于向用户显示正确(CORRECT)或错误(WRONG)的视觉反馈。调用后,组件会短暂显示绿色或红色的闪烁效果。
2.2 样式属性
PatternLock 提供了一套完整的视觉定制 API,让你可以根据应用的设计风格自由调整外观:
PatternLock(this.controller)
.sideLength(280) // 组件宽度与高度(默认 288vp)
.circleRadius(14) // 网格点的半径(默认 6vp)
.pathStrokeWidth(14) // 连接线的粗细(默认 12vp)
.regularColor('#B0B8C8') // 未选中点的颜色
.selectedColor('#1677FF') // 选中点的颜色
.activeColor('#69B1FF') // 当前触摸点的激活色
.pathColor('#1677FF') // 连接线的颜色
.backgroundColor('#1C2536') // 组件背景色
逐项说明:
-
sideLength:控制组件的整体尺寸。这是一个正方形组件,宽度和高度相等。默认 288vp 适合大多数手机屏幕。在我们的锁屏 Demo 中设为 280vp,在深色背景上有足够的视觉冲击力。
-
circleRadius:控制网格点的半径。加大半径可以让点更容易被触摸到(对于手指较粗的用户更友好),但过大会导致点与点之间的间隙变小,影响视觉清晰度。14vp 在 280vp 的网格中约占据 10% 的网格单元格宽度。
-
pathStrokeWidth:连接线的宽度。14vp 的线宽在视觉上比较显眼,在深色背景上清晰可见。较粗的线宽还有一个好处——用户在快速滑动时不需要精确经过每个点的中心,线宽增加了容错范围。
-
颜色体系:PatternLock 使用四种颜色定义不同状态下的视觉效果。
regularColor是暗灰色,代表"尚未经过的点";selectedColor是蓝色,代表"已选中的点";activeColor是浅蓝色,代表"当前手指位置附近被高亮的点";pathColor是连接线的颜色。这四种颜色的配合产生了丰富的视觉层次——用户通过颜色就能判断哪些点已经被连接、当前手指在什么位置。
2.3 autoReset 属性
.autoReset(true) // 默认值,输入完成后自动清除图案
autoReset 控制图案输入完成后是否自动清除。当设为 true(默认值)时,用户手指离开屏幕后,图案会短暂显示(用于视觉确认),然后自动消失。当设为 false 时,图案会一直保留,直到手动调用 controller.reset()。
在我们的 Demo 中,使用默认的 autoReset(true) 并结合 setTimeout 延迟调用 controller.reset(),在图案短暂显示后自动清除,给用户足够的视觉反馈时间。
2.4 onPatternComplete 回调
.onPatternComplete((input: Array<number>) => {
// input: 用户绘制的图案数组,如 [0, 4, 8, 7]
this.onPatternComplete(input);
})
onPatternComplete 是 PatternLock 最重要的回调。当用户完成图案绘制(手指离开屏幕)时触发,参数是一个数字数组,按照用户触摸的顺序排列。每个数字对应网格中的一个点(0-8)。
在我们的实现中,onPatternComplete 是驱动整个锁屏流程的引擎——根据当前的模式(创建/确认/验证),执行不同的逻辑分支。
2.5 onDotConnect 回调
.onDotConnect(callback: Callback<number>)
onDotConnect 在用户的手指每经过一个新的点时触发,参数是该点的编号(0-8)。这个回调用于实时追踪图案的绘制过程,通常用于以下场景:
- 实时显示已连接点的数量
- 在点被激活时播放触觉反馈
- 限制图案的最小长度(在连接第 N 个点之前提示用户)
在我们的 Demo 中未使用此回调,因为图案验证只关心最终结果而非实时过程。但如果你需要在绘制过程中提供实时反馈,这个回调是必不可少的。
三、实战:图案锁屏完整流程
3.1 三阶段流程设计
我们的图案锁屏 Demo 实现了完整的创建→确认→验证三阶段流程:
-
创建阶段(mode: ‘create’):用户首次绘制一个解锁图案。图案必须至少包含 4 个点。绘制完成后,图案被保存到内存变量中。
-
确认阶段(mode: ‘confirm’):用户再次绘制相同的图案来确认。如果两次绘制一致,进入验证阶段;如果不同,回到创建阶段重新开始。
-
验证阶段(mode: ‘verify’):用户绘制已设定的图案来进行解锁。如果匹配,显示"解锁成功";如果不匹配,消耗一次尝试机会(总共 5 次),超过次数后锁定。
这种三阶段设计模拟了真实锁屏的首次设置和使用流程,让 Demo 在演示层面有完整的业务闭环。
3.2 状态管理
@State mode: string = 'create'; // 当前阶段
@State message: string = '请绘制解锁图案...'; // 提示消息
@State messageColor: string = '#86909C'; // 消息颜色
@State attemptCount: number = 0; // 验证错误次数
@State unlocked: boolean = false; // 是否已解锁(隐藏 PatternLock)
private firstPattern: Array<number> = []; // 第一次绘制的图案
private controller: PatternLockController = new PatternLockController();
这里的关键设计决策:
- mode 使用
@State:因为 mode 的变化直接影响 UI 显示(消息文本、步骤指示器等),需要触发 UI 刷新。 - controller 使用
private:PatternLockController 是一个原生对象,不需要也不应该被@State追踪。这与 SwiperController、TabsController 的模式一致。 - firstPattern 使用
private:它是内部校验数据,不直接渲染到 UI 上(UI 通过 mode 间接反映),因此不需要@State。
3.3 核心回调逻辑
onPatternComplete(input: Array<number>): void {
// 1. 长度校验
if (input.length < 4) {
this.message = '至少需要连接 4 个点,请重新绘制';
this.messageColor = AppColors.ERROR;
this.controller.setChallengeResult(PatternLockChallengeResult.WRONG);
this.resetAfterDelay();
return;
}
if (this.mode === 'create') {
// 2. 首次创建:保存图案,切换到确认模式
this.firstPattern = input;
this.mode = 'confirm';
this.message = '请再次绘制相同图案以确认';
this.messageColor = AppColors.PRIMARY;
this.resetAfterDelay();
} else if (this.mode === 'confirm') {
// 3. 确认图案:对比两次输入
if (this.patternsMatch(input, this.firstPattern)) {
this.mode = 'verify';
this.message = '图案设置成功!请绘制图案验证解锁';
this.messageColor = '#52C41A';
this.controller.setChallengeResult(PatternLockChallengeResult.CORRECT);
this.resetAfterDelay();
} else {
this.message = '两次绘制不一致,请重新设置图案';
this.messageColor = AppColors.ERROR;
this.controller.setChallengeResult(PatternLockChallengeResult.WRONG);
this.mode = 'create';
this.firstPattern = [];
this.resetAfterDelay();
}
} else if (this.mode === 'verify') {
// 4. 验证解锁
if (this.patternsMatch(input, this.firstPattern)) {
this.unlocked = true;
this.message = '✅ 解锁成功!';
this.controller.setChallengeResult(PatternLockChallengeResult.CORRECT);
} else {
this.attemptCount++;
const left: number = 5 - this.attemptCount;
if (left <= 0) {
this.message = '❌ 错误次数过多,请重新设置图案';
this.unlocked = true; // 触发锁定态
} else {
this.message = `❌ 图案错误,还剩 ${left} 次机会`;
this.controller.setChallengeResult(PatternLockChallengeResult.WRONG);
this.resetAfterDelay();
}
}
}
}
此方法是一个典型的状态机实现——根据当前的 mode 决定如何处理输入。让我们按分支深入分析:
分支 1:长度校验。在三个模式中,我们都要求图案至少包含 4 个点。少于 4 个点的图案安全性太低(3 个点的图案组合太少,容易被猜到),4 个点被认为是最低安全标准。如果图案太短,通过 setChallengeResult(WRONG) 显示红色闪烁提示,并在 600ms 后自动重置组件。
分支 2:创建模式。首次绘制成功后,将图案保存到 firstPattern,模式切换到 confirm,消息引导用户再次绘制。这里 resetAfterDelay() 在短暂延迟后清除 PatternLock,让组件为第二次输入做好准备。
分支 3:确认模式。通过 patternsMatch() 逐位比较两个图案数组。这里不能直接使用 === 或 JSON.stringify 比较数组——ArkTS 中两个数组即使内容相同,引用也不同。最稳妥的比较方式是逐元素对比长度和每个位置的值。
如果确认成功,调用 setChallengeResult(CORRECT) 显示绿色闪烁,告诉用户"操作正确"的视觉反馈。如果确认失败,不仅清空 firstPattern,还将模式重置回 create,让用户从头开始。
分支 4:验证模式。这是正常使用阶段的入口。正确则 unlocked = true(隐藏 PatternLock,显示成功页面);错误则消耗一次尝试机会。5 次尝试的限制是仿照真实系统锁屏的安全策略——防止暴力破解。达到上限后 unlocked = true,但显示的是"已锁定"态(需重新设置)而非"欢迎"态。
3.4 图案比较算法
patternsMatch(a: Array<number>, b: Array<number>): boolean {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
比较两个图案时,需要满足两个条件:
- 长度相等:两个图案必须连接了相同数量的点。长度不同意味着图案不同。
- 顺序一致:每个位置上的点编号必须相同。顺序是图案身份的核心——
[0, 4, 8]和[8, 4, 0]是不同的图案,尽管经过了相同的点。
3.5 步骤指示器设计
getStepIndicator(): string {
if (this.mode === 'create') return '●●○○';
if (this.mode === 'confirm') return '●●●○';
if (this.mode === 'verify') return '●●●●';
return '';
}
步骤指示器使用 ● 和 ○ 字符的排列直观地展示当前处于三阶段流程中的哪一步。这是一种简单但有效的 UX 设计——用户不需要阅读长段文字就能理解自己在哪里、还有几步。
结合下方的描述文字(“第 1 步:设置图案” / “第 2 步:确认图案” / “第 3 步:验证解锁”),构成了完整的进度指引系统。
3.6 延迟重置机制
resetAfterDelay(): void {
setTimeout(() => {
this.controller.reset();
}, 600);
}
600ms 的延迟是一个经过权衡的值:
- 太短(< 200ms):用户来不及看到自己绘制的图案,缺乏确认感
- 太长(> 1000ms):用户会产生等待感,体验不够流畅
- 600ms:恰好让图案在屏幕上停留一下,用户能确认自己画了什么,然后自然消失
3.7 重置与跳过功能
resetAll(): void {
this.mode = 'create';
this.message = '请绘制解锁图案(至少连接 4 个点)';
this.messageColor = AppColors.TEXT_TERTIARY;
this.firstPattern = [];
this.attemptCount = 0;
this.unlocked = false;
this.controller.reset();
}
resetAll() 将所有状态变量恢复到初始值。这个方法在任何阶段都可以调用(底部有"重置图案"按钮),让用户可以随时从头开始。controller.reset() 确保 PatternLock 组件也清空显示。
"跳过设置"按钮是一个便捷的 Demo 入口——它直接将 unlocked 设为 true,用户无需完成整个流程就能看到"解锁后"的界面。这在 Demo 演示中非常实用,与真实应用中的"退出登录"或"关闭锁屏"功能对应。
四、进阶技巧与最佳实践
4.1 PatternLockController 不能是 @State
这是 PatternLock 使用中最常见的错误。PatternLockController 是一个内部包含原生调用方法的对象,将其声明为 @State 会导致框架尝试对其内部状态进行深拷贝和代理追踪,这不仅浪费性能,还可能在运行时产生未预期的行为。
正确做法是使用 private 声明:
private controller: PatternLockController = new PatternLockController();
这条规则同样适用于 SwiperController、TabsController、TextAreaController 等所有 ArkUI 控制器类。
4.2 图案存储的安全性
在我们的 Demo 中,firstPattern 存储在内存变量中。在真实应用中,你绝对不能将原始图案数组存储在客户端——这存在严重的安全风险。正确的做法是只存储图案的哈希值:
// 设置图案时
const hash = sha256(input.join(',')); // 转为字符串后哈希
storage.set('pattern_hash', hash); // 只存哈希
// 验证图案时
const attempt = sha256(input.join(','));
const stored = storage.get('pattern_hash');
const match = attempt === stored;
这样即使攻击者获取了本地存储的数据,也无法还原出原始图案。
4.3 最小图案长度的权衡
我们的 Demo 要求至少 4 个点。从安全角度看:
- 1 个点:9 种可能(极其不安全)
- 2 个点:约 56 种可能(不安全)
- 3 个点:约 320 种可能(不够安全)
- 4 个点:约 1624 种可能(基本安全)
- 5 个点:约 7152 种可能(较安全)
- 6-9 个点:显著提高,但用户体验下降
Android 的图案锁默认要求至少 4 个点,这是一个业界广泛接受的平衡点。如果你的应用处理的是高度敏感信息(如金融交易),建议要求至少 5-6 个点。
4.4 错误次数限制
5 次尝试后锁定的策略(我们的 Demo 中使用)是仿照 iOS 的"10 次错误后擦除"和 Android 的"5 次后锁定 30 秒"的中间方案。在生产环境中,你可能还需要加入:
- 递增等待时间:第 1-3 次无延迟,第 4 次等待 30 秒,第 5 次等待 1 分钟
- 强制密码回退:超过限制后要求输入备用密码
- 账户锁定:将错误历史上传到服务器,由后台决定是否锁定账户
五、总结
本文以图案锁屏为业务场景,深入解析了 ArkUI PatternLock 组件的核心 API 和完整的创建→确认→验证三阶段流程。
回顾本文覆盖的核心要点:
-
PatternLock 组件结构:9 个点的 3×3 网格,用户通过滑动连接点来绘制图案。组件自动处理触摸追踪、路径绘制和状态管理。
-
样式体系:
sideLength控制尺寸,circleRadius控制点大小,pathStrokeWidth控制线宽,regularColor/selectedColor/activeColor/pathColor四色体系定义不同状态的视觉效果。 -
核心回调:
onPatternComplete(input: Array<number>)是图案输入完成的回调,input数组按顺序记录了被连接点的编号(0-8)。 -
控制器能力:
PatternLockController提供reset()重置组件状态和setChallengeResult()设置正确/错误视觉反馈。 -
三阶段流程:创建(首次绘制)→ 确认(再次绘制)→ 验证(解锁校验),通过
mode状态变量驱动状态机切换。 -
安全机制:最小 4 点长度要求、图案精确对比算法、5 次错误尝试限制,以及生产环境中的哈希存储建议。
PatternLock 是 ArkUI 中对触摸交互封装程度最高的组件之一——它将复杂的多点触摸追踪、路径计算和视觉动画全部内聚在一个组件中。掌握 PatternLock,你就能在应用中轻松实现一个既安全又美观的图案认证系统。
更多推荐




所有评论(0)