В Compose элементы пользовательского интерфейса представлены компонуемыми функциями, которые при вызове генерируют фрагмент пользовательского интерфейса, который затем добавляется в дерево пользовательского интерфейса, отображаемое на экране. Каждый элемент пользовательского интерфейса имеет одного родительского элемента и потенциально множество дочерних элементов. Каждый элемент также располагается внутри своего родительского элемента, указывая его координату (x, y) и размер, определяемый параметрами width
и height
.
Родительские элементы определяют ограничения для своих дочерних элементов. Элементу предлагается определить свой размер в рамках этих ограничений. Ограничения ограничивают минимальную и максимальную width
и height
элемента. Если у элемента есть дочерние элементы, он может измерить каждый из них, чтобы определить его размер. После того, как элемент определит и сообщит свой размер, он сможет определить, как размещать свои дочерние элементы относительно себя, как подробно описано в разделе Создание пользовательских макетов .
Разметка каждого узла в дереве пользовательского интерфейса выполняется в три этапа. Каждый узел должен:
- Измерьте всех детей
- Определите свой собственный размер
- Поместите его детей
Использование областей действия определяет, когда можно измерять и размещать дочерние элементы. Измерение макета может выполняться только во время проходов измерения и макета, а размещение дочернего элемента возможно только во время проходов макета (и только после его измерения). Благодаря таким областям действия Compose, как MeasureScope
и PlacementScope
, это обеспечивается во время компиляции.
Используйте модификатор макета
Вы можете использовать модификатор layout
для изменения способа измерения и расположения элемента. Layout
— это лямбда-выражение; его параметры включают измеряемый элемент, передаваемый как measurable
, и входящие ограничения этого компонуемого элемента, передаваемые как constraints
. Пользовательский модификатор макета может выглядеть следующим образом:
fun Modifier.customLayoutModifier() = layout { measurable, constraints -> // ... }
Давайте выведем Text
на экран и настроим расстояние от его верхней границы до базовой линии первой строки. Именно это и делает модификатор paddingFromBaseline
, мы реализуем его здесь в качестве примера. Для этого используйте модификатор layout
, чтобы вручную разместить компонуемый элемент на экране. Вот желаемое поведение, если верхний отступ Text
установлен равным 24.dp
:
Вот код для создания такого интервала:
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) } }
Вот что происходит в этом коде:
- В
measurable
лямбда-параметре вы измеряетеText
, представленный измеряемым параметром, путем вызоваmeasurable.measure(constraints)
. - Размер компонуемого элемента задаётся вызовом метода
layout(width, height)
, который также возвращает лямбда-выражение, используемое для размещения обёрнутых элементов. В данном случае это высота между последней базовой линией и добавленным верхним отступом. - Обёрнутые элементы размещаются на экране с помощью метода
placeable.place(x, y)
. Если обёрнутые элементы не размещены, они не будут видны. Положение по осиy
соответствует верхнему отступу — положению первой базовой линии текста.
Чтобы проверить, что это работает так, как и ожидалось, используйте этот модификатор в Text
:
@Preview @Composable fun TextWithPaddingToBaselinePreview() { MyApplicationTheme { Text("Hi there!", Modifier.firstBaselineToTop(32.dp)) } } @Preview @Composable fun TextWithNormalPaddingPreview() { MyApplicationTheme { Text("Hi there!", Modifier.padding(top = 32.dp)) } }
Создавайте индивидуальные макеты
Модификатор layout
изменяет только вызывающий компонуемый объект. Для измерения и компоновки нескольких компонуемых объектов используйте компонуемый объект Layout
. Этот компонуемый объект позволяет вручную измерять и компоновать дочерние элементы. Все макеты более высокого уровня, такие как Column
и Row
, создаются с помощью компонуемого Layout
.
Давайте создадим самую простую версию Column
. Большинство пользовательских макетов следуют этому шаблону:
@Composable fun MyBasicColumn( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurables, constraints -> // measure and position children given constraints logic here // ... } }
Аналогично модификатору layout
, measurables
— это список дочерних элементов, которые необходимо измерить, а constraints
— это ограничения родительского элемента. Следуя той же логике, что и раньше, MyBasicColumn
можно реализовать следующим образом:
@Composable fun MyBasicColumn( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurables, constraints -> // Don't constrain child views further, measure them with given constraints // List of measured children val placeables = measurables.map { measurable -> // Measure each children measurable.measure(constraints) } // Set the size of the layout as big as it can layout(constraints.maxWidth, constraints.maxHeight) { // Track the y co-ord we have placed children up to var yPosition = 0 // Place children in the parent layout placeables.forEach { placeable -> // Position item on the screen placeable.placeRelative(x = 0, y = yPosition) // Record the y co-ord placed up to yPosition += placeable.height } } } }
Дочерние компонуемые элементы ограничены ограничениями Layout
(без ограничений minHeight
) и размещаются на основе yPosition
предыдущего компонуемого элемента.
Вот как будет использоваться этот индивидуальный составной элемент:
@Composable fun CallingComposable(modifier: Modifier = Modifier) { MyBasicColumn(modifier.padding(8.dp)) { Text("MyBasicColumn") Text("places items") Text("vertically.") Text("We've done it by hand!") } }
Направление макета
Измените направление макета компонуемого элемента, изменив локальное свойство композиции LocalLayoutDirection
.
Если вы размещаете компонуемые элементы на экране вручную, LayoutDirection
является частью LayoutScope
модификатора layout
или компонуемого элемента Layout
.
При использовании layoutDirection
размещайте компонуемые элементы с помощью place
. В отличие от метода placeRelative
, place
не меняется в зависимости от направления макета (слева направо или справа налево).
Пользовательские макеты в действии
Узнайте больше о макетах и модификаторах в разделе «Основные макеты» в Compose и посмотрите на пользовательские макеты в действии в примерах Compose, которые создают пользовательские макеты .
Узнать больше
Чтобы узнать больше о пользовательских макетах в Compose, ознакомьтесь со следующими дополнительными ресурсами.
Видео
{% дословно %}Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Внутренние измерения в макетах Compose
- Графика в Compose
- Модификаторы сочинения