Acessibilidade no Jetpack Compose

1. Introdução

Neste codelab, você aprenderá a usar o Jetpack Compose para melhorar a acessibilidade do seu app. Analisaremos vários casos de uso comuns e faremos a melhoria passo a passo de um app de exemplo. Falaremos sobre os tamanhos de área de toque, as descrições de conteúdo, os marcadores de clique e muito mais.

Pessoas com problemas de visão, daltonismo, dificuldades auditivas, comprometimento motor, deficiências cognitivas e muitos outros tipos de deficiência usam dispositivos Android para realizar tarefas no dia a dia. Ao desenvolver apps com a acessibilidade em mente, você melhora a experiência do usuário, especialmente para aqueles que têm essas e outras necessidades de acessibilidade.

Neste codelab, usaremos o TalkBack para testar manualmente nossas mudanças no código. O TalkBack é um serviço de acessibilidade usado principalmente por pessoas com deficiência visual. Não deixe de testar as mudanças no seu código com outros serviços de acessibilidade, como o acesso com interruptor.

Retângulo de foco do TalkBack se movendo pela tela inicial do Jetnews. O texto anunciado pelo TalkBack é mostrado na parte inferior da tela.

TalkBack em ação no app Jetnews.

O que você aprenderá

Neste codelab, você aprenderá o seguinte:

  • Como atender usuários que têm comprometimento motor aumentando o tamanho das áreas de toque.
  • O que são propriedades semânticas e como fazer alterações.
  • Como fornecer informações para elementos que podem ser compostos e melhorar a acessibilidade.

O que será necessário

O que você criará

Neste codelab, melhoraremos a acessibilidade de um app para leitura de notícias. Começaremos com um app que não tem recursos de acessibilidade essenciais e vamos aplicar o que aprendemos para fazer com que o app seja mais utilizável por pessoas com essas necessidades.

2. Etapas da configuração

Nesta etapa, você fará o download do código, que inclui um app simples para leitura de notícias.

O que será necessário

Buscar o código

O código deste codelab pode ser encontrado no repositório android-compose-codelabs do GitHub (link em inglês). Para clonar, execute o seguinte:

$ git clone https://github.com/googlecodelabs/android-compose-codelabs

Outra opção é fazer o download de dois arquivos ZIP:

Conferir o app de exemplo

Você fez o download de um arquivo com código para todos os codelabs disponíveis do Compose. Para concluir este codelab, abra o projeto AccessibilityCodelab no Android Studio.

Recomendamos que você comece com o código na ramificação main e siga o codelab passo a passo no seu ritmo.

Configurar o TalkBack

Durante este codelab, usaremos o TalkBack para conferir nossas mudanças. Ao usar um dispositivo físico para testes, siga estas instruções para ativar o TalkBack. Os emuladores não vêm com o TalkBack instalado por padrão. Escolha um emulador que inclua a Play Store e faça o download do app Acessibilidade do Android.

3. Tamanho da área de toque

Todos os elementos mostrados na tela que possam ser clicados, tocados ou com os quais é possível interagir de alguma outra forma precisam ser grandes o suficiente para uma interação confiável. Esses elementos precisam ter pelo menos 48 dp de largura e altura.

Se esses controles forem dimensionados dinamicamente ou redimensionados com base no tamanho do conteúdo, use o modificador sizeIn para definir um limite inferior para as dimensões.

Alguns componentes do Material Design definem esses tamanhos para você. Por exemplo, o elemento de botão que pode ser composto tem a propriedade MinHeight definida como 36 dp e usa um padding vertical de 8 dp. Isso equivale à altura necessária de 48 dp.

Quando abrimos o app de exemplo e executamos o TalkBack, percebemos que o ícone de X nos cards de postagem tem uma área de toque muito pequena. Essa área precisa ter pelo menos 48 dp.

Veja esta captura de tela com o app original à esquerda e a solução aprimorada à direita.

