从一个设备认证页面读懂 ArkUI 状态切换:第 104 个示例的实现、验证与边界

这个示例的页面看起来像一张跨设备认证卡片:它列出一台名为 MatePad 11 的待认证设备,提供账号认证、近场认证、扫码认证三个选择,并给出“确认认证”按钮。开始分析前,范围必须先说清楚。它不是一套连接真实设备的认证服务,也没有接入账号、近场硬件、相机扫描或可信设备管理接口;这是一个用本地状态把认证前后界面做成可点击演示的 ArkUI 单页。它最适合练习的,是一次点击如何让标题、设备状态、按钮和提示语同步变化。

初学 ArkTS 时,常见的写法是把“当前选中的方式”“是否认证成功”“底部提示”当成彼此无关的文字,再在事件里尝试逐个改控件。这个工程采用更直接的声明式思路:先保存少量事实,再让界面从事实推导。trusted 表示本页当前是否显示为可信,method 表示当前选择哪一种方法,notice 保存底部最近一次要展示的说明。只要这几项改变,使用它们的表达式会给出新的画面。理解这种关系,比记住某一个组件的圆角或颜色更重要。

工程页面的入口是 ohos/entry/src/main/ets/pages/Index.ets,文章中的能力判断都可以回到源码核对;对页面文字提到、但源码没有实现的内容,会明确标注为边界。这样读者运行页面时,看到的画面应当和文章对得上,也不会把“发现于同一局域网”这样的展示文字误读成真实设备发现结果。

首次启动时的页面

1. 工程从哪里启动,页面为何直接出现

第 104 个工程位于文章目录内的 ohos 子目录。应用级 AppScope/app.json5 给出包名 com.example.nativearticle104、版本信息、图标和应用标签;构建配置采用 HarmonyOS 的 5.0.0(12) 兼容及目标 SDK。这些配置说明工程的构建目标,但不参与页面中的“认证”判断。

页面入口可以沿着一条很短的链路追踪。entry/src/main/module.json5EntryAbility 声明为 mainElement,同时通过 $profile:main_pages 指向页面清单。resources/base/profile/main_pages.json 的清单只有 pages/IndexEntryAbility.etsonWindowStageCreate 中调用 windowStage.loadContent('pages/Index'),所以应用启动后直接装载 Index.ets。没有第二个业务页、路由跳转选择,也没有从外部带入页面参数。

Index 同时标注了 @Entry@Component。前者使该组件可作为页面入口渲染,后者表示它遵循 ArkUI 的组件结构。工程没有导入网络请求、分布式设备发现、设备管理、相机或身份认证相关模块。这一点决定了后续阅读的尺度:三种认证方式是可选择的字符串,确认按钮是本地状态切换,不会发起登录、扫描或系统级可信关系写入。

根部使用 Scroll 包住 Column。当前内容不多,使用滚动容器仍有现实意义:系统字体放大或文本换行后,内容仍有纵向承载空间。它并不等同于已经完成横屏、折叠屏或平板断点适配;源码没有相关分支,模块配置的 deviceTypes 也仅写了 phone。不要因为页面使用了百分比宽度就夸大为“全设备适配完成”。

运行时如果页面未显示,优先检查“模块配置中的 mainElement、页面清单中的 pages/IndexloadContent 的字符串和文件位置”是否一致。这个检查顺序比先改 build() 中的布局可靠得多。对只有一个页面的示例,入口问题和渲染问题应分开判断。

2. 三个状态与一个固定列表

源码开头定义了三项响应式状态和一个普通数组:

@State trusted: boolean = false;
@State method: string = '账号认证';
@State notice: string = 'MatePad 11 等待认证';
private methods: string[] = ['账号认证', '近场认证', '扫码认证'];

首次渲染时,trustedfalse,所以顶部横幅为蓝色,说明为“保护跨设备访问前的身份确认”;设备右侧显示“待验证”;按钮写“确认认证”。method 的初始值是“账号认证”,第一项方式行便以浅蓝背景高亮并显示“已选”。notice 则直接显示在底部的浅黄色区域。这些首屏信息不是分别硬编码在多个地方的副本,而是三个初值被不同 UI 表达式读取后的结果。

