El modelo de diseño de Compose te permite usar AlignmentLine
para crear líneas de alineación personalizadas que los diseños de nivel superior pueden usar para alinear y posicionar sus elementos secundarios. Por ejemplo, Row
puede usar las líneas de alineación personalizadas de sus elementos secundarios para alinearlos.
Cuando un diseño proporciona un valor para una AlignmentLine
específica, los elementos superiores del diseño pueden leer este valor después de la medición mediante el operador Placeable.get
en la instancia Placeable
correspondiente.
Según la posición de la AlignmentLine
, los elementos superiores pueden decidir el posicionamiento de los elementos secundarios.
Algunos elementos de Compose que admiten composición ya incluyen líneas de alineación. Por ejemplo, el elemento BasicText
, que admite composición, expone las líneas de alineación FirstBaseline
y LastBaseline
.
En el siguiente ejemplo, un LayoutModifier
personalizado llamado firstBaselineToTop
lee el FirstBaseline
para agregar padding al Text
a partir del primer modelo de referencia.
Figura 1: Muestra la diferencia entre agregar padding normal a un elemento y aplicar padding al modelo de referencia de un elemento de texto.
fun Modifier.firstBaselineToTop( firstBaselineToTop: Dp, ) = layout { measurable, constraints -> // Measure the composable val placeable = measurable.measure(constraints) // Check the composable has a first baseline check(placeable[FirstBaseline] != AlignmentLine.Unspecified) val firstBaseline = placeable[FirstBaseline] // Height of the composable with padding - first baseline val placeableY = firstBaselineToTop.roundToPx() - firstBaseline val height = placeable.height + placeableY layout(placeable.width, height) { // Where the composable gets placed placeable.placeRelative(0, placeableY) } } @Preview @Composable private fun TextWithPaddingToBaseline() { MaterialTheme { Text("Hi there!", Modifier.firstBaselineToTop(32.dp)) } }
Para leer el FirstBaseline
del ejemplo, se usa placeable [FirstBaseline]
en la fase de medición.
Crear líneas de alineación personalizadas
Cuando se crea un elemento Layout
personalizado que admite composición o un LayoutModifier
personalizado, puedes proporcionar líneas de alineación personalizadas a fin de que otros elementos superiores que admiten composición puedan usarlas para alinear y posicionar los elementos secundarios según corresponda.
En el siguiente ejemplo, se muestra un elemento BarChart
que admite composición y muestra dos líneas de alineación, MaxChartValue
y MinChartValue
, de modo que otros elementos que admitan composición puedan alinearse con el valor de datos máximo y mínimo del gráfico. Dos elementos de texto, Max y Min, se alinearon en el centro de las líneas de alineación personalizadas.
Figura 2: BarChart
que admite composición con el texto alineado al valor de datos máximo y mínimo.
Las líneas de alineación personalizada se definen como variables de nivel superior de tu proyecto.
/** * AlignmentLine defined by the maximum data value in a [BarChart] */ private val MaxChartValue = HorizontalAlignmentLine(merger = { old, new -> min(old, new) }) /** * AlignmentLine defined by the minimum data value in a [BarChart] */ private val MinChartValue = HorizontalAlignmentLine(merger = { old, new -> max(old, new) })
Las líneas de alineación personalizadas que se utilizan en la creación de nuestro ejemplo son del tipo HorizontalAlignmentLine
, ya que su función es alinear verticalmente los elementos secundarios. Se pasa una política de combinación como parámetro en caso de que varios diseños proporcionen un valor para estas líneas de alineación. Mientras las coordenadas del sistema de diseño de Compose y las coordenadas de Canvas
representen [0, 0]
, la esquina superior izquierda y los ejes x
y y
serán positivos hacia abajo, para que el valor MaxChartValue
siempre sea menor que MinChartValue
. Por lo tanto, la política de combinación es min
para el modelo de referencia de valor de datos del gráfico máximo y max
para el modelo de referencia de valor de datos del gráfico mínimo.
Cuando crees un elemento Layout
o un LayoutModifier
personalizado, especifica líneas de alineación personalizadas en el método MeasureScope.layout
, que toma un parámetro alignmentLines: Map<AlignmentLine, Int>
.
@Composable private fun BarChart( dataPoints: List<Int>, modifier: Modifier = Modifier, ) { val maxValue: Float = remember(dataPoints) { dataPoints.maxOrNull()!! * 1.2f } BoxWithConstraints(modifier = modifier) { val density = LocalDensity.current with(density) { // ... // Calculate baselines val maxYBaseline = // ... val minYBaseline = // ... Layout( content = {}, modifier = Modifier.drawBehind { // ... } ) { _, constraints -> with(constraints) { layout( width = if (hasBoundedWidth) maxWidth else minWidth, height = if (hasBoundedHeight) maxHeight else minHeight, // Custom AlignmentLines are set here. These are propagated // to direct and indirect parent composables. alignmentLines = mapOf( MinChartValue to minYBaseline.roundToInt(), MaxChartValue to maxYBaseline.roundToInt() ) ) {} } } } } }
Los elementos superiores directos e indirectos de este elemento que admite composición pueden consumir las líneas de alineación. El siguiente elemento que admite composición crea un diseño personalizado que toma como parámetro dos ranuras y datos Text
, y alinea los dos textos con los valores de datos de los gráficos máximo y mínimo. La vista previa de este elemento que admite composición es la que se muestra en la Figura 2.
@Composable private fun BarChartMinMax( dataPoints: List<Int>, maxText: @Composable () -> Unit, minText: @Composable () -> Unit, modifier: Modifier = Modifier, ) { Layout( content = { maxText() minText() // Set a fixed size to make the example easier to follow BarChart(dataPoints, Modifier.size(200.dp)) }, modifier = modifier ) { measurables, constraints -> check(measurables.size == 3) val placeables = measurables.map { it.measure(constraints.copy(minWidth = 0, minHeight = 0)) } val maxTextPlaceable = placeables[0] val minTextPlaceable = placeables[1] val barChartPlaceable = placeables[2] // Obtain the alignment lines from BarChart to position the Text val minValueBaseline = barChartPlaceable[MinChartValue] val maxValueBaseline = barChartPlaceable[MaxChartValue] layout(constraints.maxWidth, constraints.maxHeight) { maxTextPlaceable.placeRelative( x = 0, y = maxValueBaseline - (maxTextPlaceable.height / 2) ) minTextPlaceable.placeRelative( x = 0, y = minValueBaseline - (minTextPlaceable.height / 2) ) barChartPlaceable.placeRelative( x = max(maxTextPlaceable.width, minTextPlaceable.width) + 20, y = 0 ) } } } @Preview @Composable private fun ChartDataPreview() { MaterialTheme { BarChartMinMax( dataPoints = listOf(4, 24, 15), maxText = { Text("Max") }, minText = { Text("Min") }, modifier = Modifier.padding(24.dp) ) } }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Gráficos en Compose
- Diseños personalizados {:#custom-layouts }
- Mediciones intrínsecas en los diseños de Compose