Accesibilidad en el desarrollo de Android

Tiempo de lectura: 18 min

En la era digital actual, la accesibilidad en el desarrollo de aplicaciones móviles no es solo una característica, sino una necesidad. Como desarrolladores, nuestro objetivo es crear aplicaciones que no solo sean funcionales, sino también inclusivas, garantizando que todo el mundo pueda interactuar con nuestros productos sin problemas.

En esta publicación, vamos a explorar estrategias clave y mejores prácticas para hacer que nuestras aplicaciones Android sean más accesibles y fáciles de usar.

1. Introducción

¿Qué es la accesibilidad en las aplicaciones móviles?

La accesibilidad de una aplicación es su capacidad para que todas las personas puedan utilizar sus funciones y acceder a su contenido, independientemente de sus discapacidades técnicas, físicas o cognitivas. En otras palabras, busca que sea accesible para todas las personas, incluidos los usuarios con algún tipo de discapacidad.

Es importante no confundir accesibilidad con usabilidad. Mientras que la usabilidad se refiere a las condiciones de la aplicación que la hacen atractiva y fácil de usar para el usuario, la accesibilidad se refiere a la capacidad de la aplicación para ser utilizada por cualquier usuario.

Para que tu aplicación sea accesible, no solo tendrás que cumplir con ciertas normas relativas a los colores utilizados, el contraste, el tamaño y la escalabilidad de las fuentes de texto, etc. También tendrás que implementar configuraciones para que los asistentes de voz como TalkBack (Android) o VoiceOver (iOS) puedan proporcionar la información necesaria a los usuarios.

En este artículo nos centraremos en Android y en cómo trabajar con las API de accesibilidad y, en concreto, en cómo funciona Talkback.

¿Por qué es importante que tu aplicación sea accesible?

En los últimos años, y cada vez más, el mundo digital se ha convertido en parte de nuestra vida cotidiana y cada vez hay más servicios digitales. Servicios que utilizan todos y que, por lo tanto, deben ser accesibles para todos.

Por poner un ejemplo, al igual que es necesario que haya una entrada para personas con discapacidad en una escuela, un edificio residencial, etc., necesitamos que las aplicaciones y los servicios digitales sean accesibles.

Otro punto importante puede ser económico, ya que así conseguirás que tu aplicación sea utilizada por el mayor número posible de usuarios. En España, se estima que alrededor del 2 % de la población tiene algún grado de discapacidad visual.

Por último, pero no por ello menos importante, el organismo europeo de accesibilidad ha establecido una normativa por la que, a partir del 28 de junio de 2025, todos los productos y servicios digitales deberán ser accesibles con un requisito mínimo de WCAG 2.1: AA+, de lo contrario se aplicarán sanciones de hasta 1 millón de euros por producto.

Accesibilidad hoy en día

Hoy en día, la realidad es que casi ninguna aplicación es 100 % accesible. Según un estudio, el 47,4 % de las 50 aplicaciones más utilizadas no son accesibles. El contraste mínimo, el foco visible y el tamaño del texto son los problemas más comunes.

En cuanto a los sistemas operativos, se puede decir que iOS es más accesible, pero su elevado precio hace que Android sea el sistema más utilizado.

2. Las mejores herramientas de accesibilidad en Android

Como mencionamos al principio, en este artículo nos centraremos en cómo trabajar con las API de accesibilidad de Android y cómo funciona la herramienta TalkBack.

Sin embargo, TalkBack no es la única herramienta de accesibilidad con la que se puede hacer accesible un dispositivo Android. También existen herramientas como BrailleBack y Switch Access.

BrailleBack comenzó como una aplicación que conectaba una pantalla braille al dispositivo móvil. Hoy en día, si tienes una pantalla braille, no necesitas la aplicación BrailleBack. Puedes conectarte a través de Bluetooth y, junto con TalkBack, combinar la lectura de pantalla braille y la lectura de pantalla por voz.

Switch Access está pensada para personas con movilidad reducida. También se conoce como accesibilidad por interruptor y permite utilizar el dispositivo móvil con uno o varios botones externos en lugar de utilizar la pantalla táctil, e incluso mediante gestos que permiten realizar una acción predeterminada.

