Padrões da API

As APIs Material, Compose UI e Foundation implementam e oferecem muitas práticas acessíveis por padrão. Eles contêm semântica integrada que segue o papel e a função específicos. Isso significa que a maior parte do suporte à acessibilidade é fornecida com pouco ou nenhum trabalho extra.

Usar as APIs adequadas para a finalidade certa significa que os componentes geralmente vêm com comportamentos de acessibilidade predefinidos que abrangem casos de uso padrão. No entanto, sempre verifique se esses padrões atendem às suas necessidades de acessibilidade. Caso contrário, o Compose oferece maneiras de atender a requisitos mais específicos.

Entender a semântica e os padrões de acessibilidade padrão nas APIs do Compose ajuda você a usá-las com a acessibilidade em mente. Ela também ajuda a oferecer suporte à acessibilidade em mais componentes personalizados.

Tamanhos mínimos de á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. Ao dimensionar esses elementos, defina o tamanho mínimo como 48 dp para seguir corretamente as Diretrizes de acessibilidade do Material Design (link em inglês).

Os componentes do Material Design (como Checkbox, RadioButton, Switch, Slider e Surface) definem esse tamanho mínimo internamente, mas apenas quando o componente pode receber ações do usuário. Por exemplo, quando um Checkbox tem o parâmetro onCheckedChange definido como um valor não nulo, a caixa de seleção inclui padding com uma largura e uma altura de pelo menos 48 dp.

@Composable
private fun CheckableCheckbox() {
    Checkbox(checked = true, onCheckedChange = {})
}

Uma caixa de seleção com o padding padrão, com largura e altura de 48 dp.
Figura 1. Uma caixa de seleção com padding padrão.

Quando o parâmetro onCheckedChange é definido como nulo, o padding não é incluído, porque não é possível interagir com o componente de forma direta.

@Composable
private fun NonClickableCheckbox() {
    Checkbox(checked = true, onCheckedChange = null)
}

Uma caixa de seleção sem padding.
Figura 2. Uma caixa de seleção sem padding.

Ao implementar controles de seleção, como Switch, RadioButton ou Checkbox, você normalmente aumenta o comportamento clicável para um contêiner pai definindo o callback de clique no elemento combinável como null e adicionando um modificador toggleable ou selectable ao elemento pai.

@Composable
private fun CheckableRow() {
    MaterialTheme {
        var checked by remember { mutableStateOf(false) }
        Row(
            Modifier
                .toggleable(
                    value = checked,
                    role = Role.Checkbox,
                    onValueChange = { checked = !checked }
                )
                .padding(16.dp)
                .fillMaxWidth()
        ) {
            Text("Option", Modifier.weight(1f))
            Checkbox(checked = checked, onCheckedChange = null)
        }
    }
}

Uma caixa de seleção ao lado do texto "Opção" que está sendo marcada e desmarcada.
Figura 3. Uma caixa de seleção com comportamento clicável.

Quando o tamanho de um elemento clicável é menor que o tamanho mínimo da área de toque, o Compose aumenta o tamanho dessa área. Isso é feito expandindo o tamanho da área de toque para fora dos limites do elemento combinável.

O exemplo a seguir contém um elemento Box clicável muito pequeno. A área de toque é expandida automaticamente para além dos limites do elemento Box. Então, tocar ao lado de Box ainda acionará o evento de clique.

@Composable
private fun SmallBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .size(1.dp)
        )
    }
}

Uma caixa clicável muito pequena que é expandida para uma área de toque maior ao tocar ao lado dela.
Figura 4. Uma caixa clicável muito pequena que é expandida para uma área de toque maior.

Para evitar possíveis sobreposições entre áreas de toque de diferentes elementos combináveis, sempre use um tamanho mínimo grande o suficiente. No exemplo, isso significa usar o modificador sizeIn para definir o tamanho mínimo da caixa interna:

