В Compose элементы пользовательского интерфейса представлены компонуемыми функциями, которые при вызове создают фрагмент пользовательского интерфейса, который затем добавляется в дерево пользовательского интерфейса, отображаемое на экране. У каждого элемента интерфейса есть один родительский элемент и может быть несколько дочерних. Каждый элемент также находится внутри родительского элемента, заданного в виде позиции (x, y) и размера, заданного в виде width и height.
Родительские элементы определяют ограничения для дочерних. Элемент должен определить свой размер
в рамках этих ограничений. Ограничения задают минимальную и максимальную width и height элемента. Если у элемента есть дочерние элементы, он может измерить каждый из них, чтобы определить свой размер. После того как элемент определит и сообщит свой размер, он может определить, как разместить свои дочерние элементы относительно себя, как подробно описано в разделе Создание пользовательских макетов.
Размещение каждого узла в дереве пользовательского интерфейса состоит из трех этапов. Каждый узел должен:
- Измерить рост детей
- Определять размер самостоятельно
- Разместить дочерние элементы
Области действия определяют, когда можно измерять и размещать дочерние элементы.
Измерение макета может быть выполнено только во время проходов измерения и макета, а дочерний элемент может быть размещен только во время проходов макета (и только после того, как он был измерен). Благодаря областям видимости Compose, таким как MeasureScope и PlacementScope, это правило применяется во время компиляции.
Как использовать модификатор макета
Вы можете использовать модификатор layout, чтобы изменить способ измерения и размещения элемента. Модификатор принимает лямбда-выражение. Его параметры включают элемент, который вы можете измерить, передаваемый как measurable, и входящие ограничения этого составного элемента, передаваемые как constraints. Модификатор специального макета может выглядеть следующим образом:
fun Modifier.customLayoutModifier() = layout { measurable, constraints -> // ... }
Показывает на экране Text и позволяет управлять расстоянием от верхнего края до базовой линии первой строки текста. Именно это и делает модификатор paddingFromBaseline. Здесь вы реализуете его в качестве примера.
Для этого используйте модификатор layout, чтобы вручную разместить компонуемый объект на экране. Вот как будет выглядеть результат, если для верхнего поля Text задано значение 24.dp:
paddingFromBaseline.
Вот код, который позволяет задать такое расстояние:
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). - Вы указываете размер composable-функции, вызывая метод
layout(width, height), который также предоставляет лямбда-выражение, используемое для размещения обернутых элементов. В этом случае высота равна высотеTextплюсplaceableY, то естьfirstBaselineToTopминус первая базовая линияText. - Вы можете разместить элементы на экране, вызвав
placeable.placeRelative(x, y). Если обернутые элементы не размещены, они не будут видны. При написании справа налевоplaceRelativeзеркально отражает положениеx. Подробнее о направлении макета… Позицияy– этоfirstBaselineToTopминус первая базовая линия текста, поэтому первая базовая линия находится на расстоянииfirstBaselineToTopот верха.
Чтобы проверить, работает ли это, используйте модификатор в 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)) } }
Text, и предварительный просмотр.
Как создавать собственные макеты
Модификатор layout изменяет только вызывающую composable-функцию. Чтобы измерить и разместить несколько composable-функций, используйте composable-функцию Layout. Эта компонуемая функция позволяет вручную измерять и размещать дочерние элементы. Все макеты более высокого уровня, такие как Column и Row, создаются с помощью composable-функции 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 } } } }
Дочерние composable-функции ограничены входящими ограничениями Layout и размещаются на основе yPosition предыдущей composable-функции.
Вот как будет использоваться эта пользовательская composable-функция:
@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!") } }
Column.
Направление макета
Измените направление макета composable-функции, изменив локальную композицию LocalLayoutDirection.
Если вы размещаете composable-функции на экране вручную, LayoutDirection доступен в свойстве layoutDirection класса MeasureScope в модификаторе layout или composable-функции Layout.
При использовании layoutDirection размещайте composable-функции с помощью place. В отличие от метода placeRelative, метод place не зависит от направления макета (слева направо или справа налево).
Примеры использования собственных макетов
Подробнее о макетах и модификаторах рассказывается в статье Базовые макеты в Compose. Примеры специальных макетов можно найти в статье Примеры создания специальных макетов в Compose.
Подробнее…
Чтобы узнать больше о пользовательских макетах в Compose, ознакомьтесь со следующими дополнительными ресурсами.
Видео
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Внутренние измерения в макетах Compose
- Графика в Compose
- Модификаторы Compose