Модификаторы позволяют украсить или дополнить компонуемый объект. Модификаторы позволяют делать такие вещи:
- Изменение размера, макета, поведения и внешнего вида composable-функции
- Добавьте информацию, например ярлыки специальных возможностей
- Обработка данных, введенных пользователем
- Добавьте высокоуровневые взаимодействия, например, сделайте элемент кликабельным, прокручиваемым, перетаскиваемым или масштабируемым.
Модификаторы – это стандартные объекты Kotlin. Создайте модификатор, вызвав одну из функций класса Modifier:
@Composable private fun Greeting(name: String) { Column(modifier = Modifier.padding(24.dp)) { Text(text = "Hello,") Text(text = name) } }
Вы можете объединить эти функции в цепочку, чтобы составить их:
@Composable private fun Greeting(name: String) { Column( modifier = Modifier .padding(24.dp) .fillMaxWidth() ) { Text(text = "Hello,") Text(text = name) } }
В коде выше обратите внимание на разные функции модификаторов, используемые вместе.
paddingдобавляет пространство вокруг элемента.fillMaxWidthзаставляет composable-функцию заполнять максимальную ширину, заданную родительским элементом.
Рекомендуется, чтобы все composable-функции принимали параметр modifier и передавали этот модификатор первому дочернему элементу, который создает пользовательский интерфейс.
Это делает ваш код более пригодным для повторного использования, а его поведение более предсказуемым и интуитивно понятным. Подробную информацию можно найти в руководстве по Compose API, в разделе Элементы принимают и учитывают параметр Modifier.
Порядок модификаторов имеет значение
Порядок функций-модификаторов имеет значение. Поскольку каждая функция вносит изменения в Modifier, возвращенный предыдущей функцией, последовательность влияет на конечный результат. Рассмотрим пример.
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { // rest of the implementation } }
В приведенном выше коде вся область является кликабельной, включая окружающие отступы, поскольку модификатор padding был применен после модификатора clickable. Если порядок модификаторов обратный, то пробел, добавленный padding, не реагирует на ввод пользователя:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .padding(padding) .clickable(onClick = onClick) .fillMaxWidth() ) { // rest of the implementation } }
Встроенные модификаторы
Jetpack Compose предоставляет список встроенных модификаторов, которые помогут вам украсить или дополнить composable-функцию. Вот некоторые распространенные модификаторы, которые вы будете использовать для настройки макетов.
padding и size
По умолчанию макеты, предоставляемые в Compose, оборачивают свои дочерние элементы. Однако вы можете задать размер с помощью модификатора size:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image(/*...*/) Column { /*...*/ } } }
Обратите внимание, что указанный вами размер может не соблюдаться, если он не соответствует ограничениям, накладываемым родительским элементом макета. Если вам нужно, чтобы размер composable-функции был фиксированным независимо от входящих ограничений, используйте модификатор requiredSize:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.requiredSize(150.dp) ) Column { /*...*/ } } }
В этом примере, даже если для родительского элемента height задано значение 100.dp, высота Image будет равна 150.dp, поскольку модификатор requiredSize имеет приоритет.
Если вы хотите, чтобы дочерний макет заполнил всю доступную высоту, разрешенную родительским макетом, добавьте модификатор fillMaxHeight (в Compose также есть fillMaxSize и fillMaxWidth):
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.fillMaxHeight() ) Column { /*...*/ } } }
Чтобы добавить отступы вокруг элемента, задайте модификатор padding.
Параметры start и end функции padding зависят от направления макета. Чтобы добавить отступы слева или справа независимо от направления макета, используйте absolutePadding с параметрами left и right.
Если вы хотите добавить отступ над базовой линией текста, чтобы получить определенное расстояние от верхней части макета до базовой линии, используйте модификатор paddingFromBaseline:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text( text = artist.name, modifier = Modifier.paddingFromBaseline(top = 50.dp) ) Text(artist.lastSeenOnline) } } }
Смещение
Чтобы разместить макет относительно его исходного положения, добавьте модификатор offset и задайте смещение по осям x и y.
Смещения могут быть как положительными, так и отрицательными. Разница между padding и offset заключается в том, что добавление offset к composable-функции не меняет ее размеры.
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text(artist.name) Text( text = artist.lastSeenOnline, modifier = Modifier.offset(x = 4.dp) ) } } }
Модификатор offset применяется по горизонтали в соответствии с направлением макета.
В контексте слева направо положительное значение offset сдвигает элемент вправо, а в контексте справа налево – влево.
Если вам нужно задать смещение без учета направления макета, используйте модификатор absoluteOffset, в котором положительное значение смещения всегда сдвигает элемент вправо.
Модификатор offset предоставляет две перегрузки: offset, которая принимает смещения в качестве параметров, и offset, которая принимает лямбда-выражение.
Подробную информацию о том, когда использовать каждый из этих вариантов и как оптимизировать производительность, можно найти в разделе Производительность Compose – откладывайте чтение как можно дольше.
Безопасность области видимости в Compose
В Compose есть модификаторы, которые можно применять только к дочерним элементам определенных composable-функций. Compose обеспечивает это с помощью пользовательских областей видимости.
Например, если вы хотите сделать дочерний элемент таким же большим, как родительский Box, не влияя на размер Box, используйте модификатор matchParentSize. Функция "matchParentSize" доступна только в BoxScope.
Поэтому его можно использовать только для дочернего элемента, у которого родительским является Box.
Безопасность области видимости не позволяет добавлять модификаторы, которые не будут работать в других composable-функциях и областях видимости, и экономит время на пробах и ошибках.
Модификаторы области видимости уведомляют родительский элемент о том, что ему нужно знать о дочернем. Их также называют модификаторами родительских данных. Их внутреннее устройство отличается от модификаторов общего назначения, но с точки зрения использования эти различия не имеют значения.
matchParentSize (Box)
Как упоминалось выше, если вы хотите, чтобы дочерний макет был того же размера, что и родительский Box, не влияя на размер Box, используйте модификатор matchParentSize.
Обратите внимание, что matchParentSize доступен только в области действия Box, то есть применяется только к прямым дочерним элементам composable-функций Box.
В примере ниже дочерний элемент Spacer берет размер у родительского элемента Box, который, в свою очередь, берет размер у самого большого дочернего элемента, в данном случае ArtistCard.
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
Если бы вместо matchParentSize использовался fillMaxSize, то Spacer занял бы все доступное пространство, разрешенное для родительского элемента, что, в свою очередь, привело бы к расширению родительского элемента и заполнению всего доступного пространства.
weight в сервисах "Row" и Column
Как вы видели в предыдущем разделе Поля и размер, по умолчанию размер composable-функции определяется контентом, который она оборачивает. Вы можете задать гибкий размер composable-функции в родительском элементе с помощью модификатора weight, который доступен только в RowScope и ColumnScope.
Возьмем Row, содержащий две composable-функции Box.
Первому блоку задано значение weight в два раза больше, чем второму, поэтому он в два раза шире. Поскольку ширина Row составляет 210.dp, ширина первого Box равна 140.dp, а второго – 70.dp:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.fillMaxWidth() ) { Image( /*...*/ modifier = Modifier.weight(2f) ) Column( modifier = Modifier.weight(1f) ) { /*...*/ } } }
Извлечение и повторное использование модификаторов
Несколько модификаторов можно объединить в цепочку, чтобы украсить или дополнить composable-функцию. Эта цепочка создается с помощью интерфейса Modifier, который представляет собой упорядоченный неизменяемый список отдельных объектов Modifier.Elements.
Каждый Modifier.Element представляет собой отдельное поведение, например поведение макета, рисования и графики, все поведение, связанное с жестами, фокусом и семантикой, а также события ввода с устройства. Порядок элементов имеет значение: элементы-модификаторы, добавленные первыми, будут применены первыми.
Иногда может быть полезно повторно использовать одни и те же экземпляры цепочки модификаторов в нескольких составных объектах, извлекая их в переменные и поднимая в более высокие области видимости. Это может улучшить читаемость кода или повысить производительность приложения по нескольким причинам:
- Повторное выделение модификаторов не будет повторяться при рекомпозиции для использующих их composable-функций.
- Цепочки модификаторов могут быть очень длинными и сложными, поэтому повторное использование одного и того же экземпляра цепочки может снизить рабочую нагрузку, которую среда выполнения Compose должна выполнять при их сравнении.
- Это позволяет поддерживать чистоту, согласованность и удобство обслуживания базы кода.
Рекомендации по повторному использованию модификаторов
Создавайте собственные цепочки Modifier и извлекайте их, чтобы повторно использовать в нескольких составных компонентах. Вы можете просто сохранить модификатор, так как это объект, похожий на данные:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
Извлечение и повторное использование модификаторов при наблюдении за часто меняющимся состоянием
При наблюдении за часто меняющимися состояниями внутри composable-функций, например состояниями анимации или scrollState, может выполняться значительное количество рекомпозиций. В этом случае ваши модификаторы будут выделяться при каждой рекомпозиции.
и потенциально для каждого кадра:
@Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // Creation and allocation of this modifier will happen on every frame of the animation! modifier = Modifier .padding(12.dp) .background(Color.Gray), animatedState = animatedState ) }
Вместо этого вы можете создать, извлечь и повторно использовать один и тот же экземпляр модификатора и передать его в composable-функцию следующим образом:
// Now, the allocation of the modifier happens here: val reusableModifier = Modifier .padding(12.dp) .background(Color.Gray) @Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // No allocation, as we're just reusing the same instance modifier = reusableModifier, animatedState = animatedState ) }
Извлечение и повторное использование модификаторов без области видимости
Модификаторы могут быть без области видимости или с областью видимости для конкретной composable-функции. Модификаторы без области видимости можно легко извлечь за пределы composable-функций в виде простых переменных:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) @Composable fun AuthorField() { HeaderText( // ... modifier = reusableModifier ) SubtitleText( // ... modifier = reusableModifier ) }
Это может быть особенно полезно в сочетании с ленивыми макетами. В большинстве случаев вы захотите, чтобы все ваши, потенциально значительные, товары имели одинаковые модификаторы:
val reusableItemModifier = Modifier .padding(bottom = 12.dp) .size(216.dp) .clip(CircleShape) @Composable private fun AuthorList(authors: List<Author>) { LazyColumn { items(authors) { AsyncImage( // ... modifier = reusableItemModifier, ) } } }
Извлечение и повторное использование модификаторов с ограниченной областью действия
Если модификаторы ограничены определенными composable-функциями, вы можете извлечь их на максимально высокий уровень и повторно использовать там, где это необходимо.
Column(/*...*/) { val reusableItemModifier = Modifier .padding(bottom = 12.dp) // Align Modifier.Element requires a ColumnScope .align(Alignment.CenterHorizontally) .weight(1f) Text1( modifier = reusableItemModifier, // ... ) Text2( modifier = reusableItemModifier // ... ) // ... }
Извлеченные модификаторы с заданной областью действия следует передавать только прямым дочерним элементам с той же областью действия. Подробнее о том, почему это важно, рассказывается в разделе Безопасность области видимости в Compose.
Column(modifier = Modifier.fillMaxWidth()) { // Weight modifier is scoped to the Column composable val reusableItemModifier = Modifier.weight(1f) // Weight will be properly assigned here since this Text is a direct child of Column Text1( modifier = reusableItemModifier // ... ) Box { Text2( // Weight won't do anything here since the Text composable is not a direct child of Column modifier = reusableItemModifier // ... ) } }
Дальнейшее объединение извлеченных модификаторов
Вы можете объединять или добавлять извлеченные цепочки модификаторов, вызывая функцию .then():
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) // Append to your reusableModifier reusableModifier.clickable { /*...*/ } // Append your reusableModifier otherModifier.then(reusableModifier)
Помните, что порядок модификаторов имеет значение.
Подробнее…
Ознакомьтесь с полным списком модификаторов, их параметрами и областями действия.
Чтобы попрактиковаться в использовании модификаторов, вы также можете пройти практическую работу по базовым макетам в Compose или обратиться к репозиторию Now in Android.
Дополнительную информацию о пользовательских модификаторах и способах их создания можно найти в документации Пользовательские макеты – использование модификатора макета.
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Основы макета Compose
- Действия в редакторе {:#editor-actions}
- Собственные макеты {:#custom-layouts }