Por que acessibilidade em Kotlin é um superpoder, não uma formalidade
\n\nImagine: você criou um aplicativo incrível em Kotlin. Material Design bonito, animações suaves, streams reativos. Você se orgulha do código. Mas descobre que uma pessoa com baixa visão não consegue apertar o botão "Comprar" porque o TalkBack lê como "botão não identificado". Ou um usuário com tremor nas mãos erra o minúsculo interruptor.
\n\nAcessibilidade não é burocracia chata. É fazer seu código em Kotlin funcionar para todos. Segundo a OMS, mais de um bilhão de pessoas têm alguma deficiência. Ignorá-las é perder público e, francamente, ser um desenvolvedor ruim.
\n\nNeste artigo, vamos ver como escrever código acessível em Kotlin sem dor de cabeça. Sem toneladas de código repetitivo. Com exemplos práticos que você pode copiar e colar agora mesmo.
\n\nPrincípios básicos: ContentDescription é seu melhor amigo
\n\nA falha mais comum em acessibilidade é a falta de descrição para elementos da interface. O TalkBack (leitor de tela do Android) não lê imagens ou ícones. Ele precisa de texto.
\n\nEm Kotlin, isso é feito com o atributo contentDescription. Mas não apenas "Botão", e sim algo com significado.
Exemplo ruim (não faça isso)
\nimageView.contentDescription = "Botão"\n\nExemplo bom
\nbinding.fabAddTask.contentDescription = "Adicionar nova tarefa"\n\nSe o ícone for decorativo (apenas enfeite) — defina contentDescription = null para o TalkBack ignorá-lo.
// Ícone decorativo — não distraia o usuário\nbinding.decorativeIcon.contentDescription = null\n\nDica: Use recursos de string (strings.xml) para suportar localização. Não coloque texto fixo em português nos arquivos Kotlin.
Marcação semântica: dizendo ao sistema o que é importante
\n\nAs View padrão (Button, TextView) funcionam bem, mas componentes personalizados frequentemente quebram a acessibilidade. Em Kotlin, podemos definir a semântica explicitamente.
Adicionando um papel ao elemento
\nbinding.customView.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES\nbinding.customView.accessibilityDelegate = object : View.AccessibilityDelegate() {\n override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {\n super.onInitializeAccessibilityNodeInfo(host, info)\n info.text = "Valor atual: ${viewModel.value}"\n info.contentDescription = "Slider de volume"\n }\n}\n\nEsse código diz ao sistema: "Ei, isso não é um quadrado, é um slider!". O TalkBack entenderá que o elemento é interativo e poderá interagir corretamente.
\n\nAgrupando elementos
\nSe você tem um card de produto com imagem, preço e nome — agrupe-os em um único foco. Caso contrário, o usuário ouvirá três elementos separadamente.
\n\nbinding.cardProduct.apply {\n isFocusable = true\n contentDescription = "Produto: ${product.name}, preço: ${product.price} reais"\n}\n\nAgora o TalkBack lê todas as informações em um único toque.
\n\nNavegação por ação: teclado e D-pad
\n\nNem todos os usuários tocam na tela. Alguns usam teclado (via OTG) ou D-pad (controle remoto). Seu aplicativo deve ser navegável sem toques.
\n\nEm Kotlin, isso é resolvido com focusable e a ordem de foco (nextFocusDown, nextFocusForward).
binding.buttonNext.nextFocusForwardId = R.id.buttonConfirm\nbinding.buttonConfirm.nextFocusDownId = R.id.buttonCancel\n\nVerifique se todos os elementos interativos recebem foco. Um erro comum é TextView com ClickableSpan (link no texto). Por padrão, ele não recebe foco. Corrigimos:
binding.textWithLink.movementMethod = LinkMovement