ConstraintLayout
— это макет, позволяющий размещать компонуемые элементы относительно других компонуемых элементов на экране. Это альтернатива использованию нескольких вложенных Row
, Column
, Box
и других пользовательских элементов макета . ConstraintLayout
полезен при реализации более крупных макетов с более сложными требованиями к выравниванию.
Рассмотрите возможность использования ConstraintLayout
в следующих сценариях:
- Чтобы избежать вложения нескольких
Column
иRow
для позиционирования элементов на экране и улучшить читаемость кода. - Для позиционирования компонуемых объектов относительно других компонуемых объектов или для позиционирования компонуемых объектов на основе направляющих, барьеров или цепей.
В системе View ConstraintLayout
был рекомендуемым способом создания больших и сложных макетов, поскольку плоская иерархия представлений обеспечивала лучшую производительность, чем вложенные представления. Однако в Compose это не проблема, поскольку он способен эффективно обрабатывать глубокие иерархии макетов.
Начните работу с ConstraintLayout
Чтобы использовать ConstraintLayout
в Compose, вам необходимо добавить эту зависимость в ваш build.gradle
(в дополнение к настройке Compose ):
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
ConstraintLayout
в Compose работает следующим образом с использованием DSL :
- Создайте ссылки для каждого компонуемого объекта в
ConstraintLayout
с помощьюcreateRefs()
илиcreateRefFor()
- Ограничения предоставляются с помощью модификатора
constrainAs()
, который принимает ссылку как параметр и позволяет указать ее ограничения в лямбда-выражении тела. - Ограничения задаются с помощью
linkTo()
или других полезных методов. -
parent
— существующая ссылка, которая может использоваться для указания ограничений для самого компонуемого объектаConstraintLayout
.
Вот пример компонуемого объекта с использованием 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
Разделенный API
В примере ConstraintLayout
ограничения задаются в строке, с модификатором в компонуемом элементе, к которому они применяются. Однако бывают ситуации, когда предпочтительнее отделить ограничения от макетов, к которым они применяются. Например, может потребоваться изменить ограничения в зависимости от конфигурации экрана или реализовать анимацию между двумя наборами ограничений.
В подобных случаях можно использовать 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
содержит такие концепции, как направляющие, барьеры и цепи, которые могут помочь в позиционировании элементов внутри Composable.
Руководящие принципы
Направляющие — это небольшие визуальные помощники для проектирования макетов. Компонуемые элементы можно ограничить направляющей. Направляющие полезны для позиционирования элементов на определённом 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
, которые определяют, как работать с пространством вокруг компонуемого элемента, например:
-
ChainStyle.Spread
: Пространство равномерно распределяется по всем компонуемым элементам, включая свободное пространство перед первым компонуемым элементом и после последнего компонуемого элемента. -
ChainStyle.SpreadInside
: Пространство равномерно распределяется по всем компонуемым элементам, без свободного пространства перед первым компонуемым элементом или после последнего компонуемого элемента. -
ChainStyle.Packed
: Пространство распределяется перед первым и после последнего компонуемого элемента, компонуемые элементы упаковываются вместе без пробелов между ними.
Узнать больше
Узнайте больше о ConstraintLayout
в Compose из API-интерфейсов, используемых в примерах Compose, которые используют ConstraintLayout
.
Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Фокус в написании
- Kotlin для Jetpack Compose
- Основы создания макета