@State 的实际作用不是给变量起一个“更高级”的名字,而是告诉组件:当这个值被重新赋值时,依赖它的声明会用新值重新计算。比如 trusted 同时影响顶部背景、顶部副标题、设备右侧状态字、状态字颜色、按钮文字、按钮背景、按钮文字颜色和确认后的提示。事件只需更新一个布尔值,所有这些位置就会保持同一状态。这个例子很适合把“状态驱动 UI”拆成具体可见的关系,而不是只记一句抽象口号。

methods 没有使用 @State,这是合理的。它在本页中从不被修改,只是作为固定来源绘制三行选项。不会变化的数据不必伪装成响应式事实。若未来要从服务端取得方法、根据硬件决定可用项,才需要重新设计数据来源与更新过程;那是扩展需求,不属于当前文件的实现。

三个状态也不会保存到磁盘、偏好设置或远端服务。停止并重新运行页面,新的组件实例仍从 false、“账号认证”和“MatePad 11 等待认证”开始。页面上“可信设备组”“可访问协同服务”属于成功状态下的展示文案,不能据此推断真实设备关系已经持久化,或应用已有权限放行能力。

3. 从外到内阅读组件树

build() 的最外层是占满宽高的 Scroll,背景 #F4F8FC。里面的 Column({ space: 16 }) 纵向排列三个区域:顶部状态横幅、白色认证卡、底部通知。外层 padding(18) 让内容与页面边缘留出距离。这个结构简单,却能让读者明确每个区域的职责,不必在众多嵌套组件之间猜测。

顶部横幅是一个 Column({ space: 7 })。其中“设备认证”使用 26 号粗体白字;第二行依赖 trusted,未认证时显示“保护跨设备访问前的身份确认”,认证后显示“认证成功 · 设备已进入可信设备组”。横幅始终具有 20 的内边距和 20 的圆角,只有背景会从蓝色 #1263C6 换为绿色 #108A5B。它没有 onClick,不是步骤条或真实认证入口,只承担当前状态摘要。

中间白色卡片使用 padding(18)borderRadius(18),内部以 space:12 排列设备摘要、分隔线、认证方式标题、方式列表和主按钮。卡片先以“待认证设备”标识内容,再用 Row 放置设备名和状态。左侧小列有设备名 MatePad 11 和“平板 · 发现于同一局域网”;右侧由 trusted 决定“待验证”或“可信”。这两行左侧只是固定 Text,并非运行时检索结果。

左侧 Column 调用 layoutWeight(1),使它在同一个 Row 中占据余下空间,右侧状态文字自然靠右。它避免了给设备名和状态写死像素宽度。名称变长时左列拥有更大的伸展空间,但源码没有限制最大行数、截断策略或多语言长度处理,因此不能声称极长名称已经被完整适配。

Divider() 把设备摘要与选项区隔开。再下方是“认证方式”标题、由 ForEach 绘制的三条方式行以及主按钮。卡片外部还有一块单独的通知文本。把可操作区域和结果区域分开,使用户可以一眼看出点击后是方式高亮变化,还是信任状态变化。

确认认证后的页面

动作截图展示的不是跳到新页面,而是同一组件在 trusted=true 后的重建结果:横幅变绿,设备变“可信”,按钮变“解除信任”,底部显示账号认证的成功提示。三个认证方式仍在原位置,账号认证仍被选择。这个对照正好能帮助理解状态并非只影响某一个按钮。

4. ForEach 生成互斥的方式选择

方式列表只写了一套行布局,由 ForEach(this.methods, ...) 重复三次。每一次循环得到一个 item,它既用于显示文字,也用于和当前 method 比较:

ForEach(this.methods, (item: string) => {
  Row() {
    Text(item).layoutWeight(1).fontSize(15)
    Text(this.method === item ? '已选' : '').fontSize(13).fontColor('#1263C6')
  }
  .backgroundColor(this.method === item ? '#E9F3FF' : '#F8FAFC')
  .onClick(() => {
    this.method = item;
    this.notice = '已选择' + item + ',请完成确认。';
  })
})

