Restricciones y orden de los modificadores

En Compose, puedes encadenar varios modificadores para cambiar la apariencia y el estilo de un elemento componible. Estas cadenas de modificadores pueden afectar las restricciones que se pasan a los elementos componibles, que definen los límites de ancho y alto.

En esta página, se describe cómo los modificadores encadenados afectan las restricciones y, a su vez, la medición y la colocación de los elementos componibles.

Modificadores en el árbol de la IU

Para comprender cómo se influyen los modificadores entre sí, es útil visualizar cómo aparecen en el árbol de la IU, que se genera durante la fase de composición. Para obtener más información, consulta la sección Composición.

En el árbol de IU, puedes visualizar los modificadores como nodos de wrapper para los nodos de diseño:

Código para elementos componibles y modificadores, y su representación visual como un árbol de IU
Figura 1: Son modificadores que encapsulan nodos de diseño en el árbol de IU.

Agregar más de un modificador a un elemento componible crea una cadena de modificadores. Cuando encadenas varios modificadores, cada nodo modificador envuelve el resto de la cadena y el nodo de diseño dentro de ella. Por ejemplo, cuando encadenas un modificador clip y un modificador size, el nodo del modificador clip une el nodo del modificador size, que, a su vez, une el nodo de diseño Image.

En la fase de diseño, el algoritmo que recorre el árbol sigue siendo el mismo, pero también se visita cada nodo del modificador. De esta manera, un modificador puede cambiar los requisitos de tamaño y la colocación del nodo de diseño o modificador que contiene.

Como se muestra en la Figura 2, la implementación de los elementos Image y Text componibles consta de una cadena de modificadores que encapsulan un solo nodo de diseño. Las implementaciones de Row y Column son simplemente nodos de diseño que describen cómo diseñar sus elementos secundarios.

La estructura de árbol anterior, pero ahora cada nodo es solo un diseño simple, con muchos nodos de ajuste de modificadores a su alrededor.
Figura 2: La misma estructura de árbol que en la figura 1, pero con los elementos componibles en el árbol de IU visualizados como cadenas de modificadores.

En resumen:

  • Los modificadores encapsulan un solo nodo de diseño o modificador.
  • Los nodos de diseño pueden diseñar varios nodos secundarios.

En las siguientes secciones, se describe cómo usar este modelo mental para razonar sobre el encadenamiento de modificadores y cómo influye en el tamaño de los elementos componibles.

Restricciones en la fase de diseño

La fase de diseño sigue un algoritmo de tres pasos para encontrar el ancho, la altura y las coordenadas X e Y de cada nodo de diseño:

  1. Medir los elementos secundarios: Un nodo mide sus elementos secundarios, si los tiene.
  2. Decidir su propio tamaño: Según esas mediciones, un nodo decide su propio tamaño.
  3. Colocar elementos secundarios: Cada nodo secundario se coloca en relación con la posición del nodo.

Constraints ayuda a encontrar los tamaños adecuados para los nodos durante los dos primeros pasos del algoritmo. Las restricciones definen los límites mínimos y máximos para el ancho y la altura de un nodo. Cuando el nodo decide su tamaño, el tamaño medido debe estar dentro de este rango.

Tipos de restricciones

Una restricción puede ser una de las siguientes:

  • Delimitado: El nodo tiene un ancho y un alto máximos y mínimos.
Restricciones delimitadas de diferentes tamaños dentro de un contenedor.
Figura 3. Restricciones acotadas.
  • Sin límites: El nodo no está restringido a ningún tamaño. Los límites máximos de ancho y alto se establecen en infinito.
Son restricciones no acotadas que tienen el ancho y la altura establecidos en infinito. Las restricciones se extienden más allá del contenedor.
Figura 4. Restricciones no acotadas.
  • Exacta: Se le pide al nodo que siga un requisito de tamaño exacto. Los límites mínimo y máximo se establecen en el mismo valor.
Son restricciones exactas que se ajustan a un requisito de tamaño exacto dentro del contenedor.
Figura 5: Restricciones exactas.
  • Combinación: El nodo sigue una combinación de los tipos de restricciones anteriores. Por ejemplo, una restricción podría limitar el ancho y permitir una altura máxima ilimitada, o bien establecer un ancho exacto, pero proporcionar una altura limitada.