Estas herramientas se conectan a nuestra aplicación móvil a través del Android Framework, lo que nos permite implementar su uso de la forma más sencilla posible mediante unos pocos comandos.

3. Directrices de accesibilidad para aplicaciones móviles

Tamaño de pantalla y fuente

Aunque los criterios de conformidad con las WCAG 2.1 no especifican un tamaño mínimo concreto para las fuentes de los textos, Google recomienda que los textos de las aplicaciones móviles Android tengan un mínimo de 12 sp (píxeles independientes de la escala), mientras que Apple recomienda el uso de un mínimo de 11 pt (puntos tipográficos).

Sin embargo, las WCAG 2.1 sí exigen que haya suficiente contraste para que el texto sea legible en diversas condiciones y, sobre todo, que el texto sea escalable.

Todos los usuarios deben poder modificar el tamaño de la fuente desde la configuración de su dispositivo, pudiendo establecer una escala de texto de hasta el 200 %. Por lo tanto, es obligatorio que el tamaño de los textos se defina en sp.

Al aplicar los sp a los textos al inicio de la escala de texto, el Android Framework se encarga de gestionarlo de forma invisible, ya que multiplica los sp establecidos en cada una de las fuentes por el valor de la escala de fuente (valor que oscila entre 0,6 y 2,0, dependiendo de lo que seleccione el usuario). Por lo tanto, el desarrollador no debería preocuparse por ello más allá de establecer el tamaño de estos en SP… ¿O sí?

La mayoría de las aplicaciones móviles fallan en este punto porque los desarrolladores no preparan sus aplicaciones para este escalado de fuentes que realiza Android. Por lo general, el texto sobresale de la pantalla o de los bloques definidos, se corta y se pierde texto dentro de los bloques, o cubre otras secciones o botones importantes, etc.

Para este punto damos varios consejos:

  • No introduzcas textos en bloques con un tamaño fijo: estos bloques deben ajustarse siempre al tamaño del texto, de modo que si este crece, lo haga al mismo tiempo que el bloque.
  • Gestiona los desbordamientos: añade desplazamiento al texto y a los bloques que lo rodean, tanto vertical como horizontalmente, para evitar perder información.
  • Recomendamos el uso de archivos dimens.xml donde se establecen los tamaños de todas las fuentes para que, dependiendo del tamaño de la pantalla, los textos tengan un tamaño de fuente u otro.
  • Prueba la aplicación en dispositivos de diferentes tamaños e intenta ver el texto con el tamaño de zoom mínimo y máximo y comprueba que todo funciona correctamente.

Esquemas de color

En una aplicación Android podemos definir un esquema de color. Incluso podemos definir un esquema para el modo oscuro y otro para el modo claro. Y podemos aplicar ambos al tema de la aplicación.

Las ventajas de definir nuestra combinación de colores es que cada elemento de nuestra aplicación (textos, colores de fondo, botones, etc.) tendrá el color que hayamos definido en nuestra combinación.

En cada combinación podemos definir los colores a partir de etiquetas ya establecidas, con las que podemos definir el color principal de la aplicación, el fondo, etc.

Como en el siguiente ejemplo:

Además, los colores elegidos también son importantes para crear una aplicación accesible. Debemos tener en cuenta las posibles limitaciones visuales de los usuarios y elegir colores con un alto nivel de contraste y brillo que eviten problemas de daltonismo.

Tamaño mínimo de los elementos pulsables

Es muy recomendable que los elementos pulsables tengan un tamaño mínimo de 48 dp x 48 dp (píxeles independientes de la densidad), ya sea porque el elemento ocupa ese tamaño o porque se ha añadido un relleno a su alrededor para alcanzar ese tamaño, ya que, de lo contrario, a las personas con algún tipo de discapacidad motora les resulta extremadamente difícil manejar estos botones.

Para ello, también se recomienda utilizar, una clase que permite definir un área rectangular alrededor de una vista que también responde a los eventos táctiles como si estuvieran en la vista original.

