HarmonyOS开发中状态播报与走焦顺序
状态播报与走焦顺序:两个进阶细节决定体验上限
基础标注做完之后,体验的差距往往体现在两个细节上:一个是"可切换状态的控件怎么读"(选中/未选中、开/关),另一个是"焦点按什么顺序走"。这两件事处理好了,用户的感知是"这个应用很顺手";处理不好,就是"能用但别扭"。这篇把这两个进阶点合在一起讲。
一、状态型控件:系统默认读法不够用时怎么办
很多控件有"可切换状态"——收藏按钮有"已收藏/未收藏",开关有"开/关",复选框有"选中/未选中"。
屏幕朗读对这类控件有默认处理:它会从控件的语义属性推导出状态说明标签。比如一个 Toggle 开关,系统能自动读"开"或"关";一个设置了 accessibilitySelected 的复选框,系统能读"已选中"或"未选中"。
大部分情况下,这套默认推导够用了。但有些场景,默认的状态说明标签和业务语义对不上。这时候就需要 accessibilityStateDescription 来覆盖。
1.1 accessibilityStateDescription 是什么
accessibilityStateDescription 允许你自定义控件状态的朗读标签。官方文档的说明是:
屏幕朗读模式下,若指定了可点击控件的状态说明标签,当用户聚焦控件或执行双击操作后,屏幕朗读会播报指定的状态说明标签。
也就是说,这个属性定的文本,会在聚焦和双击时被播报,替代系统默认的状态描述。
1.2 代码示例:收藏按钮
一个经典的收藏按钮例子:
@Entry
@Component
export struct FavoriteButton {
@State private isSelected: boolean = false;
build() {
NavDestination() {
Column() {
Button() {
Image(this.isSelected ? $r('app.media.favorIcon') : $r('app.media.unfavorIcon'))
.width(30)
.height(30)
}
.accessibilityStateDescription(this.isSelected ? '已收藏' : '未收藏')
.accessibilitySelected(this.isSelected)
.onClick(() => {
this.isSelected = !this.isSelected;
})
}
}
}
}
这里三个无障碍属性配合:
accessibilitySelected():告诉系统这个按钮的选中状态(这也会影响系统默认推导)accessibilityStateDescription():自定义状态朗读标签,用"已收藏/未收藏"替代系统可能推导出的"已选中/未选中"- 状态文本跟随
isSelected动态变化
为什么"已收藏"比"已选中"好?因为"已选中"是通用的、抽象的,用户听到"已选中"还得结合上下文想"选中了什么状态";而"已收藏"直接说清了业务含义。用业务语言替代泛化语言,是自定义状态描述的核心价值。
二、走焦顺序:焦点该怎么走由你定
2.1 默认走焦顺序的局限
API 18 起,屏幕朗读支持单指左右滑动切换焦点。默认的走焦顺序是"按节点树顺序"——也就是组件在代码里的声明顺序、层级结构决定的遍历顺序。
问题在于,节点树顺序不等于视觉顺序。视觉上从左到右、从上到下排列的控件,在节点树里可能因为布局嵌套而顺序错乱。用户按直觉左滑,焦点却跳到了一个视觉上在别处的位置,这就很别扭。
2.2 accessibilityNextFocusId 自定义走焦
当默认顺序不满足需求时,用 accessibilityNextFocusId 指定"下一个焦点的 id"。
官方建议的走焦方向很明确:按照画面显示的自上而下、自左向右的方向设置。
参数说明:
| 属性 | 说明 |
|---|---|
nextId | 下一个焦点组件的 id |
nextFocusParams | 扩展参数(API 26 起) |
2.3 代码示例
下面的例子里,视觉顺序是 A→B→C→D→E,但通过自定义,把走焦顺序改成了 A→C→D→E(跳过了 B):
@Entry
@Component
export struct NextFocusIdDemo {
build() {
NavDestination() {
Column({ space: 10 }) {
Button('按钮A, Next为column')
.fontSize(20).width('80%')
.accessibilityNextFocusId('next_column') // A 的下一个焦点指向 column
Button('按钮B')
.fontSize(20).width('80%') // B 没有任何 customId 指向它,被跳过
Column() {
Button('按钮C')
Button('按钮D')
}
.id('next_column') // A 指向的容器
Button('按钮E')
.id('btn_e')
.fontSize(20).width('80%')
}
}
}
}
关键机制:
Button A通过accessibilityNextFocusId('next_column')指向下个焦点Button B因为没有任何控件的nextFocusId指向它,在走焦链路中被跳过- 目标要设
.id(),和nextId对应
2.4 API 26 的新参数
API 26.0.0 起,accessibilityNextFocusId 新增了 nextFocusParams 参数,可以通过 isConsiderDescendants 控制焦点搜索时是否考虑目标元素后代中的可聚焦节点。
这个参数在目标是一个复杂容器(内部还有可聚焦子元素)时有用。默认 false 表示只把容器本身当焦点;设为 true 则允许焦点深入到容器的后代节点。
三、走焦顺序设计的几个原则
3.1 遵循视觉顺序
焦点顺序应该和用户的视觉扫描顺序一致——通常就是自上而下、自左向右。这是官方的首要建议,也是用户在滑动时的心智模型。
3.2 避免焦点跳跃
走焦路径应该平滑、连续,不要从一个控件突然跳到视觉上很远的地方。空间上的突兀跳跃会让用户失去方位感。
3.3 不要遗漏关键控件
被跳过的控件要慎重。上面例子里跳过 B 是为了演示,但实际业务里,跳过意味着用户在走焦序列里永远访问不到它。除非 B 确实不可交互或不需要被朗读,否则不要随意让它"掉出"走焦链路。
四、两个细节的关系
状态播报和走焦顺序看起来互不相关,本质都是在解决同一个问题:让用户的听觉体验和视觉体验对齐。
- 状态播报:让"听觉上的状态"对齐"视觉上的状态"
- 走焦顺序:让"听觉上的位置"对齐"视觉上的位置"
一个管"信息对不对",一个管"顺序顺不顺"。两者叠加,决定了一个无障碍应用的上限。
五、总结一下下
这篇的两个主题,可以各用一句话收尾:
- 状态播报:系统默认读法不够贴业务时,用
accessibilityStateDescription覆盖,用业务语言替代泛化语言 - 走焦顺序:默认按节点树走焦不满足视觉顺序时,用
accessibilityNextFocusId重新定义,遵循自上而下、自左向右
更多推荐



所有评论(0)