Dos contenedores que muestran combinaciones de restricciones delimitadas y no delimitadas, y anchos y alturas exactos.
Figura 6: Combinaciones de restricciones acotadas y no acotadas, y anchos y alturas exactos.

En la siguiente sección, se describe cómo se pasan estas restricciones de un elemento superior a uno secundario.

Cómo se pasan las restricciones del elemento principal al secundario

Durante el primer paso del algoritmo que se describe en Restricciones en la fase de diseño, las restricciones se pasan del elemento principal al secundario en el árbol de IU.

Cuando un nodo principal mide a sus nodos secundarios, proporciona estas restricciones a cada nodo secundario para que sepa qué tan grande o pequeño puede ser. Luego, cuando decide su propio tamaño, también se ajusta a las restricciones que le pasaron sus propios elementos superiores.

En términos generales, el algoritmo funciona de la siguiente manera:

  1. Para decidir el tamaño que realmente quiere ocupar, el nodo raíz del árbol de IU mide sus elementos secundarios y reenvía las mismas restricciones a su primer elemento secundario.
  2. Si el hijo es un modificador que no afecta la medición, reenvía las restricciones al siguiente modificador. Las restricciones se pasan por la cadena de modificadores tal como están, a menos que se alcance un modificador que afecte la medición. Luego, las restricciones se redimensionan según corresponda.
  3. Una vez que se llega a un nodo que no tiene elementos secundarios (denominado "nodo hoja"), este decide su tamaño en función de las restricciones que se pasaron y devuelve este tamaño resuelto a su elemento superior.
  4. El elemento principal adapta sus restricciones en función de las mediciones de este elemento secundario y llama a su siguiente elemento secundario con estas restricciones ajustadas.
  5. Una vez que se miden todos los elementos secundarios de un elemento principal, el nodo principal decide su propio tamaño y se lo comunica a su propio elemento principal.
  6. De esta manera, se recorre todo el árbol en profundidad. Finalmente, todos los nodos deciden sus tamaños y se completa el paso de medición.

Para ver un ejemplo detallado, consulta el video Constraints and modifier order.

Modificadores que afectan las restricciones

En la sección anterior, aprendiste que algunos modificadores pueden afectar el tamaño de la restricción. En las siguientes secciones, se describen modificadores específicos que afectan las restricciones.

Modificador size

El modificador size declara el tamaño preferido del contenido.

Por ejemplo, el siguiente árbol de IU debe renderizarse en un contenedor de 300dp con 200dp. Las restricciones son delimitadas, lo que permite anchos entre 100dp y 300dp, y alturas entre 100dp y 200dp:

Una parte de un árbol de IU con el modificador de tamaño que ajusta un nodo de diseño y la representación de las restricciones delimitadas establecidas por el modificador de tamaño en un contenedor.
Figura 7: Restricciones delimitadas en el árbol de la IU y su representación en un contenedor.

El modificador size adapta las restricciones entrantes para que coincidan con el valor que se le pasa. En este ejemplo, el valor es 150dp:

Es igual que la figura 7, excepto que el modificador de tamaño adapta las restricciones entrantes para que coincidan con el valor que se le pasa.
Figura 8: El modificador size que ajusta las restricciones a 150dp.

Si el ancho y la altura son más pequeños que el límite de restricción más pequeño o más grandes que el límite de restricción más grande, el modificador coincide con las restricciones pasadas lo más cerca posible sin dejar de cumplir con las restricciones pasadas:

Dos árboles de IU y sus representaciones correspondientes en contenedores. En el primer caso, el modificador de tamaño acepta las restricciones entrantes; en el segundo, el modificador de tamaño se adapta a las restricciones demasiado grandes lo más cerca posible, lo que genera restricciones que llenan el contenedor.
Figura 9: El modificador size que se ajusta a la restricción pasada lo más cerca posible.

Ten en cuenta que encadenar varios modificadores size no funciona. El primer modificador size establece las restricciones mínimas y máximas en un valor fijo. Incluso si el segundo modificador de tamaño solicita un tamaño más pequeño o más grande, debe cumplir con los límites exactos que se pasaron, por lo que no anulará esos valores:

Una cadena de dos modificadores de tamaño en el árbol de IU y su representación en un contenedor, que es el resultado del primer valor pasado y no del segundo.
Figura 10: Una cadena de dos modificadores size, en la que el segundo valor pasado (50dp) no anula el primer valor (100dp).

Modificador requiredSize