Comparação de um item da lista mostrando um contorno pequeno do ícone de X à esquerda e um contorno grande à direita.

Vamos analisar a implementação e conferir o tamanho desse elemento. Abra PostCards.kt e procure o elemento PostCardHistory. Como você pode ver, a implementação define o tamanho do ícone do menu flutuante como 24 dp:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...

   Row(
       // ...
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           Icon(
               imageVector = Icons.Default.Close,
               contentDescription = stringResource(R.string.cd_show_fewer),
               modifier = Modifier
                   .clickable { openDialog = true }
                   .size(24.dp)
           )
       }
   }
   // ...
}

Para aumentar o tamanho da área de toque desse Icon, podemos adicionar padding:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       // ...
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           Icon(
               imageVector = Icons.Default.Close,
               contentDescription = stringResource(R.string.cd_show_fewer),
               modifier = Modifier
                   .clickable { openDialog = true }
                   .padding(12.dp)
                   .size(24.dp)
           )
       }
   }
   // ...
}

Em nosso caso de uso, há uma maneira mais fácil de garantir que a área de toque tenha pelo menos 48 dp. Podemos usar o componente IconButton do Material Design, que lidará com isso para nós:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       // ...
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           IconButton(onClick = { openDialog = true }) {
               Icon(
                   imageVector = Icons.Default.Close,
                   contentDescription = stringResource(R.string.cd_show_fewer)
               )
           }
       }
   }
   // ...
}

A navegação pela tela com o TalkBack agora mostra corretamente uma área de toque de 48 dp. Além disso, o IconButton também adiciona uma indicação de ondulação, que mostra ao usuário que o elemento é clicável.

4. Marcadores de clique

Por padrão, os elementos clicáveis do app não fornecem informações sobre o que esse elemento fará. Por isso, serviços de acessibilidade como o TalkBack usam uma descrição padrão muito genérica.

Para oferecer a melhor experiência aos usuários com necessidades de acessibilidade, podemos fornecer uma descrição específica que explique o que acontecerá quando o usuário clicar nesse elemento.

No app Jetnews, os usuários podem clicar nos vários cards para ler a postagem completa. Por padrão, isso fará com que o conteúdo do elemento clicável seja lido, seguido pelo texto "Toque duas vezes para ativar". Em vez disso, queremos ser mais específicos e usar "Toque duas vezes para ler o artigo". Esta é a versão original comparada à solução ideal:

Duas gravações de tela com o TalkBack ativado com o usuário tocando em uma postagem em uma lista vertical e em um carrossel horizontal.

Mudança do marcador de clique de um elemento que pode ser composto. Antes (à esquerda) x depois (à direita).

Elementos que podem ser compostos, como Surface e Card, e o modificador clickable incluem um parâmetro em que podemos definir diretamente esse marcador de clique.

Analise mais uma vez a implementação do elemento PostCardHistory:

@Composable
fun PostCardHistory(
   // ...
) {
   Row(
       Modifier.clickable { navigateToArticle(post.id) }
   ) {
       // ...
   }
}

Como você pode ver, essa implementação usa o modificador clickable. Para definir um marcador de clique, podemos definir o parâmetro onClickLabel:

@Composable
fun PostCardHistory(
   // ...
) {
   Row(
       Modifier.clickable(
               // R.string.action_read_article = "read article"
               onClickLabel = stringResource(R.string.action_read_article)
           ) {
               navigateToArticle(post.id)
           }
   ) {
       // ...
   }
}

O TalkBack agora informa "Toque duas vezes para ler o artigo".

Os outros cards na tela inicial têm o mesmo marcador de clique genérico. Vamos dar uma olhada na implementação do elemento PostCardPopular que pode ser composto e atualizar o marcador de clique dele:

@Composable
fun PostCardPopular(
   // ...
) {
   Card(
       shape = MaterialTheme.shapes.medium,
       modifier = modifier.size(280.dp, 240.dp),
       onClick = { navigateToArticle(post.id) }
   ) {
       // ...
   }
}