当且仅当 this.method === item 时,当前行得到浅蓝 #E9F3FF 与“已选”文字;其他行用 #F8FAFC,右侧空字符串不显示任何标记。页面只保存一个 method,所以自然保证三项中最多一项被标记为已选。对固定而互斥的选择,这比维护三个布尔值更易读,也减少了同时出现多个选中项的机会。

点击方式行只写入 methodnotice。它不会把 trusted 设成 true,所以选择“近场认证”后的顶部仍是蓝色、设备仍待验证、主按钮仍写确认认证。这是刻意分开的两步:先选择一种展示中的方式,再确认本页的信任状态。阅读时不要因为选项名称带有“认证”就误以为点击行本身已经认证完成。

快速连续切换也很直观:最后点击的字符串留在 method 中,最后点击的行高亮,通知显示最后一种选择。代码没有动画、防抖、禁用态或异步等待;初始状态已经有默认方法,因此也没有“必须先选一项”的校验分支。此处的即时变化来自内存赋值,不表示网络响应速度。

如果只在数组后面增加一个字符串,界面确实会多出一行,且能被选中;但这只是 UI 列表的可扩展性。添加“蓝牙认证”等文字不会自动获得蓝牙能力。真实能力需要对应 API、权限、用户授权、异步结果和失败处理。把“可多一行”描述为“已支持新认证技术”是不准确的。

5. 主按钮为何能在两种状态间来回切换

主按钮的标题、背景与字体颜色都读取 trusted。未信任时它是蓝底白字“确认认证”;已信任时是浅绿底深绿字“解除信任”。点击回调的核心是:

this.trusted = !this.trusted;
this.notice = this.trusted
  ? 'MatePad 11 已通过' + this.method + ',可访问协同服务。'
  : '已解除信任,需要重新认证。';

第一行先将布尔值取反,第二行读取的是更新后的值。首次点击从 falsetrue,通知走成功分支,并把当前选择的方式拼入句子;再次点击从 true 回到 false,通知走解除分支。这个顺序很值得实际单步观察。若把第二行误当作读取旧值,就无法解释为什么第一次点击马上出现成功说明。

确认后再点击某一种方式,页面允许 method 改变,但 trusted 保持 true。所以顶部仍为成功状态、设备仍显示可信,底部则重新出现“已选择……请完成确认。”。这不是隐藏故障,而是现有三个变量独立写入后的真实行为。对于真实安全产品,这种组合是否合理要由业务规则决定,例如可规定改方式后必须重新认证;当前工程没有这条规则,也没有实现它。

解除信任同样没有二次确认、加载状态或失败分支,它立即改变本地状态;回调没有请求服务器,没有调用系统设备服务,也没有检查当前选择。页面说“可访问协同服务”只是认证成功情景的文字,不对应任何已存在的协同服务页面或权限校验。把 UI 演示与安全结论分开,是理解这份源码时最重要的边界。

6. 不要笼统地说“页面刷新”,逐项对应状态

为了排查和讲解,最好把每个状态影响的可见结果写清。trusted=false 时:横幅背景是 #1263C6,副标题是保护身份确认;设备状态为“待验证”,颜色 #C77B00;按钮为蓝底白字的“确认认证”。trusted=true 时:横幅背景是 #108A5B,副标题为认证成功;设备状态为绿色“可信”;按钮变为浅绿背景、深绿文字的“解除信任”。这是同一个布尔值在多个表达式中的实际投影。

method 的职责较窄:它决定哪一行显示“已选”、哪一行取浅蓝背景,并在成功通知中被拼接。它不会决定横幅颜色、设备状态或按钮标题。notice 更单纯,只出现在最下方的 Text,该文本字号 14、行高 21、颜色 #486581,背景 #FFF8E6,内边距 15、圆角 14。认识到各变量的影响范围后,遇到问题就知道从哪里找。

例如,若点击近场认证后高亮未移动,应先检查回调是否仍执行 this.method = item,以及高亮条件是否仍与 item 比较;若底部没有新句子,检查 notice 是否被赋值。若点击确认后横幅没有变绿,检查 trusted 是否发生取反和背景表达式是否读取它。每一种表现都能追溯到少数代码位置,不需要一开始就怀疑 Scroll 或布局容器。

