Una composición describe la IU de tu app y se produce ejecutando elementos componibles. La composición es una estructura de árbol que consta de los elementos elementos componibles que describen tu IU.
Junto a la composición, existe un árbol paralelo denominado semántica árbol. En este árbol se describe tu IU de una manera alternativa que se comprensible para los servicios de accesibilidad y para el curso Testing en un framework de aplicaciones. Los servicios de accesibilidad usan el árbol para describir la app a los usuarios con una necesidad específica. El framework de pruebas usa el árbol para interactuar con tu app y hacer aserciones al respecto. El árbol semántico no contiene las información para dibujar tus elementos componibles, pero contiene información sobre el significado semántico de tus elementos componibles.
![Una jerarquía de IU típica y su árbol semántico](https://developer.android.com/static/develop/ui/compose/images/semantics-ui-tree.png?authuser=7&hl=es)
Si la app se conforma de elementos que admiten composición y modificadores de la biblioteca base y Material de Compose, el árbol semántico se completará y generará automáticamente. Sin embargo, cuando agregas elementos componibles personalizados de bajo nivel, tienes para proporcionar su semántica de forma manual. También, en algunas situaciones, es posible que el árbol no represente de forma correcta o completa el significado de los elementos en la pantalla. En este caso, puedes adaptar el árbol.
Por ejemplo, ten en cuenta este calendario personalizado que admite composición:
![Un calendario personalizado componible con elementos de día seleccionables](https://developer.android.com/static/develop/ui/compose/images/semantics-calendar.png?authuser=7&hl=es)
En este ejemplo, todo el calendario se implementa como un solo elemento que admite composición de bajo nivel si se usa el objeto Layout
que admite composición y se dibuja directamente en Canvas
.
Si no realizas ninguna otra acción, los servicios de accesibilidad no recibirán suficientes
información sobre el contenido del elemento componible y la selección del usuario dentro
el calendario. Por ejemplo, si un usuario hace clic en el día que contiene 17, el marco de trabajo de accesibilidad solamente recibe la información de descripción de todo el control de calendario. En este caso, el servicio de accesibilidad TalkBack
anunciar "Calendario" o, mejor, "Calendario de abril". y el usuario
estaría preguntándose qué día se seleccionó. Para que este elemento componible sea más
accesible, deberás agregar información semántica de forma manual.
Propiedades semánticas
Todos los nodos en el árbol de IU con algún significado semántico tienen un nodo paralelo en el árbol semántico. El nodo en el árbol semántico incluye esas propiedades que transmiten el significado del elemento correspondiente que admite composición. Por ejemplo, Text
.
componible contiene una propiedad semántica text
, porque ese es el significado de
ese elemento componible. Un Icon
contiene una propiedad contentDescription
(si lo establece
el desarrollador) que transmite por texto el significado de Icon
.
Elementos componibles y modificadores que se compilan sobre la base de Compose
existente ya establecieron las propiedades relevantes por ti. Opcionalmente, establecer
o anula las propiedades tú mismo con semantics
y
clearAndSetSemantics
. Por ejemplo, agrega datos
de accesibilidad a un nodo, proporcionan un estado alternativo
descripción de un elemento que se puede activar o desactivar, o bien indicar que cierto texto
elemento componible debe considerarse como un encabezado.
Para visualizar el árbol semántico, usa la herramienta Inspector de diseño o la
printToLog()
en las pruebas. Esto imprime la información
Árbol semántico dentro de Logcat
class MyComposeTest { @get:Rule val composeTestRule = createComposeRule() @Test fun MyTest() { // Start the app composeTestRule.setContent { MyTheme { Text("Hello world!") } } // Log the full semantics tree composeTestRule.onRoot().printToLog("MY TAG") } }
El resultado de esta prueba sería el siguiente:
Printing with useUnmergedTree = 'false'
Node #1 at (l=0.0, t=63.0, r=221.0, b=120.0)px
|-Node #2 at (l=0.0, t=63.0, r=221.0, b=120.0)px
Text = '[Hello world!]'
Actions = [GetTextLayoutResult]
Ten en cuenta cómo las propiedades semánticas transmiten el significado de un elemento componible. Consider a
Switch
El usuario lo verá de la siguiente manera:
![](https://developer.android.com/static/develop/ui/compose/images/semantics-switches.png?authuser=7&hl=es)
Para describir el significado de este elemento, podrías decir lo siguiente: "Este es un interruptor, que es un elemento que se puede activar o desactivar en su estado para cada estado. Puedes hacer clic en ella interactuar con ella".
Las propiedades semánticas se usan exactamente para este fin. El nodo semántico de este elemento Switch contiene las siguientes propiedades, como se visualizan con el Inspector de diseño:
![El Inspector de diseño muestra las propiedades semánticas de un elemento Switch componible.](https://developer.android.com/static/develop/ui/compose/images/semantics-layout-inspector-switch.png?authuser=7&hl=es)
El Role
indica el tipo de elemento. En el StateDescription
, se describe cómo
el botón de encendido se debe hacer referencia a ese estado. Por defecto, es una versión localizada del
la palabra "Activado", pero se puede hacer más específica (por ejemplo, "Habilitada") en función
según el contexto. ToggleableState
es el estado actual del interruptor. El
La propiedad OnClick
hace referencia al método que se usa para interactuar con este elemento. Para
una lista completa de las propiedades semánticas, consulta la SemanticsProperties
. Para obtener una lista completa de las acciones de accesibilidad posibles, consulta el objeto SemanticsActions
.
Hacer un seguimiento de las propiedades semánticas de cada elemento que admite composición en la app ofrece muchas posibilidades potentes. Estos son algunos ejemplos:
- TalkBack usa las propiedades para leer en voz alta lo que se muestra en la pantalla y permite al usuario interactuar con él sin problemas. Para el elemento Switch componible, es posible que TalkBack diga: "Activado; Switch; presiona dos veces para activar o desactivar". El usuario puede presionar dos veces su para desactivarlo.
-
El marco de trabajo de prueba usa las propiedades para encontrar nodos, interactuar con ellos y realizar aserciones. Una muestra de prueba para el Switch podría ser la siguiente:
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Árbol semántico combinado y separado
Como se mencionó antes, es posible que cada elemento que admite composición en el árbol de IU no tenga propiedades semánticas establecidas o que sí las tenga. Cuando un elemento componible no tiene propiedades semánticas establecidas, no se incluye como parte del árbol semántico. De esa manera, el árbol semántico solamente incluye los nodos que, en realidad, tienen significado semántico. Sin embargo, con frecuencia, para transmitir el significado correcto de lo que se muestra en la pantalla, también es útil combinar subárboles determinados de nodos y tratarlos como uno solo. De esa manera puedes pensar en un conjunto de nodos como un todo, en lugar de ocuparte de cada su nodo subordinado de forma individual. Como regla general, cada nodo de este árbol representa un elemento enfocable cuando se usan los servicios de accesibilidad.
Un ejemplo de este tipo de elemento componible es Button
. Puedes razonar sobre un botón
como un solo elemento, aunque pueda contener varios nodos secundarios:
Button(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = null ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Like") }
En el árbol semántico, se combinan las propiedades de los elementos subordinados del botón. y el botón se presenta como un nodo de hoja única en el árbol:
![Representación semántica de hoja única combinada](https://developer.android.com/static/develop/ui/compose/images/semantics-merged-single-leaf.png?authuser=7&hl=es)
Los elementos que admiten composición y los modificadores pueden indicar que quieren combinar las propiedades semánticas de sus elementos subordinados mediante una llamada a Modifier.semantics
(mergeDescendants = true) {}
. Establecer esta propiedad en true
indica que se deben combinar las propiedades semánticas. En el ejemplo de Button
, Button
componible usa el modificador clickable
de forma interna que incluye este
Modificador semantics
. Por lo tanto, se combinan los nodos subordinados del botón.
Lee la documentación sobre accesibilidad para obtener más información sobre cuándo deberías cambiar
de combinación en tu elemento componible.
Varios modificadores y elementos que admiten composición en las bibliotecas base y material de Compose tienen esta propiedad establecida. Por ejemplo, los modificadores clickable
y toggleable
combinarán automáticamente sus elementos subordinados. El elemento ListItem
que admite composición también los combinará.
Inspecciona los árboles
De hecho, el árbol semántico son dos árboles diferentes. Hay una semántica combinada
, que combina nodos subordinados cuando mergeDescendants
se establece en true
También hay un árbol semántico separado, que no aplica la combinación, pero
mantiene todos los nodos intactos. Los servicios de accesibilidad usan el árbol separado y aplican
sus propios algoritmos de combinación, teniendo en cuenta mergeDescendants
propiedad. El framework de prueba usa el árbol combinado de forma predeterminada.
Puedes inspeccionar ambos árboles con el método printToLog()
. De forma predeterminada, y
en los ejemplos anteriores, se registra el árbol combinado. Para imprimir el árbol separado
en su lugar, establece el parámetro useUnmergedTree
del comparador onRoot()
true
:
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
El Inspector de diseño te permite mostrar la semántica combinada y la separada. seleccionando el preferido en el filtro de vista:
![Opciones de vista del Inspector de diseño, que permiten mostrar el árbol semántico combinado y el separado.](https://developer.android.com/static/develop/ui/compose/images/semantics-layout-inspector-menu.png?authuser=7&hl=es)
Para cada nodo del árbol, el Inspector de diseño muestra la semántica combinada y la que se establece en ese nodo en el panel de propiedades:
![Se combinaron y configuraron las propiedades semánticas](https://developer.android.com/static/develop/ui/compose/images/semantics-merged-and-set.png?authuser=7&hl=es)
De forma predeterminada, los comparadores en el marco de trabajo de prueba usan el árbol semántico combinado.
Por eso, puedes interactuar con un Button
haciendo coincidir el texto que se muestra dentro
de la siguiente manera:
composeTestRule.onNodeWithText("Like").performClick()
Puedes anular este comportamiento configurando el parámetro useUnmergedTree
del
comparadores a true
, al igual que con el comparador onRoot
.
Comportamiento de combinación
Cuando un elemento que admite composición indica que sus elementos subordinados deben combinarse, ¿cómo ocurre exactamente la combinación?
Cada propiedad semántica tiene una estrategia de combinación definida. Por ejemplo, el
La propiedad ContentDescription
agrega todos los valores subordinados de ContentDescription a un
lista. Para comprobar la estrategia de combinación de una propiedad semántica, revisa su
Implementación de mergePolicy
en SemanticsProperties.kt
. Las propiedades pueden
tomar el valor superior o secundario, combinar los valores en una lista o
una cadena, no permitir combinaciones y, en su lugar, arroja una excepción, o cualquier otra
una estrategia de combinación personalizada.
Una nota importante es que los elementos subordinados que hayan configurado mergeDescendants
= true
por su cuenta no se incluyen en la combinación. Veamos un ejemplo:
![Elemento de la lista con imagen, texto y un ícono de favorito](https://developer.android.com/static/develop/ui/compose/images/semantics-dagger.png?authuser=7&hl=es)
A continuación, se muestra un elemento de lista en el que se puede hacer clic. Cuando el usuario presiona la fila, la app navega a la página de detalles del artículo, en la que el usuario puede leerlo. Dentro del elemento de la lista, hay un botón para agregar el artículo a favoritos, que se forma un elemento anidado en el que se puede hacer clic, de modo que el botón aparezca por separado en el árbol combinado. Se combina el resto del contenido en la fila:
![El árbol combinado incluye varios textos en una lista dentro del nodo Row El árbol separado incluye nodos que no están combinados para cada elemento Text que admite composición.](https://developer.android.com/static/develop/ui/compose/images/semantics-merged-vs-unmerged.png?authuser=7&hl=es)
Adapta el árbol semántico
Como se mencionó antes, puedes anular o borrar propiedades semánticas o cambiar el comportamiento de combinación del árbol. Esto es particularmente relevante cuando estás creando tus propios componentes personalizados. Sin establecer la configuración correcta y el comportamiento de combinación, es posible que no se pueda acceder a tu app y que las pruebas comportan de manera diferente a lo que esperabas. Para leer más sobre algunos casos de uso comunes dónde debes adaptar el árbol semántico, lee el artículo de accesibilidad documentación. Si deseas obtener más información sobre las pruebas, consulta el Centro de de la guía de YouTube.
Recursos adicionales
- Accesibilidad: conceptos esenciales y técnicas comunes a todo el desarrollo de apps para Android
- Compila apps accesibles: Pasos clave que puedes realizar para que tu app sea más accesible
- Principios para mejorar la aplicación accesibilidad: Los principios clave para tener en cuenta cuando trabajes para hacer que tu aplicación sea más accesible
- Prueba de accesibilidad: Principios y herramientas de prueba para la accesibilidad de Android
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Accesibilidad en Compose
- Material Design 2 en Compose
- Cómo probar tu diseño de Compose