Usa el modificador requiredSize en lugar de size si necesitas que tu nodo anule las restricciones entrantes. El modificador requiredSize reemplaza las restricciones entrantes y pasa el tamaño que especificas como límites exactos.

Cuando el tamaño se pasa de vuelta al árbol, el nodo secundario se centrará en el espacio disponible:

El modificador size y requiredSize encadenados en un árbol de IU, y la representación correspondiente en un contenedor. Las restricciones del modificador requiredSize anulan las restricciones del modificador de tamaño.
Figura 11: El modificador requiredSize anula las restricciones entrantes del modificador size.

Modificadores width y height

El modificador size adapta el ancho y el alto de las restricciones. Con el modificador width, puedes establecer un ancho fijo, pero dejar la altura sin definir. Del mismo modo, con el modificador height, puedes establecer una altura fija, pero dejar el ancho sin decidir:

Dos árboles de IU, uno con el modificador de ancho y su representación de contenedor, y el otro con el modificador de altura y su representación.
Figura 12. El modificador width y el modificador height establecen un ancho y una altura fijos, respectivamente.

Modificador sizeIn

El modificador sizeIn te permite establecer restricciones mínimas y máximas exactas para el ancho y la altura. Usa el modificador sizeIn si necesitas un control detallado sobre las restricciones.

Un árbol de IU con el modificador sizeIn con anchos y alturas mínimos y máximos establecidos, y su representación dentro de un contenedor.
Figura 13. El modificador sizeIn con los parámetros de configuración minWidth, maxWidth, minHeight y maxHeight.

Ejemplos

En esta sección, se muestra y explica el resultado de varios fragmentos de código con modificadores encadenados.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .size(50.dp)
)

Este fragmento produce el siguiente resultado:

  • El modificador fillMaxSize cambia las restricciones para establecer el ancho y el alto mínimos en el valor máximo: 300dp en ancho y 200dp en alto.
  • Aunque el modificador size quiere usar un tamaño de 50dp, aún debe cumplir con las restricciones mínimas entrantes. Por lo tanto, el modificador size también generará los límites exactos de la restricción de 300 por 200, lo que ignorará de manera efectiva el valor proporcionado en el modificador size.
  • El Image sigue estos límites y registra un tamaño de 300 por 200, que se pasa por todo el árbol.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .wrapContentSize()
        .size(50.dp)
)

Este fragmento produce el siguiente resultado:

  • El modificador fillMaxSize adapta las restricciones para establecer el ancho y la altura mínimos en el valor máximo: 300dp en ancho y 200dp en altura.
  • El modificador wrapContentSize restablece las restricciones mínimas. Por lo tanto, mientras que fillMaxSize generó restricciones fijas, wrapContentSize las restablece a restricciones acotadas. El siguiente nodo ahora puede ocupar todo el espacio nuevamente o ser más pequeño que todo el espacio.
  • El modificador size establece las restricciones en los límites mínimo y máximo de 50.
  • El Image se resuelve en un tamaño de 50 por 50, y el modificador size reenvía ese valor.
  • El modificador wrapContentSize tiene una propiedad especial. Toma su hijo y lo coloca en el centro de los límites mínimos disponibles que se le pasaron. Por lo tanto, el tamaño que comunica a sus elementos superiores es igual a los límites mínimos que se le pasaron.

Si combinas solo tres modificadores, puedes definir un tamaño para el elemento componible y centrarlo en su elemento superior.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .clip(CircleShape)
        .padding(10.dp)
        .size(100.dp)
)

Este fragmento produce el siguiente resultado:

  • El modificador clip no cambia las restricciones.
    • El modificador padding reduce las restricciones máximas.
    • El modificador size establece todas las restricciones en 100dp.
    • El Image cumple con esas restricciones y registra un tamaño de 100 por 100dp.
    • El modificador padding agrega 10dp en todos los tamaños, por lo que aumenta el ancho y el alto informados en 20dp.
    • Ahora, en la fase de dibujo, el modificador clip actúa sobre un lienzo de 120 por 120dp. Por lo tanto, crea una máscara circular de ese tamaño.
    • Luego, el modificador padding inserta su contenido en 10dp en todos los tamaños, por lo que reduce el tamaño del lienzo a 100 por 100dp.
    • El Image se dibuja en ese lienzo. La imagen se recorta según el círculo original de 120dp, por lo que el resultado no es redondo.