Esse elemento usa o Card internamente, o que causa uma sobrecarga que permite transmitir o marcador de clique:

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PostCardPopular(
   post: Post,
   navigateToArticle: (String) -> Unit,
   modifier: Modifier = Modifier
) {
   Card(
       shape = MaterialTheme.shapes.medium,
       modifier = modifier.size(280.dp, 240.dp),
       onClick = { navigateToArticle(post.id) },
       onClickLabel = stringResource(id = R.string.action_read_article)
   ) {
       // ...
   }
}

5. Ações personalizadas

Muitos apps mostram algum tipo de lista em que cada item contém uma ou mais ações. Ao usar um leitor de tela, a navegação por essa lista pode ser uma tarefa cansativa, porque a mesma ação se repetirá inúmeras vezes.

Em vez disso, podemos adicionar ações de acessibilidade personalizadas a elementos que podem ser compostos. Assim, as ações relacionadas ao mesmo item da lista podem ser agrupadas.

No app Jetnews, mostramos uma lista de artigos que o usuário pode ler. Cada item inclui uma ação para indicar que o usuário quer ver menos sobre esse tema. Aqui, moveremos isso para uma ação de acessibilidade personalizada para facilitar a navegação pela lista.

À esquerda, está a situação padrão, em que cada ícone de X é focalizável. À direita, está a solução, em que a ação está incluída nas ações personalizadas do TalkBack:

Duas gravações de tela com o TalkBack ativado. A tela à esquerda mostra como o ícone de X no item da postagem pode ser selecionado. Tocar duas vezes abre uma caixa de diálogo. A tela à direita mostra o uso de um gesto de três toques para abrir um menu de ações personalizadas. Tocar na ação "Mostrar menos disso" abre a mesma caixa de diálogo.

Adicionar uma ação personalizada a um item da postagem. Antes (à esquerda) x depois (à direita).

Vamos abrir o PostCards.kt e analisar a implementação do elemento PostCardHistory que pode ser composto. Observe as propriedades clicáveis de Row e IconButton, usando Modifier.clickable e onClick:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       Modifier.clickable(
           onClickLabel = stringResource(R.string.action_read_article)
       ) {
           navigateToArticle(post.id)
       }
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
           IconButton(onClick = { openDialog = true }) {
               Icon(
                   imageVector = Icons.Default.Close,
                   contentDescription = stringResource(R.string.cd_show_fewer)
               )
           }
       }
   }
   // ...
}

Por padrão, tanto Row quanto o elemento IconButton são clicáveis, então eles serão focalizáveis no TalkBack. Isso acontece com cada item da lista, o que significa que são necessários muitos gestos de deslizar durante a navegação. Recomendamos que a ação relacionada ao IconButton seja incluída como uma ação personalizada no item da lista. Podemos instruir os serviços de acessibilidade a não interagirem com esse Icon usando o modificador clearAndSetSemantics:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   Row(
       Modifier.clickable(
           onClickLabel = stringResource(R.string.action_read_article)
       ) {
           navigateToArticle(post.id)
       }
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            IconButton(
                modifier = Modifier.clearAndSetSemantics { },
                onClick = { openDialog = true }
            ) {
                Icon(
                    imageVector = Icons.Default.Close,
                    contentDescription = stringResource(R.string.cd_show_fewer)
                )
            }
       }
   }
   // ...
}

No entanto, ao remover a semântica do IconButton, não será mais possível executar a ação. Em vez disso, podemos adicionar a ação ao item de lista adicionando uma ação personalizada ao modificador semantics:

@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
   // ...
   val showFewerLabel = stringResource(R.string.cd_show_fewer)
   Row(
        Modifier
            .clickable(
                onClickLabel = stringResource(R.string.action_read_article)
            ) {
                navigateToArticle(post.id)
            }
            .semantics {
                customActions = listOf(
                    CustomAccessibilityAction(
                        label = showFewerLabel,
                        // action returns boolean to indicate success
                        action = { openDialog = true; true }
                    )
                )
            }
   ) {
       // ...
       CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            IconButton(
                modifier = Modifier.clearAndSetSemantics { },
                onClick = { openDialog = true }
            ) {
                Icon(
                    imageVector = Icons.Default.Close,
                    contentDescription = stringResource(R.string.cd_show_fewer)
                )
            }
       }
   }
   // ...
}

