Por padrão, o comportamento do leitor de tela de acessibilidade em um app do Compose é implementado
na ordem de leitura esperada, que geralmente é da esquerda para a direita e de cima para baixo.
No entanto, há alguns tipos de layouts de apps em que o algoritmo não consegue determinar
a ordem de leitura real sem dicas adicionais. Em apps baseados em visualização, é possível
corrija esses problemas usando as propriedades traversalBefore
e traversalAfter
.
A partir do Compose 1.5, o Compose oferece uma API igualmente flexível, mas com
um novo modelo conceitual.
isTraversalGroup
e traversalIndex
são propriedades semânticas que
permitem controlar a acessibilidade e a ordem de foco do TalkBack em cenários em que o
algoritmo de classificação padrão não é apropriado. isTraversalGroup
identifica
grupos semanticamente importantes, enquanto traversalIndex
ajusta a ordem de
elementos individuais dentro desses grupos. Você pode usar apenas isTraversalGroup
,
ou traversalIndex
para mais personalização.
Use isTraversalGroup
e traversalIndex
na sua
para controlar a ordem de apresentação do leitor de tela.
Agrupar elementos com isTraversalGroup
isTraversalGroup
é uma propriedade booleana que define se uma semântica
é um grupo de travessia. Esse tipo de nó é aquele cuja função é servir
como um limite ou fronteira na organização dos filhos do nó.
Definir isTraversalGroup = true
em um nó significa que todos os filhos dele
são visitadas antes de ir para outros elementos. Você pode definir o isTraversalGroup
como
nós que não são focalizáveis para leitores de tela, como colunas, linhas ou caixas.
O exemplo a seguir usa isTraversalGroup
. Ela emite quatro elementos de texto. O
Os dois elementos da esquerda pertencem a um elemento CardBox
, enquanto os dois elementos da direita pertencem a
pertencer a outro elemento 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 ) } }
O código produz uma saída semelhante a esta:
Como nenhuma semântica foi definida, o comportamento padrão do leitor de tela é para percorrer os elementos da esquerda para a direita e de cima para baixo. Por isso padrão, o TalkBack lê os fragmentos da frase na ordem incorreta:
"Esta frase está em" → "Esta frase é" → "a coluna da esquerda". → "no direito".
Para ordenar os fragmentos corretamente, modifique o snippet original para definir
De isTraversalGroup
a 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 } ) } }
Como isTraversalGroup
é definido especificamente em cada CardBox
, o CardBox
limites se aplicam ao classificar seus elementos. Nesse caso, o lado esquerdo
A CardBox
é lida primeiro, seguida pela CardBox
direita.
Agora, o TalkBack vai ler os fragmentos de frases na ordem correta:
"Esta frase está em" → "a coluna da esquerda". → "Esta frase é" → "no direito".
Personalizar ainda mais a ordem de apresentação
traversalIndex
é uma propriedade de ponto flutuante que permite personalizar o TalkBack.
ordem de travessia. Se agrupar elementos não for suficiente para que o TalkBack
funcionar corretamente, use traversalIndex
em conjunto com
isTraversalGroup
para personalizar ainda mais a ordem do leitor de tela.
A propriedade traversalIndex
tem as seguintes características:
- Elementos com menores valores de
traversalIndex
são priorizados. - Pode ser positivo ou negativo.
- O valor padrão é
0f
. - Afeta apenas os nós focalizáveis para o leitor de tela, como elementos na tela como
textos ou botões. Por exemplo, definir apenas
traversalIndex
em uma coluna não terão efeito, a menos que a coluna também tenhaisTraversalGroup
definido.
O exemplo a seguir mostra como usar traversalIndex
e
isTraversalGroup
juntas.
Exemplo: atravessar aparência do relógio
Uma aparência do relógio é um cenário comum em que a ordem de travessia padrão não funcionam. O exemplo desta seção é um seletor de horário, em que um usuário pode percorrer através dos números no mostrador de relógio e selecione dígitos para a hora e o minuto slots.
No snippet simplificado a seguir, há um CircularLayout
em que 12
números são desenhados, começando com 12 e movendo-se no sentido horário ao redor do círculo:
@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()) } }
Como o mostrador de relógio não é lido logicamente com os valores padrão de esquerda para direita e de cima para baixo, o TalkBack lê os números fora de ordem. Para retificar isso, use o valor do contador de incremento, conforme mostrado no snippet a seguir:
@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()) } }
Para definir corretamente a ordem de travessia, primeiro faça da CircularLayout
uma
grupo de travessia e definir isTraversalGroup = true
. Então, como cada texto de relógio é
renderizado no layout, defina o traversalIndex
correspondente para o contador
.
Como o valor do contador aumenta continuamente, o valor
traversalIndex
é maior à medida que números são adicionados à tela. O valor do relógio é 0
tem um traversalIndex
de 0 e o valor de clock 1 tem um traversalIndex
de 1.
Dessa forma, a ordem em que o TalkBack os lê é definida. Agora, os números
dentro de CircularLayout
são lidas na ordem esperada.
Como os traversalIndexes
que foram definidos são relativos somente a outros
índices dentro do mesmo agrupamento, o restante da ordenação de telas foi
preservados. Em outras palavras, as mudanças semânticas mostradas no código anterior
só modificar a ordem dentro do mostrador de relógio que tem
isTraversalGroup = true
definido.
Sem definir a semântica CircularLayout's
como isTraversalGroup =
true
, as mudanças de traversalIndex
ainda se aplicam. No entanto, sem o
CircularLayout
para vinculá-los, são lidos os 12 dígitos do mostrador de relógio
por último, depois que todos os outros elementos na tela foram visitados. Isso ocorre
porque todos os outros elementos têm um traversalIndex
padrão de 0f
, e o
os elementos de texto do relógio são lidos depois de todos os outros elementos 0f
.
Exemplo: personalizar a ordem de apresentação do botão de ação flutuante
Neste exemplo, traversalIndex
e isTraversalGroup
controlam o
Ordenação de travessia de um botão de ação flutuante (FAB, na sigla em inglês) do Material Design. Base
deste exemplo é o seguinte layout:
Por padrão, o layout neste exemplo tem a seguinte ordem do TalkBack:
Barra de apps na parte de cima → Textos de exemplo de 0 a 6 → botão de ação flutuante (FAB) → parte de baixo Barra de apps
Talvez você queira que o leitor de tela se concentre no FAB. Para definir um
traversalIndex
em um elemento do Material Design, como um FAB, faça o seguinte:
@Composable fun FloatingBox() { Box(modifier = Modifier.semantics { isTraversalGroup = true; traversalIndex = -1f }) { FloatingActionButton(onClick = {}) { Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon") } } }
Neste snippet, criar uma caixa com
isTraversalGroup
definido como true
e definindo um traversalIndex
na mesma caixa
(-1f
é menor que o valor padrão de 0f
) significa que a caixa flutuante
vem antes de todos os outros elementos na tela.
Em seguida, você pode colocar a caixa flutuante e outros elementos em um scaffold. implementa um layout do Material Design:
@OptIn(ExperimentalMaterial3Api::class) @Composable fun ColumnWithFABFirstDemo() { Scaffold( topBar = { TopAppBar(title = { Text("Top App Bar") }) }, floatingActionButtonPosition = FabPosition.End, floatingActionButton = { FloatingBox() }, content = { padding -> ContentColumn(padding = padding) }, bottomBar = { BottomAppBar { Text("Bottom App Bar") } } ) }
O TalkBack interage com os elementos na seguinte ordem:
FAB → barra de apps superior → textos de exemplo de 0 a 6 → barra de apps inferior
Outros recursos
- Acessibilidade:conceitos e conceitos essenciais técnicas comuns a todo o desenvolvimento de apps Android
- Criar apps acessíveis:etapas principais para deixar seu app mais acessível
- Princípios para melhorar o app acessibilidade:os princípios-chave para ter em mente ao trabalhar para tornar seu app mais acessível
- Testes de acessibilidade: Princípios e ferramentas de teste para acessibilidade no Android