Порядок обхода – это последовательность, в которой службы специальных возможностей перемещаются по элементам интерфейса. В приложении Compose элементы расположены в ожидаемом порядке чтения, который обычно слева направо, а затем сверху вниз. Однако в некоторых сценариях Compose могут потребоваться дополнительные подсказки, чтобы определить правильный порядок чтения.
isTraversalGroup и traversalIndex – это семантические свойства, которые позволяют влиять на порядок обхода для специальных возможностей в сценариях, когда алгоритма сортировки Compose по умолчанию недостаточно. isTraversalGroup определяет семантически важные группы, которые нужно настроить, а traversalIndex корректирует порядок отдельных элементов в этих группах.
Вы можете использовать только isTraversalGroup, чтобы указать, что все элементы в группе должны выбираться вместе, или вместе с traversalIndex для дополнительной настройки.
Используйте isTraversalGroup и traversalIndex в своем приложении, чтобы управлять порядком обхода программы чтения с экрана.
Группировка элементов для обхода
isTraversalGroup – логическое свойство, которое определяет, является ли узел semantics группой обхода. Этот тип узла служит границей при организации дочерних узлов.
Если для узла задано значение 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." → "справа".
Чтобы упорядочить фрагменты правильно, измените исходный фрагмент, задав для 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" → "on the right."
Как настроить порядок обхода
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