Una de las reglas de Compose es que solo debes medir tus elementos secundarios una vez. Si lo haces dos veces, se genera una excepción de tiempo de ejecución. Sin embargo, hay momentos en los que necesitas información sobre tus elementos secundarios antes de medirlos.
Los elementos intrínsecos te permiten realizar consultas a los elementos secundarios antes de que se midan realmente.
Para un elemento componible, puedes solicitar su intrinsicWidth
o intrinsicHeight
:
(min|max)IntrinsicWidth
: Con este ancho, ¿cuál es el ancho mínimo y máximo con el que puedes pintar correctamente el contenido?(min|max)IntrinsicHeight
: Con esta altura, ¿cuál es la altura mínima o máxima con la que puedes pintar correctamente el contenido?
Por ejemplo, si solicitas la minIntrinsicHeight
de un Text
con height
infinito, se mostrará la height
del Text
como si se hubiera dibujado el texto en una línea individual.
Funciones intrínsecas en acción
Imagina que queremos crear un elemento componible que muestre dos textos en la pantalla separados por un divisor como este:
¿Cómo podemos hacer esto? Podemos tener un objeto Row
con dos Text
que se expandan tanto como sea posible y un Divider
en el medio. Queremos que el Divider
sea tan alto como el Text
más alto y delgado (width = 1.dp
).
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) HorizontalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } }
En la vista previa, vemos que el Divider
se expande a toda la pantalla, pero eso no es lo que deseamos:
Esto ocurre porque Row
mide cada elemento secundario de forma individual, y la altura de Text
no se puede usar para restringir Divider
. Queremos que el Divider
ocupe el espacio disponible con una altura determinada. Para eso, podemos usar el modificador height(IntrinsicSize.Min)
.
height(IntrinsicSize.Min)
ajusta su tamaño a los elementos secundarios para que sean tan altos como su altura mínima intrínseca. Como es recurrente, realizará consultas a Row
y sus elementos secundarios minIntrinsicHeight
.
Cuando lo apliquemos a nuestro código, funcionará según lo esperado:
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier.height(IntrinsicSize.Min)) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) HorizontalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } } // @Preview @Composable fun TwoTextsPreview() { MaterialTheme { Surface { TwoTexts(text1 = "Hi", text2 = "there") } } }
Con vista previa:
El elemento componible minIntrinsicHeight
de la Row
será la minIntrinsicHeight
máxima de sus elementos secundarios. El elemento minIntrinsicHeight
de Divider
es 0, ya que no ocupa espacio si no se le aplican restricciones. El minIntrinsicHeight
de Text
será el del texto según un width
específico. Por lo tanto, la restricción height
del elemento Row
será la minIntrinsicHeight
máxima de los Text
. Luego, Divider
expandirá su height
a la restricción height
proporcionada por Row
.
Funciones intrínsecas en tus diseños personalizados
Cuando se crea un modificador Layout
o layout
personalizado, las mediciones intrínsecas se calculan automáticamente en función de aproximaciones. Por lo tanto, es posible que los cálculos no sean correctos para todos los diseños. Estas APIs ofrecen opciones para anular estos valores predeterminados.
Para especificar las mediciones intrínsecas de tu Layout
personalizado, anula minIntrinsicWidth
, minIntrinsicHeight
, maxIntrinsicWidth
y maxIntrinsicHeight
de MeasurePolicy
cuando la creas.
@Composable fun MyCustomComposable( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( content = content, modifier = modifier, measurePolicy = object : MeasurePolicy { override fun MeasureScope.measure( measurables: List<Measurable>, constraints: Constraints ): MeasureResult { // Measure and layout here // ... } override fun IntrinsicMeasureScope.minIntrinsicWidth( measurables: List<IntrinsicMeasurable>, height: Int ): Int { // Logic here // ... } // Other intrinsics related methods have a default value, // you can override only the methods that you need. } ) }
Cuando crees el modificador layout
personalizado, anula los métodos relacionados en la interfaz LayoutModifier
.
fun Modifier.myCustomModifier(/* ... */) = this then object : LayoutModifier { override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { // Measure and layout here // ... } override fun IntrinsicMeasureScope.minIntrinsicWidth( measurable: IntrinsicMeasurable, height: Int ): Int { // Logic here // ... } // Other intrinsics related methods have a default value, // you can override only the methods that you need. }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Diseños personalizados {:#custom-layouts }
- Líneas de alineación en Jetpack Compose
- Fases de Jetpack Compose