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, которые используют ConstraintLayout
.
Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Фокус в Compose
- Kotlin для Jetpack Compose
- Основы создания макета