¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
¿Buscas nuestro logo?
Aquí te dejamos una copia, pero si necesitas más opciones o quieres conocer más, visita nuestra área de marca.
dev
Álvaro de las Peñas Hace 18 minutos Cargando comentarios…
Continuamos con nuestro ciclo de posts en los que te ayudamos a hacer tus apps accesibles. En anteriores capítulos de esta serie ya te pusimos en situación de todo lo que se viene y te dimos algunos tips para empezar a migrar tu app. El plazo para tener tus apps accesibles por ley está cada vez más próximo a expirar.
Como hemos comentado en alguna ocasión, la accesibilidad es una carrera de fondo y hacer tu app accesible para todo el mundo lleva tiempo, paciencia y conocimiento. Pero que no cunda el pánico, que te vamos a dar algunos consejos muy top que te van a ayudar en esta larga tarea.
Esta guía la vamos a basar en los estándares actuales de Android a fecha de abril de 2025, realizando los cambios sobre Kotlin y Compose. Existen modificaciones compatibles también con Java y XML en caso de que tu app tenga código legacy y tardes en migrarlo.
Ahora vamos, al lío, a remangarse la camisa, a codificar, a trastear, ¡que ya sabes que nos encanta! ¿Apps para todo el mundo? Por supuesto que sí. ¡Vamos al turrón!
El modificador contentDescription proporciona una ayuda para lectores de pantalla como Google Talkback, permitiéndonos introducir un parámetro de tipo String describiendo el elemento Composable que estamos pintando en pantalla. Es muy útil para imágenes, introduciendo por ejemplo la descripción de la imagen que estamos pintando en pantalla, por ejemplo “playa tropical desierta en un día soleado”.
Cuando el foco se posicione sobre el elemento en cuestión, leerá el atributo contentDescription, enunciándolo y permitiendo al usuario conocer el contenido del elemento en caso de contar con alguna discapacidad visual. Los contentDescription han de ser sencillos, directos y breves, para evitar recargar al lector de pantalla con enunciados interminables.
Icon(
imageVector = Icons.Default.Close,
contentDescription = “Botón cerrar”,
tint = contentColor
)
Importante: no es necesario describir cada elemento, simplemente los significativos, como imágenes o iconos. Si una imagen es solo decorativa, asígnalo a null para evitar ruido.
Modifier
.clickable(onClickLabel = “Ver detalle del elemento”)
Dentro del modificador clickable disponemos de un onClickLabel, que nos permite describir la acción que realizamos al pulsar sobre dicho elemento agregando significado semántico a la acción, como por ejemplo “Ver detalle del elemento” o “Agregar a favoritos”..
No todos los elementos compostables contienen este modificador, en caso contrario puedes acceder mediante el modificador “Semantics” al click y ahí podrás acceder a la etiqueta:
Column (
Modifier.semantics {
onClick(label =”agregar a favoritos”, action = addToFavorites)
}
) { ...
Anidar elementos en Compose (también en XML) es la mar de recurrente. Por ello, revisa bien el foco de navegación por gestos, algunos usuarios pueden utilizar interruptores adaptativos, otros pueden hacer uso de Talkback, y esto es importante porque es probable que la interacción con tu interfaz no funcione como esperas, que el foco se pierda ayudándose en dichos elementos, que repita semánticas al enunciar lo que ocurre en pantalla, etc.
El modifier clearAndSetSemantics te permite borrar los modificadores internos a ese elemento para que Talkback lea únicamente lo de este elemento en cuestión. Tenlo en cuenta si el lector de pantalla está repitiendo información dentro del mismo Composable.
Column(
modifier = modifier
.minimumInteractiveComponentSize()
.clickable(
onClick = onClickMovie
)
.padding(4.dp)
.clearAndSetSemantics {
contentDescription = element.title
}
) {
Card { ...
Y recuerda que no todo tiene por qué ser enunciado, lo importante sí, pero lo que no aporte información procura obviarlo.
Dentro del modificador “semantics” dispones del modificador “role”, que establece qué tipo de elemento estamos manejando. El role se encarga de decirle al lector de pantalla qué rol tiene el elemento que tiene el foco en pantalla, como un switch, un checkbox, un button, etc. Esto es útil para elementos personalizados como, por ejemplo, si tenemos un elemento custom que se comporta como un switch, podemos definir su comportamiento de cara al lector de pantalla mediante “role = Role.Switch”.
Vamos a verlo con un ejemplo:
Card(onClick = { onOptionToggle() },
modifier = Modifier
.semantics(mergeDescendants = true) {
role = Role.Switch
contentDescription = optionEnabledText
}
.fillMaxWidth()
.minimumInteractiveComponentSize()) {
Row( ...
Aquí tenemos un card que envuelve a nuestro elemento, el cual contiene una row con la descripción de nuestra opción y su correspondiente switch.
De cara al lector de pantalla, nos aseguramos de decirle que todo el card es nuestro switch con el fin de evitar redundancias.
La navegación en Android a través de ciertas herramientas de accesibilidad no se maneja de la misma manera que la navegación normal. Puede que el usuario navegue por teclado, por Voice Access, por un interruptor de accesibilidad o por gestos. Para movernos por los diferentes elementos de la pantalla, lo haremos desplazando el foco a un “siguiente elemento”, “anterior elemento”, “seleccionar elemento”, entre otros, lo que repercute en la experiencia de usuario drásticamente.
En el caso de disponer de una lista de elementos y estos tengan componentes anidados, nuestro usuario puede perderse en un sinfín de elementos focusables que poco o nada pueden estar aportando a su interacción de la interfaz útil.
Vamos a utilizar el ejemplo del switch anterior para ilustrar el manejo del foco. Tenemos un card con un switch anidado, pero nos interesa que el elemento que cambie el selector sea el elemento padre, es decir, pulsamos sobre la card y el switch se activa y desactiva.
No queremos que el foco se nos pierda dentro de cada elemento ni que se posicione sobre el switch en sí, y para esto podemos ocultar al lector de pantalla ciertos elementos con invisibleToUser() dentro de semantics. Esto ocultará a Talkback el switch y, en combinación con el Role.Switch, le diremos al lector de pantalla que nuestro elemento padre es el switch que está anunciando.
Card(onClick = { onDarkModeToggle() },
modifier = Modifier
.semantics(mergeDescendants = true) {
role = Role.Switch
}
.fillMaxWidth() {
Row(
modifier = Modifier.fillMaxWidth())
) {
Text(text = “Modo oscuro”)
Switch(modifier = Modifier
.semantics {
invisibleToUser()
}, checked = isDarkMode, onCheckedChange = { onDarkModeToggle() })
}
}
Como ves, nuestro foco se posiciona donde es más accesible y no se pierde entre sus elementos hijos.
Si dispones de un elemento que no está aportando nada al lector de pantalla, puedes ocultárselo mediante Modifier.clearAndSetSemantics { … }.
Cuando tenemos elementos anidados, es posible que nuestro lector de pantalla se pierda leyendo también las semantics de sus elementos hijos, provocando repetición de la información sin aportar nada al usuario. Para evitarlo, podemos utilizar clearAndSetSemantics, que nos desactivará estos detalles de nuestros elementos hijos, dejando únicamente los del elemento padre.
Column(
modifier = Modifier
.minimumInteractiveComponentSize()
.clickable(
onClick = onClickElement
)
.clearAndSetSemantics {
contentDescription = element.title
}
) {
Card {
Image(
modifier = Modifier.semantics {
contentDescription = “Image description”)
...
En este caso tenemos varios contentDescription, uno referido al padre y otro al elemento hijo. De esta manera, nuestro lector de pantalla solo leerá el contentDescription superior. Usar este modificador o no usarlo dependerá de la información que necesitemos contar o no contar al usuario, para cada circunstancia habrá una solución más o menos óptima.
Tranquilo/a, sabemos que son muchos cambios, que muchos no son rápidos, que hay que testearlos bien y que parte de ellos hay que consensuarlos con otras tecnologías o con negocio. Te recomendamos que empieces poco a poco, y te vamos a dar algunos consejos:
Estas son solo algunas pinceladas de todo lo que puedes hacer para convertir tu app en una app para todo el mundo pero, por supuesto, hay muchas más.
Te invitamos a probar, a indagar más en el tema y a bucear en el maravilloso mundo de la accesibilidad para que todo el mundo pueda acceder a tus apps y ayudar a hacer un mundo más accesible para todos y todas.
La accesibilidad pasa a ser una característica obligatoria desde ya y aquí ya nos hemos sumado al cambio. ¿Contamos contigo?
Referencias
Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.
Cuéntanos qué te parece.