声明式状态的优势在于避免多份副本不一致。命令式地逐个改文本时,可能只改了按钮忘了改设备标签,用户会看到“可信”与“确认认证”同时存在。这里不同位置都读同一事实,只要 trusted 正确,画面就不会由手工漏改而自相矛盾。不过状态设计仍需谨慎:notice 是一段展示文字,不是完整的认证记录;如果将来要表达请求中、错误码、时间或多台设备,单一字符串就不足以承担全部业务语义。

7. 样式与布局中可观察的取舍

页面的视觉效果全部由原生 ArkUI 基础组件和修饰属性组成,没有自定义绘制、图片背景或复杂主题配置。根部 18 的边距、区域间 16 的空隙、卡片内 18 的内边距、卡片子项 12 的间距,形成从页面到内容的逐层留白。数字本身不是通用标准,但在本页前后保持一致,阅读代码时能看见布局节奏。

颜色承担状态识别而非单纯装饰。蓝色同时出现在未认证横幅和确认按钮,强调当前需要处理;成功后顶部、设备标签、按钮文字都改为绿色,形成一致的成功线索。选中方式用浅蓝而非深色,避免与主按钮竞争;底部提示采用偏暖的浅黄,提醒用户注意刚发生的动作。源码未定义错误态、深色模式、动态主题、禁用态或状态图标,文章不能把常见设计习惯写成这里已经完成的能力。

文字层级也来自具体设置:主标题 26 号粗体,设备名 19 号粗体,方式标题 15 号中等字重,辅助文本多为 13 或 14 号。设备说明用 #627D98,状态文字用黄褐或绿色,使信息层级清楚。截图中没有出现截断,但源码也没有写最大行数或省略规则,因此长设备名、多语言文案和大字号是否表现良好,需要在真实需求出现后单独测试。

多个区域都写了 width('100%'),让横幅、卡片、方式行和按钮适应当前可用内容宽度,而不是硬编码为某个手机像素。根部 Scroll 同时有 height('100%')。这提供了合理的窄屏基础,却不是对所有窗口尺寸的承诺。工程清单明确面向手机,读者应把“百分比布局”与“完成多终端适配”区分开。

8. 在 DevEco Studio 中按路径验证

第一条是首屏验证。打开 ohos 工程并运行到可用手机设备或模拟器后,应看到浅灰蓝背景、蓝色横幅、白色认证卡和浅黄色通知区。默认方式为账号认证,右侧有“已选”;设备标签是待验证;按钮是确认认证;底部是“MatePad 11 等待认证”。这一条验证入口、初值和布局,不验证真实局域网发现。

第二条是选择验证。点击近场认证,预期仅近场行变为浅蓝并显示已选,账号认证恢复普通背景,底部改为“已选择近场认证,请完成确认。”。再点扫码认证,应按相同规律切换。顶部、设备状态和按钮此时不变,因为方式回调没有写 trusted。若它们改变,应回到 Index.ets 检查当前回调和状态初值。

第三条是确认验证。任选方式后点击确认认证,预期横幅由蓝变绿,副标题转为成功说明,设备右侧从待验证变可信,按钮改为解除信任;底部应包含刚选的方法,例如“MatePad 11 已通过扫码认证,可访问协同服务。”。这验证 trustedmethod 的组合显示。页面不会寻找真实 MatePad 11,也不会因为没有网络而进入失败态。

第四条是解除验证。紧接着点击解除信任,预期顶部和设备标签回到未认证表现,按钮恢复确认认证,底部改为“已解除信任,需要重新认证。”。选中方式不会恢复默认,因为解除回调没有重置 method。这一点能清楚说明信任状态和选择状态是两份数据。

第五条是交叉操作验证。先确认成功,再改选另一种方式。按源码,可信状态仍保留,而底部会提示新方法仍需完成确认。这条路径对于产品流程未必合适,却是当前代码的准确结果。验收时应忠实记录它,而不是自行假定页面会重新变为待验证。

停止后重启可回到默认状态,因为没有持久化。连续点击主按钮应当一次切一次;不存在网络等待框、进度条或异步错误。若想了解状态变化,可在 IDE 中查看回调与变量值,但不要为了观察而把日志改成业务依赖条件。

9. 明确未实现的能力,避免把原型当成安全系统

“发现于同一局域网”是固定 Text,工程没有扫描、设备列表、在线判断、发现失败处理或第二台设备通信。MatePad 11 也是写死名称。无论在模拟器、断网环境,还是没有其他设备的情况下,页面都会显示这些文字,因此它们只能说明演示情境,不能作为发现能力存在的依据。

账号认证没有登录、账户选择、Token 校验或用户资料;近场认证没有蓝牙、NFC、距离硬件、权限调用;扫码认证没有相机预览、二维码解析或扫描结果。三个行点击事件只改变字符串与提示。未来若要接入其中任一能力,必须依据实际平台 API 和产品安全要求设计授权、回调、失败、取消与重试,不能仅凭当前三行选项推断已经具备。

“可信设备组”同样没有列表、存储、同步或撤销记录。trusted 只活在当前页面实例内;“可访问协同服务”没有对应服务入口或权限拦截。真实项目里,安全结论不应由任何前端按钮直接取反,而应由受控的系统或服务结果驱动 UI。本文不提供伪造认证或绕过校验的做法,也不把页面成功色当成真实授权凭证。

工程里还有 legacy_components_unused,但入口页面清单和 Index.ets 都没有引用其中的文件。本文只以实际被加载的 pages/Index 为准,不把未使用草稿中的内容计入当前应用。修改后页面没有变化时,也应先确认是否改到了入口正在引用的文件。

10. 适合初学者的阅读、调试和扩展思路

阅读这类单页代码,可以先搜索状态名而不是从第一行硬读到最后。搜索 trusted,会看到它出现在横幅、设备状态、按钮和回调中;搜索 method,会看到它控制高亮并进入成功文案;搜索 notice,会看到它只有初始化、写入与底部展示。先建立“谁写、谁读”的地图,再运行点击,状态流会比单独背 API 更清晰。

调试时,按钮无响应先看 Button 修饰链上的 onClick 是否存在;方式行不能点击先看 Row 是否仍挂着 onClick;列表只剩一项先看 methods 数组是否被误改。当前没有输入框、手势竞争或异步任务,基础检查足以覆盖大多数练习中出现的问题。截图可以辅助核对视觉效果,但源码才是判断交互范围的依据。

当前页面没有 @Builder 或自定义子组件,所有结构集中在一个 build()。对于这个体量,这让读者能在一个文件里看清状态流。是否拆分不是越多越好;只有在某个区块需要独立复用、独立状态或使页面难以阅读时,才有理由设计组件边界。不能把文章里讨论的“可能重构”写成此工程已经采用的结构。

若将来演进为真实设备认证,第一步不是补颜色,而是确定信任结果由谁产生、页面何时等待、失败怎样显示、撤销是否成功、设备信息从何而来。真实名称可能为空或很长,设备可能离线,方法可能受硬件或授权限制;这些要求会推动模型从一个布尔值和一个字符串扩展为更完整的数据状态。具体协议、API、存储与权限应以实际需求和平台文档为准,不能由此示例凭空决定。

本例现在的完成标准很具体:三种方式能互斥切换;确认与解除能切换;trustedmethodnotice 的读取位置与点击行为一致;首屏和动作截图能在本地重现。先把这条最小状态链路理解透彻,再接入真实业务,既不会误判页面能力,也更容易发现后续需求需要补的环节。

11. 用状态表复盘每一次操作

把页面当作一个小型的状态模型后,可以用表格之外的语言逐步复盘它的行为。初始时有三个事实:当前未可信、当前方法为账号认证、当前通知是等待认证。点击“近场认证”时,只有第二和第三个事实改变,结果是未可信、近场认证、已选择近场认证。点击“确认认证”后,第一和第三个事实改变,结果是已可信、近场认证、MatePad 11 已通过近场认证。这个过程没有网络延迟,也没有隐藏的第四个变量。

