В 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
- Модификаторы сочинения