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 .
Поскольку измерение и размещение являются отдельными подэтапами этапа компоновки, любые изменения, которые влияют только на размещение элементов, а не на измерения, могут быть выполнены отдельно.
Использование модификаторов в макетах
Как обсуждалось в разделе Модификаторы 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
. Ограничения измерений можно найти в области действия лямбда-функции контента. Вы можете использовать эти ограничения измерений для составления различных макетов для различных конфигураций экрана:
@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
.
Компоненты материала активно используют 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
- Материальные компоненты и макеты
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 .
Поскольку измерение и размещение являются отдельными подэтапами этапа компоновки, любые изменения, которые влияют только на размещение элементов, а не на измерения, могут быть выполнены отдельно.
Использование модификаторов в макетах
Как обсуждалось в разделе Модификаторы 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
. Ограничения измерений можно найти в области действия лямбда-функции контента. Вы можете использовать эти ограничения измерений для составления различных макетов для различных конфигураций экрана:
@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
.
Компоненты материала активно используют 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
- Материальные компоненты и макеты