遍历顺序是无障碍服务浏览界面元素的顺序。在 Compose 应用中,元素会按预期的阅读顺序排列,通常是从左到右,然后从上到下。不过,在某些情况下,Compose 可能需要额外的提示才能确定正确的阅读顺序。
isTraversalGroup
和 traversalIndex
是语义属性,可让您在 Compose 的默认排序算法不足以满足需求的情况下,影响无障碍服务的遍历顺序。isTraversalGroup
用于识别需要自定义且具有重要语义的组,而 traversalIndex
用于调整这些组中各个元素的顺序。您可以单独使用 isTraversalGroup
来表示应一起选择组中的所有元素,也可以将其与 traversalIndex
搭配使用以进行进一步的自定义。
在应用中使用 isTraversalGroup
和 traversalIndex
来控制屏幕阅读器的遍历顺序。
对元素进行分组以进行遍历
isTraversalGroup
是一个布尔值属性,用于定义语义节点是否为遍历组。此类节点的功能是在整理节点的子项时充当边界或边框。
在节点上设置 isTraversalGroup = true
意味着系统会先访问该节点的所有子节点,然后再移动到其他元素。您可以在非屏幕阅读器可聚焦的节点(例如列、行或框)上设置 isTraversalGroup
。
以下示例使用 isTraversalGroup
。它会发出四个文本元素。左侧的两个元素属于一个 CardBox
元素,而右侧的两个元素属于另一个 CardBox
元素:
// CardBox() function takes in top and bottom sample text. @Composable fun CardBox( topSampleText: String, bottomSampleText: String, modifier: Modifier = Modifier ) { Box(modifier) { Column { Text(topSampleText) Text(bottomSampleText) } } } @Composable fun TraversalGroupDemo() { val topSampleText1 = "This sentence is in " val bottomSampleText1 = "the left column." val topSampleText2 = "This sentence is " val bottomSampleText2 = "on the right." Row { CardBox( topSampleText1, bottomSampleText1 ) CardBox( topSampleText2, bottomSampleText2 ) } }
该代码会产生类似于以下内容的输出:

由于未设置任何语义,屏幕阅读器的默认行为是从左到右、从上到下遍历元素。由于此默认设置,TalkBack 会以错误的顺序读出句子片段:
“This sentence is in” → “This sentence is” → “the left column.”→“在右侧。”
如需正确排列 fragment,请修改原始代码段,将 isTraversalGroup
设置为 true
:
@Composable fun TraversalGroupDemo2() { val topSampleText1 = "This sentence is in " val bottomSampleText1 = "the left column." val topSampleText2 = "This sentence is" val bottomSampleText2 = "on the right." Row { CardBox( // 1, topSampleText1, bottomSampleText1, Modifier.semantics { isTraversalGroup = true } ) CardBox( // 2, topSampleText2, bottomSampleText2, Modifier.semantics { isTraversalGroup = true } ) } }
由于 isTraversalGroup
是在每个 CardBox
上专门设置的,因此在对其元素进行排序时,系统会应用 CardBox
边界。在这种情况下,系统会先读取左侧 CardBox
,然后再读取右侧 CardBox
。
现在,TalkBack 会按正确的顺序读出句子片段:
“This sentence is in”→“the left column”。→ “This sentence is” → “on the right.”
自定义遍历顺序
traversalIndex
是一个浮点属性,可让您自定义 TalkBack 遍历顺序。如果将元素分组在一起不足以让 TalkBack 正常运行,请将 traversalIndex
与 isTraversalGroup
结合使用,以进一步自定义屏幕阅读器的排序。
traversalIndex
属性具有以下特性:
traversalIndex
值越低的元素优先级越高。- 可以是正面的,也可以是负面的。
- 默认值为
0f
。 - 为了让遍历索引影响遍历行为,必须在可供无障碍服务选择和聚焦的组件(例如文本或按钮等屏幕元素)上设置该索引。
- 例如,仅对
Column
设置traversalIndex
不会产生任何影响,除非该列也设置了isTraversalGroup
。
- 例如,仅对
以下示例展示了如何将 traversalIndex
和 isTraversalGroup
搭配使用。
表盘是标准遍历顺序不起作用的常见场景。本部分的示例是一个时间选择器,用户可以在其中浏览表盘上的数字,并为小时和分钟槽选择数字。

在以下简化代码段中,有一个 CircularLayout
,其中绘制了 12 个数字,从 12 开始,顺时针绕圆形移动:
@Composable fun ClockFaceDemo() { CircularLayout { repeat(12) { hour -> ClockText(hour) } } } @Composable private fun ClockText(value: Int) { Box(modifier = Modifier) { Text((if (value == 0) 12 else value).toString()) } }
由于系统并非按默认的从左到右、从上到下的顺序读取表盘,因此 TalkBack 会乱序读取数字。如需解决此问题,请使用递增计数器值,如以下代码段所示:
@Composable fun ClockFaceDemo() { CircularLayout(Modifier.semantics { isTraversalGroup = true }) { repeat(12) { hour -> ClockText(hour) } } } @Composable private fun ClockText(value: Int) { Box(modifier = Modifier.semantics { this.traversalIndex = value.toFloat() }) { Text((if (value == 0) 12 else value).toString()) } }
如需正确设置遍历顺序,请先将 CircularLayout
设为遍历组,然后设置 isTraversalGroup = true
。然后,将每个时钟文本绘制到布局上时,将其对应的 traversalIndex
设置为计数器值。
由于计数器值会不断增加,因此随着数字添加到屏幕上,每个时钟值的 traversalIndex
都会变大 - 时钟值 0 的 traversalIndex
为 0,时钟值 1 的 traversalIndex
为 1。这样,TalkBack 读取这些内容的顺序便会设定好。现在,CircularLayout
中的数字会按预期顺序读取。
由于已设置的 traversalIndexes
仅与同一分组中的其他索引相关,因此屏幕上的其余排序保持不变。换句话说,上例代码段中显示的语义更改只会修改已设置 isTraversalGroup = true
的界面中的排序。
请注意,即使未将 CircularLayout's
语义设置为 isTraversalGroup =
true
,traversalIndex
更改仍会应用。不过,如果没有 CircularLayout
来绑定它们,系统会在访问屏幕上的所有其他元素后,最后读取表盘的十二位数字。之所以会出现这种情况,是因为所有其他元素的默认 traversalIndex
均为 0f
,并且系统会在读取所有其他 0f
元素后读取时钟文本元素。
API 注意事项
使用遍历 API 时,请考虑以下事项:
- 应在包含分组元素的父级上设置
isTraversalGroup = true
。 traversalIndex
应设置在包含语义且将由无障碍服务选择的子组件上。- 确保您要调查的所有元素都位于同一
zIndex
级别,因为这也会影响语义和遍历顺序。 - 确保不进行任何不必要的语义合并,因为这可能会影响应用遍历索引的组件。
为您推荐
- 注意:当 JavaScript 处于关闭状态时,系统会显示链接文字
- Compose 中的无障碍功能
- [Compose 中的 Material Design 2][19]
- 测试 Compose 布局