HarmonyOS 7 屏幕朗读跳过整组按钮?API 26 的 accessibilityNextFocusId 新参数怎么用
页面里有“筛选条件”和一组操作按钮。视觉上按钮就在筛选后面,可打开屏幕朗读、向右扫动时,却可能跳过按钮组,或者反复落在错误的位置。这里先别急着把每个按钮都硬编码成下一站:你指定的目标可能是容器,而不是可聚焦的按钮。

先确认改动点
华为开发者文档 自定义走焦顺序(2026-09-08 更新)说明:accessibilityNextFocusId 用来指定屏幕朗读下一个焦点的组件 ID。API 26.0.0 起多了第二个参数 nextFocusParams,其中 isConsiderDescendants 默认是 false,设置为 true 才会在目标组件的后代节点里寻找可聚焦节点。这是屏幕朗读的无障碍焦点,不要和键盘 Tab 焦点或页面导航栈混为一谈。
最小示意(Stage 模型、API 26.0.0+):
@Entry
@Component
struct FocusOrderPage {
build() {
Column({ space: 16 }) {
Button('筛选条件')
.id('filter')
.accessibilityNextFocusId('actions', { isConsiderDescendants: true })
Row({ space: 12 }) {
Button('应用筛选').id('apply')
Button('重置筛选').id('reset')
}
.id('actions')
Button('查看结果').id('results')
}
}
}
这里把 actions 设为目标 ID,第二个参数允许从该容器下面继续寻找可聚焦项。Row 的 ID 必须能匹配,目标消失或写错时,这条自定义指向就无效。上面的代码只展示接口接线;我没有在 API 26 真机上宣称按钮的最终朗读顺序已经验证。
案例一:目标是容器,按钮在里面
复现时做两组对照:A 组只写 .accessibilityNextFocusId('actions');B 组加 { isConsiderDescendants: true }。开启系统屏幕朗读,从“筛选条件”向右扫动,同时记录朗读内容和绿色焦点框究竟落在容器、内部按钮,还是后面的“查看结果”。
根据官方参数定义,A 组不主动在目标容器的后代里找焦点;B 组会考虑后代。这不等于保证第一个按钮必然获焦:实际结果还受组件是否可访问、可见状态和系统走焦策略影响,需要以设备测试为准。把按钮组折叠或隐藏后再测一次,才能发现动态页面上的边界问题。
| 检查项 | 可能原因 | 处理方式 |
|---|---|---|
| 指向容器后没落到子按钮 | 默认不搜索后代 | API 26+ 使用第二参数并实测 |
| 指向 ID 完全无效 | ID 写错、目标未渲染 | 检查真实组件树和条件分支 |
| 焦点绕圈 | 自定义跳转互相指回 | 删掉不必要的边,检查焦点图 |
| 折叠后顺序变了 | 节点已销毁或不可访问 | 展开/收起各跑一遍朗读路径 |
案例二:列表动态增删,别用会失效的目标 ID
另一个常见页面是搜索结果列表:顶部有“清除筛选”,下面的首条结果可能因网络刷新被移除。若把清除按钮的下一焦点写死到首条结果的 ID,刷新后它指向的节点可能根本不存在。官方接口参考明确指出,nextId 没有对应组件时设置无效;这不是“屏幕朗读偶尔抽风”。
更稳的处理分两步。先决定是否真的需要强制跳转:如果自然组件树顺序已经合理,就让默认走焦工作,减少维护边。确需跨过中间装饰节点时,优先指向稳定的结果容器,并在 API 26+ 选择是否搜索后代;列表为空时不要仍把跳转绑到已删除的结果节点。每次异步刷新后复测首项、末项、空列表三种状态。
我用下面的小模型在本地检查 ID 缺失、重复、自己指向自己和闭环。它只能查我们写的焦点关系,不能模拟系统屏幕朗读,也不能证明 ArkUI 的最终获焦对象:
export function validateFocusGraph(nodes, links) {
const ids = new Set(nodes.map(node => node.id));
if (ids.size !== nodes.length) throw new Error('duplicate id');
for (const [from, to] of Object.entries(links)) {
if (!ids.has(from) || !ids.has(to)) throw new Error('missing node');
if (from === to) throw new Error('self loop');
}
for (const start of Object.keys(links)) {
const seen = new Set();
let current = start;
while (links[current]) {
if (seen.has(current)) throw new Error('cycle');
seen.add(current);
current = links[current];
}
}
return true;
}
本地 Node.js 跑过 7 个断言:默认不搜索容器后代、开启后可在模型里找到子按钮、目标缺失、ID 不存在、自环、循环等。模型只是写文章前的静态检查。真机验收仍应开启屏幕朗读,分别走过完整列表、空列表、折叠按钮组,录下每一步朗读内容和焦点框位置;还要测大字体下布局换行后是否仍合乎阅读逻辑。
什么时候不要加这条配置
没有证据说明默认走焦顺序有问题时,不建议到处设置 accessibilityNextFocusId。手工指定的边越多,新增按钮、删除列表项、弹窗插入时越容易形成旧 ID 和跳转环。先用屏幕朗读走完整条路径,再只修真正错位的一处,最后把动态状态也纳入回归清单。
本文核对的官方资料:自定义走焦顺序、AccessibilityNextFocusParams 定义。说明:本文的 JavaScript 焦点图检查已在本地运行;ArkTS 示例和真机屏幕朗读效果尚未完成 API 26 SDK/设备验证,不把预期行为当实测结论。
更多推荐



所有评论(0)