@Composable
private fun LargeBox() {
    var clicked by remember { mutableStateOf(false) }
    Box(
        Modifier
            .size(100.dp)
            .background(if (clicked) Color.DarkGray else Color.LightGray)
    ) {
        Box(
            Modifier
                .align(Alignment.Center)
                .clickable { clicked = !clicked }
                .background(Color.Black)
                .sizeIn(minWidth = 48.dp, minHeight = 48.dp)
        )
    }
}

A caixa muito pequena do exemplo anterior é aumentada para criar uma área de toque maior.
Figura 5. Uma área de toque maior.

Se o elemento precisar manter um tamanho visível menor, use o modificador minimumInteractiveComponentSize do Material 3. Ele reserva pelo menos 48 dp de espaço de layout em cada dimensão e centraliza o elemento nesse espaço, para que ele mantenha o tamanho desenhado e os elementos ao redor se movam para abrir espaço. O modificador não expande a área de toque, mas deixa espaço no layout para a expansão descrita anteriormente. Os componentes do Material Design, como Checkbox, usam esse modificador internamente. Coloque-o antes de qualquer modificador de tamanho, porque um modificador de tamanho antes dele limita o espaço que ele pode reservar.

@Composable
private fun MinimumInteractiveBox() {
    var clicked by remember { mutableStateOf(false) }
    Row(verticalAlignment = Alignment.CenterVertically) {
        Box(
            Modifier
                .clickable { clicked = !clicked }
                .minimumInteractiveComponentSize()
                .size(24.dp)
                .background(if (clicked) Color.DarkGray else Color.Black)
        )
        Text("Option")
    }
}

Elementos gráficos

Quando você define um elemento combinável Image ou Icon, não há como o framework do Android entender de forma automática o que o app está mostrando. É necessário fornecer uma descrição textual do elemento gráfico.

Imagine uma tela em que o usuário pode compartilhar a página atual com amigos. Essa tela contém um ícone de compartilhamento clicável:

Uma faixa de quatro ícones clicáveis, com o ícone "compartilhar" destacado.
Figura 6. Uma linha de ícones clicáveis com o ícone "Compartilhar" selecionado.

Tendo somente o ícone como base, o framework do Android não consegue encontrar uma forma de descrevê-lo para um usuário com deficiência visual. O framework do Android precisa de uma descrição textual complementar do ícone.

O parâmetro contentDescription descreve um elemento gráfico. Use uma string localizada, porque ela fica visível para o usuário.

@Composable
private fun ShareButton(onClick: () -> Unit) {
    IconButton(onClick = onClick) {
        Icon(
            imageVector = Icons.Filled.Share,
            contentDescription = stringResource(R.string.label_share)
        )
    }
}

Alguns elementos gráficos são apenas decorativos, e você pode optar por não os descrever ao usuário. Ao definir o parâmetro contentDescription como null, você indica ao framework do Android que esse elemento não contém ações ou estados associados.

@Composable
private fun PostImage(post: Post, modifier: Modifier = Modifier) {
    val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1)

    Image(
        painter = image,
        // Specify that this image has no semantic meaning
        contentDescription = null,
        modifier = modifier
            .size(40.dp, 40.dp)
            .clip(MaterialTheme.shapes.small)
    )
}

O contentDescription é destinado principalmente a elementos gráficos, como imagens. Os componentes do Material Design, como Button ou Text, e os comportamentos acionáveis, como clickable ou toggleable, vêm com outras semânticas predefinidas que descrevem o comportamento intrínseco deles e podem ser mudadas por outras APIs do Compose.

Elementos interativos

As APIs Material e Foundation Compose criam elementos da interface com que os usuários podem interagir usando as APIs modificadoras clickable e toggleable. Como os componentes interativos podem consistir em vários elementos, clickable e toggleable mesclam a semântica dos filhos por padrão, para que o componente seja tratado como uma entidade lógica.

Por exemplo, um Button do Material Design pode consistir em um ícone filho e um texto. Em vez de tratar os filhos como indivíduos, um Button do Material Design mescla a semântica dos filhos por padrão para que os serviços de acessibilidade possam agrupá-los de acordo:

