Além das informações principais que um elemento combinável carrega, como uma string de texto
de um elemento combinável Text, pode ser útil ter mais informações
complementares sobre os elementos da interface.
As informações sobre o significado e a função de um componente no Compose são chamadas de semântica, que é uma forma de fornecer mais contexto sobre os elementos combináveis para serviços como acessibilidade, preenchimento automático e testes. Por exemplo, um ícone de câmera pode ser visualmente apenas uma imagem, mas o significado semântico pode ser "Tirar uma foto".
Ao combinar a semântica adequada com as APIs do Compose apropriadas, você fornece o máximo de informações possível sobre o componente aos serviços de acessibilidade, que decidem como representá-lo para o usuário.
As APIs Material, Compose UI e Foundation vêm com semântica integrada que segue o papel e a função específicos delas, mas você também pode modificar essa semântica para APIs atuais ou definir novas para componentes personalizados, de acordo com seus requisitos específicos.
Propriedades semânticas
As propriedades semânticas transmitem o significado do elemento combinável correspondente. Por
exemplo, o elemento combinável Text contém uma propriedade semântica text, porque
esse é o significado desse elemento. Um Icon contém uma propriedade contentDescription, se definida pelo desenvolvedor, que transmite em texto o significado do ícone.
Considere como as propriedades semânticas transmitem o significado de um elemento combinável. Considere um
Switch. Para o usuário, ele tem esta aparência:
Switch nos estados "Ativado" e "Desativado".Para descrever o significado desse elemento, você poderia dizer o seguinte: "Esta é uma chave, um elemento alternável no estado 'Ativado'. Clique nele para interagir."
É exatamente para isso que servem as propriedades semânticas. O nó de semântica desse elemento Switch contém as seguintes propriedades, conforme visualizado com o Layout Inspector:
Switch.O Role indica o tipo de elemento. O StateDescription descreve como
o estado "Ativado" precisa ser referenciado. Por padrão, essa é uma versão localizada da
palavra "Ativado", mas pode ser mais específica (por exemplo, "Ativado") com base
no contexto. O ToggleableState é o estado atual do interruptor. A propriedade OnClick faz referência ao método usado para interagir com esse elemento.
Acompanhar as propriedades semânticas de cada elemento combinável no app abre muitas possibilidades:
- Os serviços de acessibilidade usam as propriedades para representar a IU mostrada na tela e permitir que os usuários interajam com ela. Para o elemento de composição Switch, o TalkBack pode anunciar: "Ativado; Interruptor, toque duas vezes para ativar/desativar". O usuário pode tocar duas vezes na tela para desativar o interruptor.
-
O framework de teste usa as propriedades para encontrar nós, interagir com
eles e fazer declarações:
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Os elementos que podem ser compostos e os modificadores criados de acordo com a biblioteca Compose Foundation já definem as propriedades relevantes para você por padrão. Se quiser, você pode mudar essas propriedades manualmente para melhorar o suporte à acessibilidade em casos de uso específicos ou mudar a estratégia de mesclagem ou limpeza dos elementos que podem ser compostos.
Para sinalizar o tipo de conteúdo específico do componente para os serviços de acessibilidade, é possível aplicar uma variedade de semânticas diferentes. Essas adições vão dar suporte às principais informações semânticas em vigor e ajudar os serviços de acessibilidade a ajustar como o componente é representado, anunciado ou interagido.
Para uma lista completa de propriedades de semântica, consulte o
objeto SemanticsProperties. Para uma lista completa de possíveis ações de acessibilidade, consulte o objeto SemanticsActions.
Títulos
Os apps geralmente contêm telas com conteúdo avançado, como artigos longos ou páginas de notícias, que costumam ser divididas em diferentes subseções com títulos:
Usuários com necessidades de acessibilidade podem ter dificuldades para navegar em uma tela como
essa. Para melhorar a experiência de navegação, alguns serviços de acessibilidade permitem
uma navegação mais fácil diretamente entre seções ou títulos. Para ativar esse recurso,
indique que o componente é um heading definindo a propriedade
semântica dele:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Alertas e pop-ups
Se o componente for um alerta ou um pop-up, como um Snackbar, talvez seja necessário
sinalizar aos serviços de acessibilidade que uma nova estrutura ou atualizações de conteúdo
podem ser transmitidas aos usuários.
Os componentes semelhantes a alertas podem ser marcados com a propriedade de semântica
liveRegion. Isso permite que os serviços de acessibilidade notifiquem automaticamente o usuário sobre
mudanças nesse componente ou nos filhos dele:
PopupAlert( message = "You have a new message", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite } )
Use liveRegionMode.Polite na maioria dos casos em que a atenção dos usuários
precisa ser direcionada brevemente para alertas ou mudanças importantes no conteúdo da tela.
Use o liveRegion.Assertive com moderação para evitar feedback que atrapalhe.
Ele deve ser usado em situações em que é crucial que os usuários sejam informados
sobre conteúdo urgente:
PopupAlert( message = "Emergency alert incoming", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Assertive } )
As regiões dinâmicas não devem ser usadas em conteúdo que é atualizado com frequência, como timers de contagem regressiva, para evitar sobrecarregar os usuários com feedback constante.
Componentes semelhantes a janelas
Componentes personalizados semelhantes a janelas, como ModalBottomSheet, precisam de outros
indicadores para se diferenciarem do conteúdo ao redor. Para isso, use a semântica
paneTitle para que todas as mudanças relevantes de janela ou painel sejam
representadas adequadamente pelos serviços de acessibilidade, junto com as principais
informações semânticas:
ShareSheet( message = "Choose how to share this photo", modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .semantics { paneTitle = "New bottom sheet" } )
Para referência, consulte como o Material 3 usa paneTitle nos componentes.
Componentes de erro
Para outros tipos de conteúdo, como componentes semelhantes a erros, talvez seja necessário
expandir as principais informações semânticas para usuários com necessidades de acessibilidade.
Ao definir estados de erro, você pode informar os serviços de acessibilidade sobre a semântica
error e fornecer mensagens de erro expandidas.
Neste exemplo, o TalkBack lê as informações principais do texto de erro, seguidas por mensagens adicionais expandidas:
Error( errorText = "Fields cannot be empty", modifier = Modifier .semantics { error("Please add both email and password") } )
Componentes de monitoramento do progresso
Para componentes personalizados que acompanham o progresso, talvez seja interessante notificar os usuários sobre
as mudanças no progresso, incluindo o valor atual, o intervalo e
o tamanho da etapa. Para isso, use a semântica progressBarRangeInfo, que
garante que os serviços de acessibilidade estejam cientes das mudanças no progresso e possam
atualizar os usuários de acordo. Diferentes tecnologias adaptativas também podem ter maneiras
exclusivas de sugerir o aumento e a diminuição da progressão.
ProgressInfoBar( modifier = Modifier .semantics { progressBarRangeInfo = ProgressBarRangeInfo( current = progress, range = 0F..1F ) } )
Informações de listas e itens
Em listas e grades personalizadas com muitos itens, pode ser útil para os serviços de acessibilidade receberem informações mais detalhadas, como o número total de itens e índices.
Ao usar a semântica collectionInfo e collectionItemInfo na
lista e nos itens, respectivamente, nessa lista longa, os serviços de acessibilidade podem
informar aos usuários em qual índice de item eles estão em relação ao total da coleção, além
de informações semânticas textuais:
MilkyWayList( modifier = Modifier .semantics { collectionInfo = CollectionInfo( rowCount = milkyWay.count(), columnCount = 1 ) } ) { milkyWay.forEachIndexed { index, text -> Text( text = text, modifier = Modifier.semantics { collectionItemInfo = CollectionItemInfo(index, 0, 0, 0) } ) } }
Descrição do estado
Um elemento combinável pode definir uma stateDescription como semântica, que é usada pelo
framework do Android para ler o estado em que o elemento se encontra. Por
exemplo, uma função combinável alternável pode se encontrar no estado "marcado" ou "desmarcado". Em alguns casos, pode ser necessário substituir os rótulos de descrição de estado padrão
usados pelo Compose. Para fazer isso, especifique explicitamente os rótulos de descrição de estado
antes de definir um elemento combinável como alternável:
@Composable private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) { val stateSubscribed = stringResource(R.string.subscribed) val stateNotSubscribed = stringResource(R.string.not_subscribed) Row( modifier = Modifier .semantics { // Set any explicit semantic properties stateDescription = if (selected) stateSubscribed else stateNotSubscribed } .toggleable( value = selected, onValueChange = { onToggle() } ) ) { /* ... */ } }
Ações personalizadas
As ações personalizadas podem ser usadas para gestos mais complexos na tela touchscreen, como deslizar para dispensar ou arrastar e soltar, já que essas ações podem ser difíceis para usuários com deficiências motoras ou outras.
Para tornar o gesto de deslizar para dispensar mais acessível, vincule-o a uma ação personalizada, transmitindo a ação de dispensar e o rótulo:
SwipeToDismissBox( modifier = Modifier.semantics { // Represents the swipe to dismiss for accessibility customActions = listOf( CustomAccessibilityAction( label = "Remove article from list", action = { removeArticle() true } ) ) }, state = rememberSwipeToDismissBoxState(), backgroundContent = {} ) { ArticleListItem() }
Um serviço de acessibilidade como o TalkBack destaca o componente e indica que há mais ações disponíveis no menu, representando a ação de deslizar para dispensar:
Outro caso de uso para ações personalizadas são listas longas com itens que têm mais ações disponíveis, já que pode ser cansativo para os usuários iterar cada ação para cada item individualmente:
Para melhorar a experiência de navegação, o que é especialmente útil para tecnologias adaptativas baseadas em interação, como o acesso com interruptor ou o Voice Access, use ações personalizadas no contêiner para mover ações de uma travessia individual para um menu de ações separado:
ArticleListItemRow( modifier = Modifier .semantics { customActions = listOf( CustomAccessibilityAction( label = "Open article", action = { openArticle() true } ), CustomAccessibilityAction( label = "Add to bookmarks", action = { addToBookmarks() true } ), ) } ) { Article( modifier = Modifier.clearAndSetSemantics { }, onClick = openArticle, ) BookmarkButton( modifier = Modifier.clearAndSetSemantics { }, onClick = addToBookmarks, ) }
Nesses casos, limpe manualmente a semântica dos filhos originais
com o modificador clearAndSetSemantics, já que você os está movendo para
ações personalizadas.
Usando o acesso com interruptor como exemplo, o menu é aberto ao selecionar o contêiner e lista as ações aninhadas disponíveis:
Árvore semântica
Uma composição descreve a interface do app e é produzida pela execução de elementos combináveis. A composição é uma estrutura de árvore que consiste nos elementos combináveis que descrevem a interface.
Ao lado da composição, há uma árvore paralela, chamada de árvore de semântica. Essa árvore descreve a IU de uma maneira alternativa e compreensível para os serviços de acessibilidade e os frameworks de testes. Os serviços de acessibilidade usam a árvore para descrever o app para usuários com uma necessidade específica. O framework de teste usa a árvore para interagir com o app e fazer declarações sobre ele. A árvore semântica não contém informações sobre como mostrar os elementos que podem ser compostos, mas sim sobre o significado semântico deles.
Caso o app seja formado por funções que podem ser compostas e modificadores da biblioteca de base do Compose e da biblioteca do Material Design, a árvore semântica será preenchida e gerada automaticamente para você. No entanto, ao adicionar elementos combináveis personalizados de baixo nível, é necessário fornecer manualmente a semântica deles. Também pode haver situações em que a árvore não representa o significado dos elementos na tela de forma correta ou completa. Nesse caso, é possível adaptar a árvore.
Considere, por exemplo, esta função que pode ser composta de agenda personalizada:
Neste exemplo, a agenda toda é implementada como um única função que pode ser composta
de baixo nível, usando a função Layout que pode ser composta e exibindo diretamente em Canvas.
Se você não fizer mais nada, os serviços de acessibilidade não vão receber informações suficientes
sobre o conteúdo do elemento combinável e a seleção do usuário na
agenda. Por exemplo, se o usuário clicar no dia 17, o
framework de acessibilidade receberá apenas as informações de descrição de todo o
controle da agenda. Nesse caso, o serviço de acessibilidade do TalkBack anunciaria apenas "Agenda" ou, em uma hipótese um pouco melhor, "Agenda de abril", mas o usuário
ficaria sem saber que dia foi selecionado. Para tornar esse elemento combinável mais
acessível, você precisa adicionar informações semânticas manualmente.
Árvore mesclada e não mesclada
Como mencionado anteriormente, é possível ter zero ou mais propriedades definidas para cada função que pode ser composta na árvore da IU. Quando um elemento que pode ser composto não tem propriedades semânticas definidas, ele não é incluído como parte da árvore semântica. Dessa forma, a árvore semântica contém apenas os nós que realmente têm significado semântico. No entanto, algumas vezes pode ser útil mesclar subárvores de nós específicas e tratá-las como uma só para transmitir o significado correto do conteúdo exibido na tela. Dessa forma, você pode pensar em um conjunto de nós como um todo, em vez de lidar com cada nó descendente individualmente. Como regra geral, cada nó da árvore representa um elemento focalizável ao usar os serviços de acessibilidade.
Um exemplo desse tipo de elemento combinável é Button. Você pode pensar em um botão
como um único elemento, mesmo que ele contenha vários nós filhos:
Button(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = null ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Like") }
Na árvore de semântica, as propriedades dos descendentes do botão são mescladas, e o botão é apresentado como um único nó de folha na árvore:
As funções que podem ser compostas e os modificadores podem indicar que querem mesclar as
propriedades semânticas dos descendentes chamando o método Modifier.semantics
(mergeDescendants = true) {}. Definir essa propriedade como true indica que
as propriedades semânticas precisam ser mescladas. No exemplo do Button, o elemento combinável Button
usa o modificador clickable internamente, que inclui o
modificador semantics. Portanto, os nós descendentes do botão são mesclados.
Leia a documentação de acessibilidade para saber mais sobre quando mudar
o comportamento de mesclagem no elemento que pode ser composto.
Vários modificadores e funções que podem ser compostas nas bibliotecas Compose Foundation e
Compose Material têm essa propriedade definida. Por exemplo, os modificadores clickable e toggleable
mesclam os descendentes automaticamente. Além disso, a função ListItem
mesclará os descendentes.
Inspecionar a árvore
A árvore semântica é, na verdade, duas árvores diferentes. Há uma árvore de semântica mesclada,
que mescla nós descendentes quando mergeDescendants é definido como true.
Há também uma árvore semântica não mesclada, que não aplica a mesclagem, mas
mantém todos os nós intactos. Os serviços de acessibilidade usam a árvore não mesclada e aplicam
os próprios algoritmos de mesclagem, considerando a propriedade
mergeDescendants. O framework de teste usa a árvore mesclada por padrão.
É possível inspecionar as duas árvores usando o método printToLog(). Por padrão, e como nos
exemplos anteriores, a árvore mesclada é registrada. Para imprimir a árvore sem mesclagem, defina o parâmetro useUnmergedTree do matcher onRoot() como
true:
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
O Layout Inspector permite mostrar as árvores de semântica mesclada e não mesclada. Basta selecionar a opção de sua preferência no filtro de visualização:
O Layout Inspector mostra a semântica mesclada e a semântica definida de cada nó da árvore no painel de propriedades:
Por padrão, os matchers do framework de testes usam a árvore semântica mesclada.
Por isso, é possível interagir com um Button correspondendo ao texto mostrado
nele:
composeTestRule.onNodeWithText("Like").performClick()
Substitua esse comportamento definindo o parâmetro useUnmergedTree dos
comparadores como true, como no comparador onRoot.
Adaptar a árvore
Como mencionado antes, é possível modificar ou limpar determinadas propriedades de semântica ou mudar o comportamento de mesclagem da árvore. Isso é particularmente relevante quando você está criando seus próprios componentes personalizados. Sem definir as propriedades e o comportamento de mesclagem corretos, seu app pode não ser acessível, e os testes podem se comportar de maneira diferente do esperado. Para saber mais sobre testes, consulte o guia de testes.
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Acessibilidade no Compose
- Material Design 2 no Compose
- Como testar o layout do Compose