ConstraintLayout
es un diseño que te permite colocar elementos componibles en relación con otros del mismo tipo en la pantalla. Es una alternativa al uso de varios Row
, Column
, Box
y otros elementos de diseño personalizados anidados. ConstraintLayout
es útil cuando se implementan diseños más grandes con requisitos de alineación más complejos.
Considera usar ConstraintLayout
en las siguientes situaciones:
- A fin de evitar anidar varios
Column
yRow
para posicionar elementos en la pantalla a fin de mejorar la legibilidad del código. - Para posicionar elementos de componibilidad en relación con otros elementos que admiten composición, o bien posicionarlos según lineamientos, barreras o cadenas.
Nota: En el sistema de View, ConstraintLayout
era la forma recomendada para crear diseños grandes y complejos, ya que una jerarquía de vistas plana era mejor, en términos de rendimiento, que las vistas anidadas. Sin embargo, eso no es problema en Compose, ya que puede procesar de manera eficiente jerarquías de diseño detalladas.
Para comenzar con ConstraintLayout
Para usar ConstraintLayout
en Compose, debes agregar esta dependencia en tu build.gradle
(además de la configuración de Compose):
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
En Compose, ConstraintLayout
funciona de la siguiente manera con una DSL:
- Crea referencias para cada elemento componible en
ConstraintLayout
mediantecreateRefs()
ocreateRefFor()
. - Las restricciones se proporcionan mediante el modificador
constrainAs()
, que toma la referencia como parámetro y te permite especificar sus restricciones en la expresión lambda del cuerpo. - Las restricciones se especifican mediante
linkTo()
o algún otro método útil. parent
es una referencia existente que se puede usar para especificar restricciones hacia el mismo elementoConstraintLayout
.
En este ejemplo vemos uno de esos elementos que usa un ConstraintLayout
:
@Composable fun ConstraintLayoutContent() { ConstraintLayout { // Create references for the composables to constrain val (button, text) = createRefs() Button( onClick = { /* Do something */ }, // Assign reference "button" to the Button composable // and constrain it to the top of the ConstraintLayout modifier = Modifier.constrainAs(button) { top.linkTo(parent.top, margin = 16.dp) } ) { Text("Button") } // Assign reference "text" to the Text composable // and constrain it to the bottom of the Button composable Text( "Text", Modifier.constrainAs(text) { top.linkTo(button.bottom, margin = 16.dp) } ) } }
Este código restringe la parte superior del Button
al elemento principal, con un margen de 16.dp
, y un Text
a la parte inferior del Button
, también con un margen de 16.dp
.
API desacoplada
En el ejemplo de ConstraintLayout
, las restricciones se especifican de forma intercalada, con un modificador en el elemento que admite composición al que se aplican. Sin embargo, hay situaciones en las que es preferible desacoplar las restricciones de los diseños a los que se aplican. Por ejemplo, quizás querrías cambiar las restricciones en función de la configuración de la pantalla o agregar una animación entre dos conjuntos de restricciones.
En casos como esos, puedes usar ConstraintLayout
de otro modo:
- Pasa un
ConstraintSet
como parámetro aConstraintLayout
. - Asigna referencias creadas en el
ConstraintSet
a los elementos componibles con el modificadorlayoutId
.
@Composable fun DecoupledConstraintLayout() { BoxWithConstraints { val constraints = if (minWidth < 600.dp) { decoupledConstraints(margin = 16.dp) // Portrait constraints } else { decoupledConstraints(margin = 32.dp) // Landscape constraints } ConstraintLayout(constraints) { Button( onClick = { /* Do something */ }, modifier = Modifier.layoutId("button") ) { Text("Button") } Text("Text", Modifier.layoutId("text")) } } } private fun decoupledConstraints(margin: Dp): ConstraintSet { return ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") constrain(button) { top.linkTo(parent.top, margin = margin) } constrain(text) { top.linkTo(button.bottom, margin) } } }
Luego, cuando necesites cambiar las restricciones, simplemente puedes pasar un ConstraintSet
diferente.
Conceptos de ConstraintLayout
ConstraintLayout
contiene conceptos, como lineamientos, barreras y cadenas, que pueden ayudarte a posicionar elementos dentro del elemento de componibilidad.
Guías
Las guías son pequeñas ayudas visuales para crear diseños. Los elementos de componibilidad pueden estar limitados a un guía. Las guías son útiles para posicionar elementos en un cierto dp
o percentage
dentro del elemento componible superior.
Existen dos tipos de guías: verticales y horizontales. Las dos horizontales son top
y bottom
, y las dos verticales son start
y end
.
ConstraintLayout { // Create guideline from the start of the parent at 10% the width of the Composable val startGuideline = createGuidelineFromStart(0.1f) // Create guideline from the end of the parent at 10% the width of the Composable val endGuideline = createGuidelineFromEnd(0.1f) // Create guideline from 16 dp from the top of the parent val topGuideline = createGuidelineFromTop(16.dp) // Create guideline from 16 dp from the bottom of the parent val bottomGuideline = createGuidelineFromBottom(16.dp) }
Para crear una guía, usa createGuidelineFrom*
con el tipo requerido. Esto crea una referencia que se puede usar en el bloque Modifier.constrainAs()
.
Barreras
Las barreras hacen referencia a varios elementos componibles para crear un lineamiento virtual basado en el widget más extremo del lado especificado.
Para crear una barrera, usa createTopBarrier()
(o createBottomBarrier()
, createEndBarrier()
, createStartBarrier()
) y proporciona las referencias que la conformarán.
ConstraintLayout { val constraintSet = ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") val topBarrier = createTopBarrier(button, text) } }
La barrera se puede usar en un bloque Modifier.constrainAs()
.
Cadenas
Las cadenas proporcionan un comportamiento similar al del grupo en un solo eje (horizontal o vertical). El otro eje se puede restringir de forma independiente.
Para crear una cadena, usa createVerticalChain
o createHorizontalChain
:
ConstraintLayout { val constraintSet = ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") val verticalChain = createVerticalChain(button, text, chainStyle = ChainStyle.Spread) val horizontalChain = createHorizontalChain(button, text) } }
La cadena se puede usar en el bloque Modifier.constrainAs()
.
Se puede configurar una cadena con diferentes ChainStyles
, que deciden cómo administrar el espacio que rodea un elemento componible, por ejemplo:
ChainStyle.Spread
: El espacio se distribuye de manera uniforme entre todos los elementos de componibilidad, incluidos los espacios libres antes del primer elemento de componibilidad y después del último.ChainStyle.SpreadInside
: El espacio se distribuye de manera uniforme entre todos los elementos de componibilidad, sin ningún espacio libre antes del primer elemento que admite composición ni después del último.ChainStyle.Packed
: El espacio se distribuye antes del primero y después del último elemento de componibilidad, que se empaquetan sin espacio entre sí.
Más información
Obtén más información sobre ConstraintLayout
en Compose a partir del funcionamiento de las APIs en los ejemplos de Compose que usan ConstraintLayout
.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Enfoque en Compose
- Kotlin para Jetpack Compose
- Conceptos básicos sobre el diseño de Compose