4. Talk back

¿Cómo funciona TalkBack?

Cuando activas TalkBack en tu dispositivo, el sistema lee en voz alta todos los elementos que aparecen en la pantalla. Uno de los elementos tendrá el foco de TalkBack y, a medida que el usuario cambie el foco entre los diferentes elementos, el sistema leerá en voz alta la información de cada uno de ellos y te permitirá interactuar con ellos mediante gestos, como dar dos toques para activar un botón.

Este es un vídeo oficial en el que Google explica cómo funciona TalkBack:

Gestos principales

Cuando activas TalkBack, hay diferentes gestos que te permiten moverte por la pantalla para recorrer todos los elementos.

Información del elemento

Una vez que hayamos aprendido los gestos que nos permiten movernos por la pantalla y navegar cuando tenemos TalkBack activado, es hora de saber qué información le proporcionará TalkBack al usuario. Esta información proporcionará una descripción del elemento para saber qué elemento es y qué puede hacer el usuario con él.

Estas son las propiedades semánticas del elemento, como la descripción del contenido y las etiquetas de clic.

Cuando TalkBack selecciona un elemento, captura una descripción del mismo accediendo a la descripción del contenido y a la etiqueta de clic mencionadas anteriormente. Estas propiedades semánticas tienen un valor predeterminado, que será leído por TalkBack.

Esta información es configurable y será lo que veremos en esta publicación.

5. Marco de trabajo de Android

En Jetpack Compose, todas las funciones de accesibilidad se gestionan principalmente mediante el uso de semántica.

Junto a la composición, hay un árbol paralelo, denominado árbol semántico. Este árbol describe la interfaz de usuario de una forma alternativa que pueden entender los servicios de accesibilidad y el marco de trabajo de pruebas.

Los servicios de accesibilidad utilizan este árbol para describir la aplicación a los usuarios. El marco de pruebas lo utiliza para realizar afirmaciones sobre la aplicación.

El árbol semántico no contiene la información necesaria para dibujar los elementos componibles, pero sí contiene información sobre el significado semántico de dichos elementos.

Para modificar este árbol semántico, Android nos proporciona dos funciones de extensión de Kotlin: semantics y clearAndSetSemantics.

Ambas tienen la misma funcionalidad, nos permiten modificar los valores semánticos que queramos, con la única diferencia de que clearAndSetSemantics «rompe» la jerarquía que tiene ese elemento debajo.

Por ejemplo, si tenemos este componente:

Box(
Modifier.clearAndSetSemantics {
contentDescription = “Test”
}
){
Column {
Text()
Text()
Box {
...
}
}
Row {
Button() {
...
}
}
}

En el árbol semántico solo habrá un cuadro con la descripción «Prueba», por lo que Talkback solo detectará «Prueba» e ignorará los bloques que hay dentro (textos, botones, etc.).

Importante: como hemos mencionado, las herramientas de prueba también dependen de este árbol semántico, por lo que hay que tener especial cuidado con el uso de clearAndSetSemantics, ya que se pueden dejar elementos totalmente inaccesibles para las pruebas de interfaz de usuario.

Gestionar la voz en off

Las etiquetas predeterminadas proporcionan información muy general y básica. Al no proporcionar detalles específicos, es posible que el usuario no tenga claro qué es y cómo utilizar su aplicación. Por eso debemos prestar especial atención a los modificadores que nos proporciona el Framework para proporcionar información a nuestros componentes. La etiqueta más importante que encontraremos en este Framework es la mencionada en el punto anterior: contentDescription. Este modificador nos permite sustituir el discurso predeterminado de Talkback.

For example, if we have a text like this:

Text(
"This is a test",
modifier = Modifier.semantics { contentDescription = "Hello World" }
)

Talkback leerá «Hola mundo» en lugar de «Esto es una prueba».

Hay otros elementos, como los botones, que tienen texto predeterminado. Por ejemplo, si encontramos un botón con la prueba «Empezar», TalkBack leerá por defecto: «Empezar, botón. Toque dos veces para activar».

