ConstraintLayout – это макет, который позволяет размещать композиции относительно других композиций на экране. Он позволяет не использовать несколько вложенных элементов Row, Column, Box и других элементов макета.
В системе View ConstraintLayout был рекомендованным способом создания больших и сложных макетов, поскольку плоская иерархия View лучше подходит для повышения производительности, чем вложенные представления. Однако в Compose это не проблема, поскольку он эффективно обрабатывает глубокие иерархии макетов, поэтому ConstraintLayout не так полезен.
Настроить ConstraintLayout
Чтобы использовать ConstraintLayout в Compose, добавьте эту зависимость в файл build.gradle (помимо настройки Compose):
implementation "androidx.constraintlayout:constraintlayout-compose:$constraintlayout_compose_version"
ConstraintLayout в Compose работает следующим образом, используя DSL:
- Создайте ссылки для каждого компонента в
ConstraintLayout, используяcreateRefs()илиcreateRefFor(). - Ограничения задаются с помощью модификатора
constrainAs(), который принимает ссылку в качестве параметра и позволяет указать ограничения в теле лямбда-функции. - Ограничения задаются с помощью
linkTo()или других полезных методов. parent– это существующая ссылка, которую можно использовать, чтобы задать ограничения для самой composable-функцииConstraintLayout.
Вот пример composable-функции, в которой используется 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) } ) } }
Этот код ограничивает верхнюю часть элемента Button родительским элементом с отступом 16.dp, а нижнюю часть элемента Text – нижней частью элемента Button с отступом 16.dp.
Button и Text, связанные друг с другом в ConstraintLayout.
Decoupled API
В примере с ConstraintLayout ограничения задаются встроенным образом с помощью модификатора в composable-функции, к которой они применяются. Однако в некоторых случаях лучше отделить ограничения от макетов, к которым они применяются.
Например, вы можете изменить ограничения в зависимости от конфигурации экрана или анимировать переход между двумя наборами ограничений.
В таких случаях можно использовать ConstraintLayout следующим образом:
- Передайте
ConstraintSetв качестве параметра вConstraintLayout. - Чтобы назначить ссылки, созданные в
ConstraintSet, композициям, используйте модификаторlayoutId.
@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) } } }
Когда вам понадобится изменить ограничения, просто передайте другой объект ConstraintSet.
ConstraintLayout понятий
ConstraintLayout содержит такие концепции, как направляющие, барьеры и цепочки, которые помогают размещать элементы внутри композиции.
Правила
Направляющие – это небольшие визуальные элементы, которые помогают создавать макеты. Компонуемые функции можно привязать к направляющей. Направляющие линии помогают размещать элементы в определенной dp или percentage внутри родительского компонента.
Существует два типа направляющих: вертикальные и горизонтальные. Горизонтальные стрелки – top и bottom, а вертикальные – start и 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) }
Чтобы создать правило, используйте createGuidelineFrom* с нужным типом правила. Это создает ссылку, которую можно использовать в блоке Modifier.constrainAs().
Барьеры
Барьеры ссылаются на несколько композиций, чтобы создать виртуальную направляющую на основе самого крайнего виджета на указанной стороне.
Чтобы создать барьер, используйте createTopBarrier() (или createBottomBarrier(), createEndBarrier(), createStartBarrier()) и укажите ссылки, которые должны составлять барьер.
ConstraintLayout { val constraintSet = ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") val topBarrier = createTopBarrier(button, text) } }
Затем барьер можно использовать в блоке Modifier.constrainAs().
Сети
Цепочки позволяют группировать объекты по одной оси (горизонтальной или вертикальной). Другую ось можно ограничить независимо.
Чтобы создать цепочку, используйте createVerticalChain или 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) } }
Затем цепочку можно использовать в блоке Modifier.constrainAs().
Цепочка может быть настроена с использованием различных ChainStyles, которые определяют, как обрабатывать пространство вокруг composable-функции, например:
ChainStyle.Spread: пространство равномерно распределяется между всеми composable-функциями, включая свободное пространство до первой и после последней composable-функции.ChainStyle.SpreadInside: пространство распределяется равномерно между всеми composable-функциями без свободного места до первой composable-функции и после последней composable-функции.ChainStyle.Packed: пробел распределяется до первого и после последнего элемента, а сами элементы располагаются без пробелов между ними.