Botões com semântica de filhos não mesclados e mesclados.
Figura 7. Botões com semântica de filhos não mesclados e mesclados.

Da mesma forma, o uso do modificador clickable também faz com que um elemento combinável mescle a semântica dos descendentes em uma única entidade, que é enviada aos serviços de acessibilidade com uma representação de ação correspondente:

Row(
    // Uses `mergeDescendants = true` under the hood
    modifier = Modifier.clickable { openArticle() }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open",
    )
    Text("Accessibility in Compose")
}

Você também pode definir um onClickLabel específico no elemento clicável pai para fornecer mais informações aos serviços de acessibilidade e oferecer uma representação mais refinada da ação:

Row(
    modifier = Modifier
        .clickable(onClickLabel = "Open this article") {
            openArticle()
        }
) {
    Icon(
        painter = painterResource(R.drawable.ic_logo),
        contentDescription = "Open"
    )
    Text("Accessibility in Compose")
}

Usando o TalkBack como exemplo, esse modificador clickable e o rótulo de clique permitem que o TalkBack forneça uma dica de ação como "Toque duas vezes para abrir este artigo", em vez do feedback padrão mais genérico "Toque duas vezes para ativar".

Esse feedback muda de acordo com o tipo de ação. Um clique longo fornece uma dica do TalkBack de "Toque duas vezes e mantenha pressionado para", seguida por um rótulo:

Row(
    modifier = Modifier
        .combinedClickable(
            onLongClickLabel = "Bookmark this article",
            onLongClick = { addToBookmarks() },
            onClickLabel = "Open this article",
            onClick = { openArticle() },
        )
) {}

Em alguns casos, talvez você não tenha acesso direto ao modificador clickable (por exemplo, quando ele é definido em uma camada aninhada inferior),mas ainda queira mudar o rótulo de anúncio padrão. Para fazer isso, divida a configuração do clickable da modificação do aviso usando o modificador semantics e definindo o identificador de clique para modificar a representação da ação:

@Composable
private fun ArticleList(openArticle: () -> Unit) {
    NestedArticleListItem(
        // Clickable is set separately, in a nested layer:
        onClickAction = openArticle,
        // Semantics are set here:
        modifier = Modifier.semantics {
            onClick(
                label = "Open this article",
                action = {
                    // Not needed here: openArticle()
                    true
                }
            )
        }
    )
}

Não é necessário transmitir a ação de clique duas vezes. As APIs do Compose, como clickable ou Button, cuidam disso para você. A lógica de mesclagem verifica se o rótulo e a ação do modificador mais externo são usados para as informações presentes. No exemplo anterior, o NestedArticleListItem transmite automaticamente a ação de clique openArticle() para a semântica clickable. Você pode deixar a ação de clique nula na segunda ação do modificador de semântica. No entanto, o rótulo de clique é retirado do segundo modificador de semântica onClick(label = "Open this document") porque não estava presente no primeiro.

Você pode se deparar com cenários em que espera que a semântica de filhos seja mesclada com a de um pai, mas isso não acontece. Consulte Mesclar e limpar para mais informações.

Componentes personalizados

Ao criar um componente personalizado, analise a implementação de um componente semelhante na biblioteca do Material Design ou em outras bibliotecas do Compose. Em seguida, imite ou modifique o comportamento de acessibilidade conforme apropriado. Por exemplo, se você substituir o Checkbox do Material Design pela sua própria implementação, analisar a implementação atual do Checkbox vai lembrar você de adicionar o modificador triStateToggleable, que processa as propriedades de acessibilidade do componente. Além disso, use bastante os modificadores do Foundation, já que eles incluem considerações de acessibilidade integradas e práticas do Compose abordadas nesta seção.

Você também pode encontrar um exemplo de componente de alternância personalizado na seção Limpar e definir semântica, além de informações mais detalhadas sobre como oferecer suporte à acessibilidade em componentes personalizados nas diretrizes da API.