Según la lógica de Android, estos textos se construyen de la siguiente manera: «(descripción del contenido), botón, doble clic para (etiqueta del botón onClick)».

He aquí un ejemplo de cómo funcionaría: si desea que su botón diga «Hola mundo, botón. Haga doble clic para decir hola», el botón tendría que modificarse de la siguiente manera:

Button(
edit = edit.semantics {
contentDescription = “Hello world”
onClick(“say hello”) { false }
}
)

Gestionar acciones

Podemos modificar el funcionamiento del botón cuando hacemos clic en él. La función onClick sigue estas reglas:

onClick ([button label]) { 
[additional actions 1]
[additional actions 2]
[additional actions ...]
[additional actions N]
[true/false]
}

En su interior, puedes añadir todas las acciones que desees al hacer clic en el elemento. Por último, debes devolver un valor booleano que indique si deseas que la acción del clic se propague a los componentes principales.

A continuación se muestran varios ejemplos:

Caso 1:

IconButton(
modifier = Modifier.semantics {
contentDescription = stringMuteButton
onClick(stringMuteMicro) { testAction(); true }
},
onClick = { viewModel.clickMute(!isMute) }
)

Cuando devuelve verdadero, solo ejecutará el método testAction().

Caso 2:

IconButton(
modifier = Modifier.semantics {
contentDescription = stringMuteButton
onClick(stringMuteMicro) { testAction(); false }
},
onClick = { viewModel.clickMute(!isMute) }
)

Cuando devuelve falso, ejecutará el método testAction() y, a continuación, viewModel.clickMute(!isMute).

Agrupación de elementos

Una de las principales acciones que nos permite realizar el marco es agrupar varios elementos para que Talkback los detecte como uno solo.

Esto se consigue gracias a la propiedad mergeDescendants. Esta característica se incluye en los composables que agrupan otros composables en su interior, como Row, Column, Box, etc.

Ejemplo de uso:

Tenemos una pantalla que tiene una columna con varios textos:

Column (
Modifier.semantics(mergeDescendants = true) {}
) {
Text(“Welcome to Test App”)
Text(“This is a test application”)
}

Cuando Talkback llega a esa parte de la pantalla, selecciona directamente la columna que rodea estos elementos y los trata como un único elemento, y dice lo siguiente: «Bienvenido a la aplicación de prueba. Esta es una aplicación de prueba».

Tiene mucho potencial cuando se combina con la propiedad onClick, ya que permite crear grupos en los que se puede hacer clic para facilitar la usabilidad del usuario.

Por ejemplo, tenemos una fila con el texto «Al continuar, aceptas los términos y condiciones» y un botón «Continuar». Con mergeDescendants podemos hacer que toda la fila sea clicable con un solo clic.

Row (
Modifier.semantics(mergeDescendants = true) {
onClick() { continueAction(), true; }
}
){
Text(“By continuing, you agree to the terms and conditions.”)
OutlinedButton ({ onClick = {continueAction(); })
Text(“Continue”) }
}

Elementos organizativos

Por defecto, Talkback lee de arriba a abajo y de izquierda a derecha, en orden secuencial y según la profundidad del eje Z (zIndex), siendo las capas superiores las primeras en seleccionarse.

Pero hay casos en los que nos puede interesar modificar este orden. Para cambiarlo debemos utilizar esta semántica: isTraversalGroup, que es un booleano que si es verdadero indica que queremos que se priorice el orden que indicamos. Entonces, tenemos que utilizar traversalIndex = (número flotante), siendo el número más bajo al que se le dará más prioridad. Ejemplo:

Column (
Modifier.semantics { isTraversalGroup = true }
) {
Text(
“Lorem”,
Modifier.semantics { traversalIndex = 3f }
)
Text(
“Ipsum”,
Modifier.semantics { traversalIndex = 1f }
)
Text(
“Dolor”,
Modifier.semantics { traversalIndex = 2f }
)
}

El Talkback leerá: «Ipsum Dolor Lorem».