Agora, podemos usar o pop-up de ação personalizada no TalkBack para aplicar a ação. Isso fica cada vez mais relevante à medida que o número de ações dentro de um item da lista aumenta.

6. Descrições de elementos visuais

Nem todos os usuários do app poderão ver ou interpretar os elementos visuais mostrados nele, como ícones e ilustrações. Os serviços de acessibilidade também não podem detectar elementos visuais com base apenas nos pixels. Isso faz com que o desenvolvedor precise transmitir mais informações sobre os elementos visuais do app para os serviços de acessibilidade.

Elementos visuais que podem ser compostos, como Image e Icon, incluem um parâmetro contentDescription. Aqui, você transmitirá uma descrição localizada desse elemento visual ou o valor null, se o elemento for puramente decorativo.

Em nosso app, faltam algumas descrições de conteúdo na tela do artigo. Vamos executar o app e selecionar o primeiro artigo para navegar até a tela dele.

Duas gravações de tela com o TalkBack ativado e em que o botão "Voltar" é tocado na tela do artigo. À esquerda, é mostrado "Botão. Toque duas vezes para ativar". À direita, é mostrado "Navegar para cima. Toque duas vezes para ativar".

Adição de uma descrição visual do conteúdo. Antes (à esquerda) x depois (à direita).

Quando não fornecemos informações, o ícone de navegação no canto superior esquerdo anuncia a mensagem "Botão. Toque duas vezes para ativar". Isso não informa nada ao usuário sobre a ação que será realizada quando ele ativar esse botão. Vamos abrir o ArticleScreen.kt:

@Composable
fun ArticleScreen(
   // ...
) {
   // ...
   Scaffold(
       topBar = {
           InsetAwareTopAppBar(
               title = {
                   // ...
               },
               navigationIcon = {
                   IconButton(onClick = onBack) {
                       Icon(
                           imageVector = Icons.Filled.ArrowBack,
                           contentDescription = null
                       )
                   }
               }
           )
       }
   ) {
       // ...
   }
}

Adicione uma descrição clara de conteúdo para o ícone:

@Composable
fun ArticleScreen(
   // ...
) {
   // ...
   Scaffold(
       topBar = {
           InsetAwareTopAppBar(
               title = {
                   // ...
               },
               navigationIcon = {
                   IconButton(onClick = onBack) {
                       Icon(
                           imageVector = Icons.Filled.ArrowBack,
                           contentDescription = stringResource(
                               R.string.cd_navigate_up
                           )
                       )
                   }
               }
           )
       }
   ) {
       // ...
   }
}

Outro elemento visual deste artigo é a imagem do cabeçalho. Em nosso caso, essa imagem é decorativa, ou seja, ela não mostra nada que precisamos transmitir ao usuário. Portanto, a descrição do conteúdo é definida com o valor null e o elemento é ignorado quando usamos um serviço de acessibilidade.

O último elemento visual da tela é a foto do perfil. Neste caso, estamos usando um avatar genérico, então não é necessário adicionar uma descrição de conteúdo aqui. Ao usar a foto do perfil real desse autor, podemos pedir que ele forneça uma descrição de conteúdo adequada (link em inglês).

7. Títulos

Quando uma tela tem muito texto, como nossa tela de artigo, os usuários com deficiência visual têm dificuldade para encontrar rapidamente a seção que procuram. Para ajudar nisso, podemos indicar quais partes do texto são cabeçalhos. Os usuários podem navegar rapidamente por esses cabeçalhos diferentes deslizando para cima ou para baixo.

