Jetpack Compose значительно упрощает проектирование и разработку пользовательского интерфейса вашего приложения. Compose преобразует состояние в элементы пользовательского интерфейса с помощью:
- Состав элементов
- Расположение элементов
- Чертеж элементов
В этом документе основное внимание уделяется компоновке элементов и объясняются некоторые строительные блоки, которые Compose предоставляет для помощи в компоновке элементов пользовательского интерфейса.
Цели макетов в Compose
Реализация системы макетов Jetpack Compose имеет две основные цели:
- Высокая производительность
- Возможность легкого написания пользовательских макетов
Основы компонуемых функций
Компонуемые функции — это базовый строительный блок Compose. Компонуемая функция — это функция, создающая Unit
, которая описывает часть вашего пользовательского интерфейса. Функция принимает входные данные и генерирует то, что отображается на экране. Подробнее о компонуемых функциях см. в документации по ментальной модели Compose .
Компонуемая функция может генерировать несколько элементов пользовательского интерфейса. Однако, если вы не дадите указаний по их расположению, Compose может расположить элементы так, как вам не понравится. Например, этот код генерирует два текстовых элемента:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
Без указания того, как вы хотите их расположить, Compose накладывает текстовые элементы друг на друга, делая их нечитаемыми:
Compose предоставляет набор готовых макетов, которые помогут вам упорядочить элементы пользовательского интерфейса, а также упрощает определение ваших собственных, более специализированных макетов.
Стандартные компоненты компоновки
Во многих случаях вы можете просто использовать стандартные элементы макета Compose .
Используйте Column
для вертикального размещения элементов на экране.
@Composable fun ArtistCardColumn() { Column { Text("Alfred Sisley") Text("3 minutes ago") } }
Аналогично, используйте Row
для горизонтального размещения элементов на экране. Как Column
, так и Row
поддерживают настройку выравнивания содержащихся в них элементов.
@Composable fun ArtistCardRow(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { Text(artist.name) Text(artist.lastSeenOnline) } } }
Используйте Box
для размещения элементов друг над другом. Box
также поддерживает настройку выравнивания содержащихся в нём элементов.
@Composable fun ArtistAvatar(artist: Artist) { Box { Image(bitmap = artist.image, contentDescription = "Artist image") Icon(Icons.Filled.Check, contentDescription = "Check mark") } }
Часто этих стандартных блоков достаточно. Вы можете написать собственную функцию компоновки, чтобы объединить эти макеты в более сложный макет, подходящий для вашего приложения.
Чтобы задать положение дочерних элементов в Row
, задайте аргументы horizontalArrangement
и verticalAlignment
. Для Column
задайте аргументы verticalArrangement
и horizontalAlignment
:
@Composable fun ArtistCardArrangement(artist: Artist) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { /*...*/ } } }
Модель компоновки
В модели макета дерево пользовательского интерфейса размечается за один проход. Сначала каждому узлу предлагается измерить себя, затем рекурсивно измерить все дочерние элементы, передавая ограничения по размеру вниз по дереву. Затем определяются размеры и размещаются листовые узлы, а определённые размеры и инструкции по размещению передаются обратно вверх по дереву.
Коротко говоря, родители снимают мерки раньше своих детей, но их размеры и размещение определяются после детей.
Рассмотрим следующую функцию SearchResult
.
@Composable fun SearchResult() { Row { Image( // ... ) Column { Text( // ... ) Text( // ... ) } } }
Эта функция создает следующее дерево пользовательского интерфейса.
SearchResult
Row
Image
Column
Text
Text
В примере SearchResult
структура дерева пользовательского интерфейса имеет следующий порядок:
- Корневой узел
Row
просят измерить. - Корневой узел
Row
запрашивает у своего первого дочернего узлаImage
выполнение измерений. -
Image
является конечным узлом (т.е. не имеет дочерних элементов), поэтому оно сообщает размер и возвращает инструкции по размещению. - Корневой узел
Row
запрашивает у своего второго дочернего узлаColumn
измерение. - Узел
Column
запрашивает у своего первого дочернего элементаText
измерение. - Первый
Text
узел является конечным узлом, поэтому он сообщает размер и возвращает инструкции по размещению. - Узел
Column
запрашивает у своего второго дочернего элементаText
измерение. - Второй узел
Text
является конечным узлом, поэтому он сообщает размер и возвращает инструкции по размещению. - Теперь, когда узел
Column
измерил, определил размер и разместил свои дочерние элементы, он может определить свой собственный размер и размещение. - Теперь, когда корневой узел
Row
измерил, определил размер и разместил своих дочерних элементов, он может определить свой собственный размер и размещение.
Производительность
Compose достигает высокой производительности, измеряя дочерние элементы только один раз. Однопроходное измерение положительно влияет на производительность, позволяя Compose эффективно обрабатывать глубокие древовидные структуры пользовательского интерфейса. Если элемент измеряет свой дочерний элемент дважды, а этот дочерний элемент измеряет каждый из своих дочерних элементов дважды и так далее, то для одной попытки компоновки всего пользовательского интерфейса потребуется выполнить большой объем работы, что затруднит поддержание производительности приложения.
Если по какой-то причине вашему макету требуется несколько измерений, Compose предлагает специальную систему — внутренние измерения . Подробнее об этой функции можно узнать в статье «Внутренние измерения в макетах Compose» .
Поскольку измерение и размещение являются отдельными подэтапами этапа компоновки, любые изменения, которые влияют только на размещение элементов, а не на измерения, могут быть выполнены отдельно.
Использование модификаторов в макетах
Как обсуждалось в разделе «Модификаторы композиции» , вы можете использовать модификаторы для украшения или дополнения своих компонуемых элементов. Модификаторы необходимы для настройки макета. Например, здесь мы объединяем несколько модификаторов для настройки ArtistCard
:
@Composable fun ArtistCardModifiers( artist: Artist, onClick: () -> Unit ) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ } Spacer(Modifier.size(padding)) Card( elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), ) { /*...*/ } } }
В приведенном выше коде обратите внимание на совместное использование различных функций-модификаторов.
-
clickable
создает реакцию на пользовательский ввод и отображает рябь. -
padding
создает пространство вокруг элемента. -
fillMaxWidth
задает для компонуемой заливки максимальную ширину, предоставленную ей родителем. -
size()
определяет предпочтительную ширину и высоту элемента.
Прокручиваемые макеты
Дополнительную информацию о прокручиваемых макетах можно найти в документации по жестам создания сообщений .
Информацию о списках и ленивых списках см. в документации по составлению списков .
Адаптивные макеты
Макет должен быть разработан с учётом различных ориентаций экрана и размеров форм-фактора. Compose предлагает несколько встроенных механизмов для адаптации компонуемых макетов к различным конфигурациям экрана.
Ограничения
Чтобы узнать ограничения, накладываемые родительским элементом, и спроектировать макет соответствующим образом, можно использовать BoxWithConstraints
. Ограничения измерений можно найти в области действия лямбда-функции content. Эти ограничения измерений можно использовать для создания различных макетов для разных конфигураций экрана:
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
Макеты на основе слотов
Compose предоставляет широкий выбор компонуемых элементов на основе Material Design с зависимостью androidx.compose.material:material
(включаемой при создании проекта Compose в Android Studio) для упрощения разработки пользовательского интерфейса. Доступны такие элементы, как Drawer
, FloatingActionButton
и TopAppBar
.
Компоненты Material активно используют API слотов — шаблон, который Compose внедряет для создания уровня настройки поверх компонуемых элементов. Такой подход делает компоненты более гибкими, поскольку они принимают дочерний элемент, который может настраиваться самостоятельно, вместо того, чтобы предоставлять доступ ко всем параметрам конфигурации дочернего элемента. Слоты оставляют пустое пространство в пользовательском интерфейсе, которое разработчик может заполнить по своему усмотрению. Например, вот слоты, которые можно настроить в TopAppBar
:
Компонуемые элементы обычно принимают лямбда-функцию компонуемого content
( content: @Composable () -> Unit
). API слотов предоставляют несколько параметров content
для определённых целей. Например, TopAppBar
позволяет указать контент для title
, navigationIcon
и actions
.
Например, Scaffold
позволяет реализовать пользовательский интерфейс с базовой структурой макета Material Design. Scaffold
предоставляет слоты для наиболее распространённых компонентов Material верхнего уровня, таких как TopAppBar
, BottomAppBar
, FloatingActionButton
и Drawer
. Используя Scaffold
, можно легко обеспечить правильное расположение этих компонентов и их корректную совместную работу.
@Composable fun HomeScreen(/*...*/) { ModalNavigationDrawer(drawerContent = { /* ... */ }) { Scaffold( topBar = { /*...*/ } ) { contentPadding -> // ... } } }
Рекомендовано для вас
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Модификаторы сочинения
- Kotlin для Jetpack Compose
- Компоненты материалов и макеты