如果此时点击“扫码认证”,结果会成为已可信、扫码认证、已选择扫码认证,请完成确认。注意,这个状态虽然在真实流程上显得不够严谨,却符合当前源码:方式行的回调没有根据 trusted 禁止点击,也没有在改方法时自动撤销信任。一个好的源码说明应当把这种可观察事实写出来,而不是替作者补一条不存在的规则。这样读者在运行时看到不同组合,能回到代码解释原因。

再点击“解除信任”,得到未可信、扫码认证、已解除信任,需要重新认证。方法没有退回账号认证,说明 trustedmethod 的生命周期不同。若应用重新启动,三个值回到初始值,说明当前状态是组件实例内存,不是用户账户或设备的持久记录。即使用户在成功后等待很久,页面也不会自行超时,因为工程没有定时器、生命周期回调中的重置逻辑或后台任务。

这种复盘方式还可以帮助定位“修改一个地方是否足够”。如果产品只想更换成功后的颜色,只需检查所有读取 trusted 的颜色表达式;如果只想调整选择提示,应只改 notice 的拼接文本。若需求是“确认后禁止换方法”,就不能只改按钮颜色,而要在方式行回调中加入明确规则,并重新定义解除、失败和重新认证后的状态。这些推理都从现有变量关系出发,不需要虚构额外架构。

对初学者而言,手动写几组状态表比立刻引入复杂状态机库更合适。当前页面只有一个设备、三个方法和一个布尔信任值,直接赋值的代码已足够表达全部行为。当状态数量增加到请求中、成功、失败、撤销中等多个阶段时,再考虑用枚举、对象或独立业务层承载语义。不要为了让演示看起来“工程化”而无端增加抽象层。

12. 组件属性与事件链的细看

Text 在本页承担了标题、说明、设备名、状态、方法名和通知等不同角色。它们的共同点是内容来自字面量或状态表达式,页面没有文本输入,也没有双向绑定。主标题设置了 width('100%'),让文字沿横幅宽度排布;设备名所在列没有设置固定宽度,而是由 layoutWeight(1) 配合行布局分配空间。初学者可以比较这两种写法:前者是让单个文本占满父容器,后者是让一个子列在兄弟组件之间获得剩余空间。

方式行中的第一个 Text 也使用 layoutWeight(1),第二个 Text 只在选中时产生“已选”内容。这样无论右侧有没有标记,方法名都能占据主体区域,点击热区仍然覆盖整行。行本身的 width('100%')padding(12)borderRadius(10) 决定了可点击的视觉卡片范围。若把 onClick 错挂到内部文字上,点击空白处可能没有反馈;现有代码将事件放在 Row 上,整行都能响应。

主按钮的 width('100%') 让它和卡片内容同宽,背景、文字色和标题均为条件表达式。按钮没有设置高度、圆角或字体大小,截图中的最终高度主要由 ArkUI 默认按钮样式与宽度共同决定。文章不应把截图中看见的某个高度数字写成源码明确设置的值。做视觉修改时,可以先确认哪些属性是代码指定的,哪些是组件默认行为,避免把运行环境的默认主题误认为页面逻辑。

Divider 没有事件,也没有额外颜色属性,负责在设备摘要和方式标题之间提供一条分隔线。它的存在让卡片有两个信息层级,但不会参与任何状态判断。外层 Scroll 只负责内容可滚动,通知文本并没有绑定滚动事件。组件职责越清楚,调试时越容易把“布局问题”和“状态问题”区分开。

事件回调使用箭头函数,因此可以直接访问当前组件的 this。在 ForEach 中,item 是当前迭代字符串;在按钮中没有额外参数,直接读取组件状态。所有赋值都是同步的,没有 asyncawait 或 Promise。若将来在回调中加入异步认证,必须考虑点击期间的重复提交和回调返回后组件是否仍存在;当前版本不需要这些防护,也没有对应的错误展示区。

13. 颜色、文字和可访问性检查

从运行截图可以看到,蓝色横幅和绿色成功横幅都使用白色主标题,辅助说明采用较浅的蓝白色。卡片正文为深色,辅助说明为蓝灰色,状态文字用黄褐或绿色。这样的色彩分工在视觉上能提示用户“待处理、可操作、已完成”三个层次。但颜色不是唯一信息:页面同时使用“待验证”“可信”“确认认证”“解除信任”等文字,所以即使色觉差异导致颜色区分不明显,仍有文字线索。

