HarmonyOS 7 新特性(二十七)|无障碍走焦顺序与读屏验收

HarmonyOS 7(API 26)Beta2 新增自定义控件走焦顺序指南。开启屏幕朗读后,用户可通过单指左右滑动切换焦点;当默认顺序不符合业务语义时,应用可以重新定义下一个焦点。
视觉页面常按位置排列组件,但读屏用户依赖的是语义顺序。一个双栏页面在 DOM 或组件树中可能先创建完整左栏,再创建右栏;读屏却应该按“标题—摘要—主要操作—次要信息”移动。默认顺序一旦与视觉和业务不一致,用户会在页面中迷路。
本文以“订单详情”页面为例,说明如何设计焦点图、处理动态组件、弹窗与错误信息,并建立可自动化的无障碍验收。
一、先画语义焦点图
不要从 API 开始。先列出用户完成任务所需节点:页面标题、订单状态、商品、金额、地址、主要按钮、帮助入口。装饰图片、分隔线和重复文案不应获得焦点。
interface FocusNode {
id: string
role: 'heading' | 'text' | 'button' | 'link' | 'input' | 'group'
label: string
required: boolean
nextId?: string
previousId?: string
}
interface FocusGraph {
route: string
nodes: FocusNode[]
}
焦点图是页面语义契约,可由设计、开发和测试共同评审。
二、顺序遵循任务而非组件树
订单页优先宣布状态和主要操作;错误页面优先错误标题、原因与恢复按钮;表单按标签—输入—说明—错误移动。不要为了视觉左右布局就让焦点在两列之间来回跳。
const orderFocus: FocusGraph = {
route: 'order-detail',
nodes: [
{ id: 'title', role: 'heading', label: '订单详情', required: true, nextId: 'status' },
{ id: 'status', role: 'text', label: '待支付', required: true, nextId: 'items' },
{ id: 'items', role: 'group', label: '商品列表', required: true, nextId: 'pay' },
{ id: 'pay', role: 'button', label: '立即支付', required: true, nextId: 'help' },
{ id: 'help', role: 'link', label: '需要帮助', required: false }
]
}
实际绑定属性以当前 Accessibility Kit 文档为准。
三、名称、角色、状态缺一不可
“按钮”不是完整标签,“提交订单,按钮,已禁用”才让用户知道对象和状态。图标按钮必须提供可读名称;选中、展开、加载和错误状态要随 UI 更新。
function paymentLabel(state: PaymentState): string {
if (state.kind === 'loading') return '正在提交支付,请稍候'
if (state.kind === 'disabled') return `立即支付,不可用,${state.reason}`
return `立即支付,金额 ${state.amountText}`
}
不要把视觉文案简单拼接成冗长朗读,应以任务所需信息为准。

四、动态内容必须维护焦点
商品删除、折叠区域展开和异步刷新会改变节点。如果当前焦点节点消失,应移动到合理邻居,而不是回到页面顶部或丢失焦点。
function nextAfterRemoval(removedId: string, order: string[]): string | null {
const index = order.indexOf(removedId)
if (index < 0) return order[0] ?? null
return order[index + 1] ?? order[index - 1] ?? null
}
列表使用稳定业务 ID,避免排序后焦点落到错误商品。
五、错误出现时要主动可感知
表单错误不能只把边框变红。提交失败后先宣布摘要,再允许用户跳到首个错误字段。若自动移动焦点会打断输入,可使用无障碍提示并让用户选择跳转。
interface FormErrorSummary {
count: number
firstFieldId: string
message: string
}
function buildSummary(errors: FieldError[]): FormErrorSummary | null {
if (!errors.length) return null
return {
count: errors.length,
firstFieldId: errors[0].fieldId,
message: `有 ${errors.length} 项需要修改,第一项是${errors[0].label}`
}
}
六、弹窗必须形成焦点闭环
模态框打开后焦点进入标题或首个主要元素,并限制在弹窗内部;关闭后恢复到触发按钮。背景内容不应继续被读屏遍历。
嵌套弹窗尽量避免。如果无法避免,使用栈保存每层触发节点,按后进先出恢复。
class FocusReturnStack {
private ids: string[] = []
push(id: string) { this.ids.push(id) }
pop(): string | undefined { return this.ids.pop() }
}
七、分栏和折叠屏按语义重排
手机单栏与平板双栏可能共享组件,但走焦顺序不必跟随组件创建顺序。容器宽度变化后重新生成焦点图,并尽量保持当前业务节点 ID。
在平行视界中还要区分左右页面所有权:右侧详情关闭后,焦点回到左侧触发项,而不是跳到左栏第一项。
八、不要制造焦点陷阱
轮播图、可滚动容器、Web 内容和自定义画布最容易形成陷阱。用户必须能进入、操作并离开。长列表不要让每个装饰元素都可聚焦;成组内容先提供摘要,再允许深入浏览。
触控可点击区域与无障碍可操作对象保持一致,避免读屏宣布可点击但实际没有响应。
九、减少动态与大字体兼容
字体放大后文本换行可能改变视觉位置,但语义顺序不应变化。关闭动画或减少动态时,焦点移动不能依赖转场结束事件。横竖屏切换后重新验证当前元素仍存在。
十、自动化检查与人工读屏
function validateGraph(graph: FocusGraph): string[] {
const ids = new Set(graph.nodes.map(x => x.id))
const errors: string[] = []
for (const node of graph.nodes) {
if (!node.label.trim()) errors.push(`${node.id}: empty label`)
if (node.nextId && !ids.has(node.nextId)) errors.push(`${node.id}: invalid nextId`)
}
return errors
}
describe('order focus graph', () => {
it('contains no broken edge', () => expect(validateGraph(orderFocus)).toEqual([]))
})
自动化能发现空标签、重复 ID 和断边,但不能判断朗读是否自然。必须使用真机屏幕朗读完成核心任务,覆盖正向、错误、弹窗和动态更新。
十一、上线清单
- 页面有经过评审的语义焦点图;
- 装饰元素不聚焦,交互元素名称和角色完整;
- 选中、禁用、展开和错误状态可朗读;
- 动态删除后焦点移动到合理邻居;
- 弹窗形成焦点闭环并能返回触发点;
- 手机、平板、分栏和大字体顺序一致;
- 自动化检查与真机读屏任务均通过。

结语
自定义走焦不是“给组件编号”,而是把页面任务转换成线性、可预测的语义路径。以稳定 ID 管理动态焦点,以弹窗闭环和错误摘要保护上下文,再通过真机读屏完成验收,复杂布局才能真正对所有用户可用。
官方参考
- 2026 年 7 月开发者月刊:https://developer.huawei.com/consumer/cn/monthly/202607
- HarmonyOS 无障碍开发资源:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/accessibility-development
更多推荐



所有评论(0)