Порядок обхода — это порядок, в котором службы специальных возможностей перемещаются по элементам пользовательского интерфейса. В приложении 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 зачитывает фрагменты предложений в неправильном порядке:
«Это предложение находится в» → «Это предложение» → «левый столбец». → «справа».
Чтобы правильно упорядочить фрагменты, измените исходный фрагмент, установив для 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 зачитывает фрагменты предложения в правильном порядке:
«Это предложение находится в» → «левом столбце». → «Это предложение» → «справа».
Настроить порядок обхода
traversalIndex
— это свойство с плавающей запятой, которое позволяет настроить порядок обхода TalkBack. Если группировки элементов недостаточно для правильной работы TalkBack, используйте traversalIndex
в сочетании с isTraversalGroup
для дальнейшей настройки порядка чтения с экрана.
Свойство traversalIndex
имеет следующие характеристики:
- Элементы с более низкими значениями
traversalIndex
имеют приоритет в первую очередь. - Может быть положительным или отрицательным.
- Значение по умолчанию —
0f
. - Чтобы индекс обхода влиял на поведение обхода, он должен быть установлен на компоненте, который будет выбираться и фокусироваться службами специальных возможностей, например, на экранных элементах, таких как текст или кнопки.
- Например, установка только
traversalIndex
дляColumn
не будет иметь никакого эффекта, если только для столбца также не установлен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
- [Material Design 2 в Compose][19]
- Тестирование макета Compose