В 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
- Создание модификаторов