一、引言
手机锁屏是与用户接触最频繁的交互界面之一。从传统的四位 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 状态管理
@Statemode:string='create';// 当前阶段@Statemessage:string='请绘制解锁图案...';// 提示消息@StatemessageColor:string='#86909C';// 消息颜色@StateattemptCount:number=0;// 验证错误次数@Stateunlocked:boolean=false;// 是否已解锁(隐藏 PatternLock)privatefirstPattern:Array<number>=[];// 第一次绘制的图案privatecontroller:PatternLockController=newPatternLockController();这里的关键设计决策:
- 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();}elseif(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();}}elseif(this.mode==='verify'){// 4. 验证解锁if(this.patternsMatch(input,this.firstPattern)){this.unlocked=true;this.message='✅ 解锁成功!';this.controller.setChallengeResult(PatternLockChallengeResult.CORRECT);}else{this.attemptCount++;constleft: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)returnfalse;for(leti=0;i<a.length;i++){if(a[i]!==b[i])returnfalse;}returntrue;}比较两个图案时,需要满足两个条件:
- 长度相等:两个图案必须连接了相同数量的点。长度不同意味着图案不同。
- 顺序一致:每个位置上的点编号必须相同。顺序是图案身份的核心——
[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声明:
privatecontroller:PatternLockController=newPatternLockController();这条规则同样适用于SwiperController、TabsController、TextAreaController等所有 ArkUI 控制器类。
4.2 图案存储的安全性
在我们的 Demo 中,firstPattern存储在内存变量中。在真实应用中,你绝对不能将原始图案数组存储在客户端——这存在严重的安全风险。正确的做法是只存储图案的哈希值:
// 设置图案时consthash=sha256(input.join(','));// 转为字符串后哈希storage.set('pattern_hash',hash);// 只存哈希// 验证图案时constattempt=sha256(input.join(','));conststored=storage.get('pattern_hash');constmatch=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,你就能在应用中轻松实现一个既安全又美观的图案认证系统。