Advertencia: esto puede entrar en conflicto con el eje Z (zIndex), por lo que se debe tener cuidado al colocar todo en la misma capa o manejar las capas según sea necesario.

Detectar si Talkback está activo

A veces necesitamos saber si Talkback está activo para poder gestionar las operaciones dentro de nuestra aplicación.

Para ello, podemos recopilar el siguiente valor booleano:

val isTalkbackActive = getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN)

Voces en off personalizadas

Otra de las funcionalidades que nos brinda el Android Framework es la de dar voz a textos personalizados cuando se ejecutan determinadas acciones en la aplicación. Esto es gracias a la función LocalView.current llamada view.announceForAccessibility({text}). Así, podemos dar voz a una frase personalizada al entrar en una pantalla:

LaunchedEffect(Unit) {
view.announceForAccessibility(
“You are in the contacts screen, here you can add or remove numbers from your address book.”
)
}

LiveRegions

Esta función propone una solución muy sencilla a uno de los mayores obstáculos a los que se enfrentan los usuarios con algún tipo de discapacidad visual.

Imagina que tienes una aplicación en la que, al hacer clic en un botón, aumenta el valor de un contador (que es visible en la pantalla).

Si TalkBack tiene el foco en el botón, el usuario no sabrá si el contador ha aumentado ni cuál es el valor actual.

Por lo tanto, el usuario tendría que desplazarse hasta el contador (buscándolo por toda la pantalla) y ver cuál es su valor, y luego, si desea aumentarlo de nuevo, volver al botón, y así sucesivamente.

Afortunadamente, Android cuenta con regiones activas, que nos permiten anunciar al usuario cuándo ha cambiado un elemento.

Según el ejemplo anterior, si el usuario pulsa el botón de aumentar el contador, este se modificará y TalkBack anunciará su valor. De este modo, el usuario recibirá información continua sin tener que desplazarse por la pantalla.

Hay dos tipos de LiveRegions:

  1. Polite: TalkBack esperará a que termines de hablar para leer el cambio.
  2. Asertivo: Talkback interrumpirá el discurso actual (si lo hay) para leer el cambio. Se recomienda utilizar este punto cuando sea estrictamente necesario, ya que interrumpir el discurso actual puede resultar confuso para el usuario.

Para incluir esta operación en nuestro código, solo hay que poner la siguiente semántica y Android y Talkback se encargarán automáticamente del resto:

Polite: 
Modifier.semantics { liveRegion = LiveRegionMode.Polite }

Assertive:
Modifier.semantics { liveRegion = LiveRegionMode.Assertive }

Funciones

De forma predeterminada, Talkback define ciertos comportamientos para ayudar al usuario en función del tipo de elemento. Por ejemplo, si tienes un botón con el texto «Inicio», TalkBack dirá: «Inicio, botón».

Talkback crea funciones basadas en la clase Java a la que pertenece cada elemento.

Por lo tanto, surgen problemas cuando creamos elementos personalizados. Sin embargo, gracias a las funciones, podemos definir comportamientos predeterminados para nuestras vistas personalizadas.

Por ejemplo:

@Composable
fun CustomButton() {
Box(
modifier = Modifier.semantics { role = Role.Button }
)
}

Podemos encontrar definidos estos diferentes roles:

  • Botón
  • Casilla de verificación
  • Lista desplegable
  • Imagen
  • Botón de radio
  • Interruptor
  • Pestaña

En esta publicación hemos explicado cómo crear aplicaciones móviles accesibles para Android. En futuras publicaciones, hablaremos sobre herramientas de prueba y accesibilidad para comprobar el grado de accesibilidad de tu aplicación, como Accessibility Scanner.

Bibliografía

WCAG: https://www.w3.org/TR/wcag2ict/

Semántica de Android: https://developer.android.com/develop/ui/compose/accessibility/semantics?hl=es-419

LiveRegions: https://mobilea11y.com/blog/android-live-regions/

Roles: https://bryanherbst.com/2021/02/19/compose-role-semantics/

Compártelo en tus redes sociales


Medios de comunicación

Contacta con nuestro departamento de comunicación o solicita material adicional.