【HarmonyOS 7新能力|024】Accessibility Kit入门实战:从能力边界到最小可运行链路
【HarmonyOS 7新能力024】Accessibility Kit入门实战从能力边界到最小可运行链路一个自定义控件在视觉上“像按钮”不代表辅助技术能理解它。屏幕朗读可能只读出零散文字键盘焦点可能跳进装饰图标列表刷新后还可能突然回到页面顶部。对依赖读屏、键盘或开关控制的用户来说这些问题会直接阻断任务。本文围绕自定义控件走焦顺序建立“任务梳理—语义拆分—名称与角色—逻辑排序—动态恢复—真实验证”的最小链路。示例属性和类型是应用侧教学抽象不代表 HarmonyOS 7 Accessibility Kit 的完整官方 API具体属性名称、事件与设备支持范围应以当前 SDK 和华为官方文档为准。一、走焦顺序服务于任务顺序焦点不应机械地按组件创建顺序移动而应跟随用户完成任务的路径。文章卡片通常先读标题与摘要再到“阅读全文”表单通常依次到标签、输入框、错误提示和提交按钮。视觉上靠得近的元素不一定属于同一操作。第一版先选一个包含卡片、收藏按钮和底部导航的页面验收目标是每个可操作元素能获得焦点装饰元素不占焦顺序与任务一致焦点不会困在局部状态变化有播报刷新后焦点落在合理位置。二、把视觉组件转换成语义树自定义卡片可能由图片、标题、摘要、徽标和箭头组成。若所有子元素都分别暴露用户需要滑动很多次才能进入下一张卡若整张卡完全合并收藏按钮又可能消失。应按实际动作边界决定合并与拆分。interface SemanticNode { id: string label: string role: button | link | text | switch focusable: boolean disabled: boolean order: number }语义树不是视觉树的复印件。装饰图片可以隐藏于辅助技术标题和摘要可以组合成卡片名称独立收藏操作则保留为单独按钮。三、名称、角色和状态缺一不可“收藏”只描述动作却没有告诉用户当前是否已收藏“星形图标”描述外观却没有说明用途。稳定节点至少需要可读名称、正确角色、当前状态和可执行动作。function favoriteLabel(title: string, selected: boolean): string { const state selected ? 已收藏 : 未收藏 return ${title}${state} } function favoriteHint(selected: boolean): string { return selected ? 激活可取消收藏 : 激活可添加收藏 }名称应简洁、上下文充分避免重复朗读屏幕上已经组合进父节点的文字。角色要与真实交互一致不要把不可点击容器伪装成按钮。四、建立可审查的焦点清单在写代码前列出页面焦点表节点 ID、可读名称、角色、前后节点、出现条件和激活结果。它能快速暴露重复节点、隐藏节点仍可达以及视觉顺序与逻辑顺序冲突。const focusPlan: ReadonlyArraySemanticNode [ { id: article-card, label: 精选文章探索鸿蒙未来, role: link, focusable: true, disabled: false, order: 10 }, { id: favorite, label: 探索鸿蒙未来未收藏, role: button, focusable: true, disabled: false, order: 20 }, { id: history, label: 浏览记录, role: button, focusable: true, disabled: false, order: 30 } ]数字只是应用侧排序模型最终实现要映射到平台支持的焦点机制。不要依赖相距很近的“魔法数字”临时插队。五、逻辑顺序优先于坐标推断响应式布局中两列卡片可能在窄屏变成一列。若焦点顺序完全由固定坐标或手工索引决定旋转屏幕后就会出现横跳。更稳妥的方式是按内容分组和任务关系生成顺序。interface FocusGroup { groupOrder: number nodes: ReadonlyArraySemanticNode } function flatten(groups: ReadonlyArrayFocusGroup): SemanticNode[] { return [...groups] .sort((a, b) a.groupOrder - b.groupOrder) .flatMap((group) [...group.nodes].sort((a, b) a.order - b.order)) .filter((node) node.focusable !node.disabled) }布局改变时更新分组映射并分别测试手机竖屏、横屏、小窗口和平板宽度。六、避免焦点陷阱和循环弹窗、抽屉和自定义轮播最容易形成焦点陷阱。临时模态区域打开时焦点可以限制在区域内但必须存在明确的关闭动作关闭后应回到触发它的节点而不是页面开头。interface FocusHistory { triggerId: string | null activeScope: string } function restoreTarget(history: FocusHistory, existingIds: ReadonlySetstring): string { if (history.triggerId ! null existingIds.has(history.triggerId)) return history.triggerId return page-heading }当触发节点已被删除使用页面标题或最近的稳定容器作为后备位置不能把焦点交给不存在的元素。七、动态内容变化要可感知加载完成、校验失败、收藏状态改变等变化视觉用户能直接看到读屏用户却可能毫无感知。需要为重要、非连续的结果提供简短状态通知但不要把每次动画和计数刷新都朗读出来。type AnnouncementPriority polite | assertive interface Announcement { message: string priority: AnnouncementPriority dedupeKey: string } function savedAnnouncement(title: string): Announcement { return { message: 已收藏${title}, priority: polite, dedupeKey: saved-${title} } }高频事件需去重或合并。错误若阻断提交可以提高优先级普通保存成功不应打断正在朗读的长内容。八、异步刷新后恢复用户位置列表刷新时重新创建全部节点会导致当前焦点丢失。刷新前保存稳定业务 ID刷新后若该条目仍存在就把焦点恢复到对应语义节点若被删除则移动到相邻条目或列表标题。function nextAfterRemoval(ids: ReadonlyArraystring, removedIndex: number): string { if (ids.length 0) return list-heading return ids[Math.min(removedIndex, ids.length - 1)] }不要使用数组下标作为长期身份。排序、分页或插入数据后下标会变化业务 ID 才能正确关联刷新前后的节点。九、禁用、隐藏与不可见要区分禁用控件可能仍需让用户知道它存在以及为何不可用隐藏控件则不应出现在焦点链。仅把透明度设为零或移出屏幕不一定会同步改变语义可见性。interface VisibilityState { rendered: boolean semanticVisible: boolean enabled: boolean } function canReceiveFocus(state: VisibilityState): boolean { return state.rendered state.semanticVisible state.enabled }每次条件渲染、动画切换和权限变化都要同时审查视觉状态与语义状态避免“看不见但能聚焦”。十、视觉焦点必须清晰可见键盘和遥控器用户需要明确知道当前焦点。焦点描边不能被裁剪也不能只依赖轻微颜色变化。应验证深浅色模式、不同缩放比例和高对比场景保证描边与背景有足够对比。自定义绘制焦点样式时还要保留按下、选中、禁用等状态差异。焦点与选中不是同一个概念焦点表示当前操作位置选中表示业务状态二者需要能同时辨认。十一、真实辅助技术验证不可省略自动化检查能发现空名称和部分顺序问题却无法判断朗读是否自然。至少使用屏幕朗读完整完成一次核心任务再使用键盘或目标输入设备完成同一路径。interface AuditRecord { scenario: string expectedOrder: ReadonlyArraystring actualOrder: ReadonlyArraystring screenReaderPassed: boolean keyboardPassed: boolean issue?: string }记录实际朗读文本和焦点序列而不是只写“测试通过”。动态弹窗、错误提示、列表空态和权限拒绝都应单独验收。十二、用失败场景完成回归回归清单应覆盖装饰元素不占焦、卡片信息不重复朗读、焦点顺序与任务一致、焦点能离开每个区域、弹窗关闭后正确恢复、刷新后位置稳定、隐藏节点不可达、禁用原因可理解、错误状态可感知、深浅色焦点清晰、横竖屏路径合理。还要把文字放大、启用系统深色模式并在较小窗口中检查内容是否可达。一次顺畅的正常流程不能证明无障碍完成只有这些边界共同通过才说明自定义控件真正具备可用的语义和走焦链路。无障碍不是给视觉界面补几句说明而是为同一个任务提供等价的操作模型。把语义节点、逻辑顺序、动态通知、焦点恢复和真实设备验证纳入组件设计自定义控件才能从“看起来能用”变成“不同用户都能完成任务”。