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 árvore de semântica. Esta árvore descreve a IU de uma maneira alternativa, que é compreensível para os serviços de acessibilidade e para o framework 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 as informações sobre como mostrar os elementos combináveis, mas ela contém informações 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 a semântica correspondente manualmente. 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 receberão 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 "Agenda" ou, um pouco melhor, "Agenda de abril", e o usuário
ficaria sem saber que dia foi selecionado. Para tornar esse elemento combinável mais
acessível, é necessário adicionar informações semânticas manualmente.
Propriedades semânticas
Todos os nós na árvore da IU com algum significado semântico têm um nó paralelo na
árvore semântica. O nó na árvore semântica contém essas propriedades que
transmitem o significado da função que pode ser composta correspondente. Por exemplo, o elemento combinável Text
contém uma propriedade semântica text
, porque esse é o significado
desse elemento. Uma Icon
contém uma propriedade contentDescription
, se definida pelo
desenvolvedor, que transmite em texto o significado do Icon
.
Os elementos combináveis e modificadores criados com base na biblioteca
fundamental do Compose já definem as propriedades relevantes. Opcionalmente, defina
ou substitua as propriedades com os modificadores semantics
e
clearAndSetSemantics
. Por exemplo, adicione ações
de acessibilidade personalizadas a um nó, forneça uma descrição
de estado alternativa para um elemento alternável ou indique que um determinado elemento
de texto precisa ser considerado como título.
Para visualizar a árvore semântica, use a ferramenta Layout Inspector ou o
método printToLog()
nos testes. Isso mostra a árvore semântica atual
dentro do Logcat.
class MyComposeTest { @get:Rule val composeTestRule = createComposeRule() @Test fun MyTest() { // Start the app composeTestRule.setContent { MyTheme { Text("Hello world!") } } // Log the full semantics tree composeTestRule.onRoot().printToLog("MY TAG") } }
A saída desse teste seria semelhante a esta:
Printing with useUnmergedTree = 'false'
Node #1 at (l=0.0, t=63.0, r=221.0, b=120.0)px
|-Node #2 at (l=0.0, t=63.0, r=221.0, b=120.0)px
Text = '[Hello world!]'
Actions = [GetTextLayoutResult]
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:
Para descrever o significado desse elemento, você pode dizer: "É um switch, que é um elemento alternável no estado "Ativado". Você pode clicar nele para interagir com ele".
É exatamente para isso que servem as propriedades semânticas. O nó semântico desse elemento Switch contém as propriedades abaixo, conforme visualizadas com o Layout Inspector:
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 ela 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. Para
conferir uma lista completa de propriedades semânticas, consulte o objeto
SemanticsProperties
. Para ver uma lista completa das possíveis ações de acessibilidade, consulte o objeto
SemanticsActions
.
O monitoramento das propriedades semânticas de cada função que pode ser composta no app resulta em muitas possibilidades eficientes. Alguns exemplos:
- O TalkBack usa as propriedades para ler em voz alta o que é mostrado na tela e permite que o usuário interaja facilmente com ele. Para o elemento combinável "Switch", o TalkBack pode dizer: "Ativado; Chave; toque duas vezes para alternar". O usuário pode tocar duas vezes na tela para desativar a opção.
-
O framework de testes usa as propriedades para encontrar nós, interagir com
eles e fazer declarações. Este é um exemplo de teste do switch:
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Árvores semânticas mescladas e não mescladas
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 combinável 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, é possível pensar sobre 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
. Pense 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 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 estão mesclados.
Leia a documentação de acessibilidade para saber mais sobre quando mudar
o comportamento de mesclagem do elemento combinável.
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 as árvores
A árvore semântica é, na verdade, duas árvores diferentes. Há uma árvore semântica mesclada, que mescla os 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 exibir a árvore não mesclada, defina o parâmetro useUnmergedTree
do matcher onRoot()
como true
:
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
O Layout Inspector permite mostrar a árvore semântica mesclada e a não mesclada, selecionando a árvore de 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 que você pode interagir com uma Button
fazendo a correspondência do texto mostrado
nele:
composeTestRule.onNodeWithText("Like").performClick()
Modifique esse comportamento definindo o parâmetro useUnmergedTree
dos
matchers como true
, assim como o matcher onRoot
.
Comportamento de mesclagem
Quando uma função que pode ser composta indica que os descendentes precisam ser mesclados, como a mesclagem ocorre exatamente?
Cada propriedade semântica tem uma estratégia de mesclagem definida. Por exemplo, a
propriedade ContentDescription
adiciona todos os valores descendentes de ContentDescription a uma
lista. Verifique a estratégia de mesclagem de uma propriedade semântica verificando a
implementação de mergePolicy
em SemanticsProperties.kt
. As propriedades podem
usar o valor pai ou filho, mesclar os valores em uma lista ou
string, não permitir a mesclagem e gerar uma exceção ou qualquer outra
estratégia de mesclagem personalizada.
É importante observar que os descendentes que definiram mergeDescendants
= true
não são incluídos na mesclagem. Confira um exemplo:
Veja um item de lista clicável. Quando o usuário pressiona a linha, o app navega para a página de detalhes do artigo, onde é possível ler o artigo. Dentro do item da lista, há um botão para adicionar a matéria aos favoritos, formando um elemento clicável aninhado para que o botão apareça separadamente na árvore mesclada. O restante do conteúdo da linha será mesclado:
Adaptar a árvore semântica
Como mencionado anteriormente, é possível substituir ou limpar algumas propriedades semânticas ou mudar o comportamento de mesclagem da árvore. Isso é especialmente relevante quando você está criando seus próprios componentes personalizados. Sem definir as propriedades e o comportamento de mesclagem corretos, o app pode não ser acessível e os testes podem se comportar de maneira diferente do esperado. Para ler mais sobre alguns casos de uso comuns em que é preciso adaptar a árvore semântica, leia a documentação de acessibilidade. Se você quiser saber mais sobre testes, confira o guia de testes.
Outros recursos
- Acessibilidade:conceitos e técnicas essenciais comuns a todo o desenvolvimento de apps Android.
- Criar apps acessíveis:as principais etapas que você pode seguir para tornar seu app mais acessível.
- Princípios para melhorar a acessibilidade do app:princípios importantes que precisam ser considerados ao trabalhar para tornar seu app mais acessível.
- Testes de acessibilidade:princípios e ferramentas de teste para acessibilidade do Android.
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