Por padrão, nenhum elemento que pode ser composto é marcado como cabeçalho, então não é possível navegar por eles. Queremos que nossa tela de artigo forneça a navegação por cabeçalhos:

Duas gravações de tela com o TalkBack ativado e em que é usado o gesto de deslizar para baixo para navegar pelos títulos. A tela da esquerda mostra a mensagem "Nenhum cabeçalho a seguir". A tela da direita percorre os cabeçalhos e lê cada um deles em voz alta.

Adição de cabeçalhos. Antes (à esquerda) x depois (à direita).

Os cabeçalhos do artigo são definidos no PostContent.kt. Vamos abrir esse arquivo e rolar até o elemento Paragraph que pode ser composto:

@Composable
private fun Paragraph(paragraph: Paragraph) {
   // ...
   Box(modifier = Modifier.padding(bottom = trailingPadding)) {
       when (paragraph.type) {
           // ...
           ParagraphType.Header -> {
               Text(
                   modifier = Modifier.padding(4.dp),
                   text = annotatedString,
                   style = textStyle.merge(paragraphStyle)
               )
           }
           // ...
       }
   }
}

Aqui, o Header é definido como um elemento Text simples que pode ser composto. Podemos definir a propriedade semântica heading para indicar que esse elemento é um cabeçalho.

@Composable
private fun Paragraph(paragraph: Paragraph) {
   // ...
   Box(modifier = Modifier.padding(bottom = trailingPadding)) {
       when (paragraph.type) {
           // ...
           ParagraphType.Header -> {
               Text(
                   modifier = Modifier.padding(4.dp)
                     .semantics { heading() },
                   text = annotatedString,
                   style = textStyle.merge(paragraphStyle)
               )
           }
           // ...
       }
   }
}

8. Mesclagem personalizada

Como vimos nas etapas anteriores, serviços de acessibilidade como o TalkBack fazem a navegação na tela de um elemento a outro. Por padrão, cada elemento de baixo nível no Jetpack Compose que pode ser composto e define pelo menos uma propriedade semântica recebe foco. Por exemplo, um Text que pode ser composto define a propriedade semântica text e, portanto, recebe foco.

No entanto, ter muitos elementos focalizáveis na tela pode gerar confusão conforme o usuário navega por eles. Em vez disso, os elementos podem ser mesclados usando o modificador semantics com a propriedade mergeDescendants.

Vamos conferir a tela do nosso artigo. A maioria dos elementos tem o nível de foco certo. No entanto, os metadados do artigo agora são lidos em voz alta como vários itens separados. Isso pode ser melhorado com uma mesclagem resultando em uma única entidade focalizável:

Duas gravações de tela com o TalkBack ativado. A tela à esquerda mostra retângulos verdes separados do TalkBack para os campos "Autor" e "Metadados". A tela à direita mostra um único retângulo em torno dos dois campos e lê o conteúdo concatenado.

Mesclagem de elementos que podem ser compostos. Antes (à esquerda) x depois (à direita).

Vamos abrir o PostContent.kt e conferir o elemento PostMetadata que pode ser composto:

@Composable
private fun PostMetadata(metadata: Metadata) {
   // ...
   Row {
       Image(
           // ...
       )
       Spacer(Modifier.width(8.dp))
       Column {
           Text(
               // ...
           )

           CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
               Text(
                   // ..
               )
           }
       }
   }
}

Podemos instruir a linha de nível superior para mesclar os descendentes, o que levará ao comportamento desejado:

@Composable
private fun PostMetadata(metadata: Metadata) {
   // ...
   Row(Modifier.semantics(mergeDescendants = true) {}) {
       Image(
           // ...
       )
       Spacer(Modifier.width(8.dp))
       Column {
           Text(
               // ...
           )

           CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
               Text(
                   // ..
               )
           }
       }
   }
}

9. Chaves e caixas de seleção