源码没有显式设置无障碍描述、语义等级、朗读顺序或高对比模式。截图只能说明在当前模拟器主题下文字可见,不能证明所有辅助功能用户都能顺畅操作。若将页面用于真实产品,应根据 HarmonyOS 无障碍指南补充语义和可读性检查;本文只记录现有代码没有相关设置,不把建议冒充完成结果。

文字通知采用一整句自然语言拼接。选择方式时,字符串是“已选择”加方法名再加“请完成确认”;确认成功时,字符串包含设备名、方法名和“可访问协同服务”;解除时则是一句固定提醒。由于这些内容直接写在回调中,换行、国际化、复数形式和动态设备名都未抽象成资源键。资源目录目前只提供应用名称 app_name,页面内其他文字均为代码字面量。若要国际化,不能只翻译 string.json 中的应用名,还要迁移这些页面文本并处理拼接顺序。

大字号测试也是值得执行的手工步骤。把系统字体调大后,顶部第二行、设备说明和底部通知可能需要更多高度;Scroll 能让内容继续向下滚动,但卡片内部行宽和文字换行仍应观察。方式行没有显式最小高度,实际触摸区域由内边距和文本共同决定。文章不宣称已经通过完整的无障碍或多语言验收,而是给出读者可以复现的观察点。

14. 构建与文件边界检查

本例的可编辑页面文件只有 entry/src/main/ets/pages/Index.ets,入口文件 EntryAbility.ets 负责载入它,资源文件负责应用名称和图标。重写文章时只修改对应 Markdown,不修改这些工程文件。这样做有两个好处:文章可以准确描述已经验证过的版本,后续发布也不会因为文档任务意外改变示例行为。

在 DevEco Studio 中构建时,可以先执行同步和编译,再安装到手机或模拟器。若编译报页面找不到,检查 main_pages.jsonpages/Index 是否与 loadContent('pages/Index') 一致;若资源报错,检查 module.json5 引用的 $string:app_name$media:app_icon 和颜色资源是否仍存在。页面本身不依赖额外 npm 包,因此不应把依赖下载问题与认证交互混为一谈。

构建输出中出现 HAP 文件,只说明 ArkTS 页面被打包,并不说明应用已经拥有设备认证权限。真正判断页面逻辑仍应安装后点击验证。反过来,如果设备没有第二台 MatePad,页面仍然可以完整验证,因为当前代码使用静态设备文案。把构建成功、页面运行成功和真实业务链路成功分成三个层次,可以避免测试报告过度解读。

文章发布前还应检查 Markdown 中两张图片都能正常加载,并且分别对应初始与确认后的状态。图片展示的是本地运行结果,代码片段只用于解释关键部分,完整事实仍以工程文件为准。

15. 从原型继续设计时的决策问题

若产品要求把这张卡片变成真实功能,至少需要先回答几个问题。第一,设备列表是本机发现、系统提供还是服务端下发;第二,认证方式是否全部可用,还是要根据设备能力动态隐藏或禁用;第三,成功状态的有效期是本次会话、账户范围还是设备持久关系;第四,解除信任是否需要再次确认、网络同步或本地凭据清理。当前示例没有这些答案,所以只保留最小的演示状态。

还要决定错误怎样回到页面。网络超时、用户取消、权限拒绝、设备离线和服务端拒绝不应都显示“认证失败”四个字;用户需要知道是否可以重试、是否要更换方式、是否需要去设置中授权。相应地,notice: string 可能演进为包含状态类型、提示内容和可重试标记的对象。这个方向可以从本例的通知区域得到启发,但不是本次重写要补入的代码。

另一个决策是撤销后的数据一致性。当前点击解除只让本地按钮和颜色返回未认证。如果真实设备关系已经写入系统或服务端,撤销可能失败,也可能在另一台设备上异步完成。界面需要区分“正在撤销”“撤销成功”“撤销失败”,并避免用户在请求尚未完成时重复点击。这个复杂性说明,原型中的 this.trusted = !this.trusted 是为了教学清晰而做的简化,不是安全系统的通用实现。

最终,产品文案也应与实际结果一致。如果认证只允许访问某一个服务,就不要笼统写“可访问协同服务”;如果设备仍需等待服务端确认,就不要立刻显示“认证成功”。当前示例使用肯定句来强化状态切换的视觉效果,文章会同时说明其本地性质。将显示文本和业务事实对应起来,是从 UI 原型走向可发布产品时不可省略的一步。

在提交前还可以做一次反向阅读:只看文章中的结论,再回到源码寻找证据。若某句话找不到对应变量、组件、事件或配置,就应改成“可以扩展”或删除。反向阅读尤其能发现 AI 腔常见的泛化表达,例如把普通 Scroll 写成完整的性能方案,把固定文本写成动态数据,把一次布尔取反写成安全协议。这篇文章的技术价值就在于边界清楚、行为可复现,准确比夸张更值得发布。

结语

第 104 个示例用一个入口页面、三个响应式状态和一个固定数组,展示了 ArkUI 声明式界面最直观的因果链:用户点击,状态赋值,所有依赖状态的文字和样式同步呈现。methods 提供选项,method 表示选择,trusted 表示认证前后外观,notice 把最近动作反馈给用户。页面仅使用基础 ArkUI 组件完成布局,没有网络或设备认证模块参与。

把它看作设备认证交互原型是准确的;把它写成已经实现分布式发现、身份核验或可信设备管理的成品则不准确。只要始终以 Index.ets 的变量、事件、布局和入口为依据,并按文中的状态路径运行核对,就能既学会 ArkTS 的状态驱动写法,也能保持对安全能力边界的清醒判断。

16. 发布前的最后一次人工核对

在把文章交给读者前,可以按照“文件、首屏、操作、边界”四个方向各做一次人工核对。文件核对先确认打开的是 articles/104-DeviceAuth/ohos/entry/src/main/ets/pages/Index.ets,而不是目录中 legacy_components_unused 下的旧示例。随后查看 EntryAbility.etsmain_pages.json,确认它们仍然把 pages/Index 作为启动页面。这样能够排除“文档分析了一个文件、设备实际启动了另一个文件”的常见误会。

首屏核对只看第一次启动时的事实:trustedfalsemethod 为账号认证、notice 为等待认证。此时三个方式行并不是三个都可用的认证通道,而是同一个数组产生的三个演示选项;其中第一行因字符串相等而显示已选。截图应当能看到蓝色横幅、白色卡片和浅黄色通知区域,若设备保留了上次运行状态,直接热重载可能无法代表初始画面,应停止应用后重新启动再拍摄。

操作核对要按顺序记录每一次点击后的结果,而不是只确认最终颜色。先点击近场认证,检查是否只有高亮和通知变化;再点击确认认证,检查横幅、设备标签和按钮是否同时变化;最后点击解除信任,检查方法选择是否仍然保留。这个顺序可以覆盖三个状态变量的主要写入点,也能暴露把 method 错写成重置变量、把按钮事件误绑到方式列表等问题。若运行结果与文字不同,应先以源码和实际截图为准修正文档描述,不要为了迎合文章去虚构额外逻辑。

边界核对则专门查找容易被夸大的句子。页面没有调用网络、相机、蓝牙、NFC 或分布式设备 API;没有持久化;没有失败和加载状态;没有真实设备列表。文章中的“MatePad 11”“发现于同一局域网”“可信设备组”都是当前界面中的固定展示语句。把这些名词写成服务端返回值,会让读者在接入项目时形成错误预期。发布版本应保留对这些限制的说明,让示例的学习目标停留在状态驱动 UI,而不是未经验证的安全承诺。

最后检查 Markdown 本身:两张图片均能加载,代码片段与 Index.ets 中的变量名称一致,标题没有暗示工程已经完成真实认证。正文中的调试建议应当能在 DevEco Studio 中复现,不能依赖读者安装不存在的第三方库。完成这轮核对后,这篇文章既满足篇幅要求,也能作为一篇独立的 ArkUI 状态切换案例发布。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