Elementos alternáveis, como Switch e Checkbox, têm o estado de marcação lido em voz alta quando são selecionados pelo TalkBack. Sem contexto, pode ser difícil entender a que esses elementos alternáveis se referem. Podemos incluir contexto para eles elevando o estado alternável, de modo que um usuário possa alternar Switch ou Checkbox pressionando o próprio elemento que pode ser composto ou o marcador que o descreve.

Um exemplo pode ser visto na tela "Interesses". Para acessar, abra a gaveta de navegação na tela inicial. Na tela "Interesses", temos uma lista de temas em que um usuário pode se inscrever. Por padrão, as caixas de seleção nessa tela têm o foco separado dos marcadores delas, o que dificulta a compreensão do contexto. Preferimos que todo o elemento Row seja alternável:

Duas gravações de tela com o TalkBack ativado mostrando a tela de interesses com uma lista de temas selecionáveis. Na tela à esquerda, o TalkBack seleciona separadamente cada caixa. Na tela à direita, o TalkBack seleciona a linha inteira.

Uso de caixas de seleção. Antes (à esquerda) x depois (à direita).

Vamos abrir InterestsScreen.kt e analisar a implementação do elemento TopicItem que pode ser composto:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   Row(
       modifier = Modifier
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = { onToggle() },
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

Como você pode ver, o elemento Checkbox tem um callback onCheckedChange que processa a alternância do elemento. Podemos elevar esse callback para o nível do elemento Row completo:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   Row(
       modifier = Modifier
           .toggleable(
               value = selected,
               onValueChange = { _ -> onToggle() },
               role = Role.Checkbox
           )
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = null,
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

10. Descrições de estado

Na etapa anterior, elevamos o comportamento de alternância de um elemento Checkbox para o Row pai. Podemos melhorar ainda mais a acessibilidade adicionando uma descrição personalizada para o estado do elemento que pode ser composto.

Por padrão, o status do elemento Checkbox é lido como "Marcado" ou "Não marcado". Podemos substituir essa descrição por uma personalizada:

Duas gravações de tela com o TalkBack ativado com o usuário tocando em um tema na tela "Interesses". A tela à esquerda anuncia "Não marcado", enquanto a tela à direita anuncia "Não inscrito".

Adição de descrições de estado. Antes (à esquerda) x depois (à direita).

Podemos continuar com o elemento TopicItem que pode ser composto, adaptado na última etapa:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   Row(
       modifier = Modifier
           .toggleable(
               value = selected,
               onValueChange = { _ -> onToggle() },
               role = Role.Checkbox
           )
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = null,
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

Podemos adicionar as descrições de estado personalizadas usando a propriedade stateDescription dentro do modificador semantics:

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
   // ...
   val stateNotSubscribed = stringResource(R.string.state_not_subscribed)
   val stateSubscribed = stringResource(R.string.state_subscribed)
   Row(
       modifier = Modifier
           .semantics {
               stateDescription = if (selected) {
                   stateSubscribed
               } else {
                   stateNotSubscribed
               }
           }
           .toggleable(
               value = selected,
               onValueChange = { _ -> onToggle() },
               role = Role.Checkbox
           )
           .padding(horizontal = 16.dp, vertical = 8.dp)
   ) {
       // ...
       Checkbox(
           checked = selected,
           onCheckedChange = null,
           modifier = Modifier.align(Alignment.CenterVertically)
       )
   }
}

11. Parabéns!

Parabéns! Você concluiu este codelab e aprendeu mais sobre acessibilidade no Compose. Você aprendeu sobre áreas de toque, descrições de elementos visuais e descrições de estado. Além disso, adicionamos marcadores de clique, cabeçalhos e ações personalizadas. Agora você sabe como adicionar mesclagem personalizada e trabalhar com chaves e caixas de seleção. A aplicação desses aprendizados aos seus apps deixa a acessibilidade deles muito melhor.

Confira os outros codelabs no módulo do Compose e outros exemplos de código (link em inglês), incluindo o Jetnews.

Documentação

Para mais informações e orientações sobre esses temas, consulte a seguinte documentação: