Модификаторы позволяют вам украшать или дополнять компонуемый объект. Модификаторы позволяют вам делать такие вещи:
- Изменить размер, макет, поведение и внешний вид компонуемого объекта
- Добавьте информацию, например метки доступности.
- Обработка пользовательского ввода
- Добавьте высокоуровневые взаимодействия, например, сделайте элемент кликабельным, прокручиваемым, перетаскиваемым или масштабируемым.
Модификаторы — это стандартные объекты 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
задает для компонуемой заливки максимальную ширину, предоставленную ей ее родителем.
Лучше всего, чтобы все ваши компонуемые элементы принимали параметр- modifier
и передавали этот модификатор своему первому потомку, который выдает UI. Это делает ваш код более пригодным для повторного использования и делает его поведение более предсказуемым и интуитивным. Для получения дополнительной информации см. руководство Compose API, Элементы принимают и уважают параметр-модификатор .
Порядок модификаторов имеет значение
Порядок функций-модификаторов имеет значение . Поскольку каждая функция вносит изменения в Modifier
, возвращаемый предыдущей функцией, последовательность влияет на конечный результат. Давайте рассмотрим пример этого:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { // rest of the implementation } }
В коде выше вся область кликабельна, включая окружающий padding, поскольку модификатор padding
был применен после модификатора clickable
. Если порядок модификаторов обратный, пространство, добавленное padding
не реагирует на ввод пользователя:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .padding(padding) .clickable(onClick = onClick) .fillMaxWidth() ) { // rest of the implementation } }
Встроенные модификаторы
Jetpack Compose предоставляет список встроенных модификаторов, которые помогут вам украсить или дополнить компонуемый объект. Вот некоторые общие модификаторы, которые вы будете использовать для настройки макетов.
padding
и size
По умолчанию макеты, предоставляемые в Compose, оборачивают своих потомков. Однако вы можете задать размер, используя модификатор size
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image(/*...*/) Column { /*...*/ } } }
Обратите внимание, что указанный вами размер может не соблюдаться, если он не удовлетворяет ограничениям, исходящим от родителя макета. Если вам требуется, чтобы компонуемый размер был фиксированным независимо от входящих ограничений, используйте модификатор 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
.
Если вы хотите добавить отступ над базовой линией текста таким образом, чтобы получить определенное расстояние от верха макета до базовой линии, используйте модификатор 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 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 performance — Defer reads as long as possible .
Безопасность области действия в Compose
В Compose есть модификаторы, которые можно использовать только при применении к потомкам определенных компонуемых объектов. Compose обеспечивает это посредством пользовательских областей.
Например, если вы хотите сделать дочерний элемент таким же большим, как родительский Box
, не влияя на размер Box
, используйте модификатор matchParentSize
. matchParentSize
доступен только в BoxScope
. Поэтому его можно использовать только для дочернего элемента внутри родительского Box
.
Безопасность области действия не позволяет добавлять модификаторы, которые не будут работать в других компонуемых объектах и областях действия, а также экономит время, затрачиваемое на пробы и ошибки.
Модификаторы области действия уведомляют родителя о некоторой информации, которую родитель должен знать о потомке. Их также часто называют модификаторами родительских данных . Их внутреннее устройство отличается от модификаторов общего назначения, но с точки зрения использования эти различия не имеют значения.
matchParentSize
в Box
Как упоминалось выше, если вы хотите, чтобы дочерний макет имел тот же размер, что и родительский Box
, не влияя на размер Box
, используйте модификатор matchParentSize
.
Обратите внимание, что matchParentSize
доступен только в области действия Box
, то есть он применяется только к прямым дочерним элементам составных элементов Box
.
В примере ниже дочерний элемент Spacer
берет свой размер из своего родительского Box
, который, в свою очередь, берет свой размер из самого большого дочернего элемента, в данном случае ArtistCard
.
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
Если бы вместо matchParentSize
использовалось fillMaxSize
, Spacer
занял бы все доступное пространство, предоставленное родительскому элементу, в свою очередь заставив родительский элемент расшириться и заполнить все доступное пространство.
weight
в Row
и Column
Как вы видели в предыдущем разделе Padding и size , по умолчанию компонуемый размер определяется содержимым, которое он оборачивает. Вы можете сделать компонуемый размер гибким в пределах его родителя, используя модификатор weight
, который доступен только в RowScope
и ColumnScope
.
Давайте возьмем Row
, содержащую два компонуемых 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) ) { /*...*/ } } }
Извлечение и повторное использование модификаторов
Несколько модификаторов могут быть объединены в цепочку для украшения или дополнения компонуемого объекта. Эта цепочка создается с помощью интерфейса Modifier
, который представляет собой упорядоченный, неизменяемый список отдельных Modifier.Elements
.
Каждый Modifier.Element
представляет индивидуальное поведение, например, поведение макета, рисования и графики, все поведение, связанное с жестами, фокусом и семантикой, а также события ввода устройства. Их порядок имеет значение: элементы-модификаторы, которые добавляются первыми, будут применены первыми.
Иногда может быть полезно повторно использовать одни и те же экземпляры цепочки модификаторов в нескольких компонуемых объектах, извлекая их в переменные и поднимая их в более высокие области видимости. Это может улучшить читаемость кода или помочь улучшить производительность вашего приложения по нескольким причинам:
- Перераспределение модификаторов не будет повторяться при перекомпоновке для компонуемых объектов, которые их используют.
- Цепочки модификаторов могут быть потенциально очень длинными и сложными, поэтому повторное использование одного и того же экземпляра цепочки может облегчить нагрузку на среду выполнения Compose при их сравнении.
- Такое извлечение способствует чистоте кода, согласованности и удобству обслуживания всей кодовой базы.
Лучшие практики повторного использования модификаторов
Создавайте собственные цепочки Modifier
и извлекайте их для повторного использования в нескольких составных компонентах. Совершенно нормально просто сохранить модификатор, поскольку они являются объектами, подобными данным:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
Извлечение и повторное использование модификаторов при наблюдении за часто меняющимся состоянием
При наблюдении за часто меняющимися состояниями внутри компонуемых объектов, таких как состояния анимации или 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 ) }
Вместо этого вы можете создать, извлечь и повторно использовать один и тот же экземпляр модификатора и передать его в составной объект следующим образом:
// 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 ) }
Извлечение и повторное использование модификаторов без области действия
Модификаторы могут быть необласть действия или область действия для определенного компонуемого объекта. В случае необласть действия модификаторов вы можете легко извлечь их за пределы любого компонуемого объекта как простые переменные:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) @Composable fun AuthorField() { HeaderText( // ... modifier = reusableModifier ) SubtitleText( // ... modifier = reusableModifier ) }
Это может быть особенно полезно в сочетании с Lazy layouts. В большинстве случаев вы захотите, чтобы все ваши потенциально значительные количества элементов имели одинаковые модификаторы:
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, ) } } }
Извлечение и повторное использование модификаторов области действия
При работе с модификаторами, область действия которых ограничена определенными составными объектами, вы можете извлечь их до максимально возможного уровня и использовать повторно при необходимости:
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 отключен.
- Основы создания макета
- Действия редактора {:#editor-actions}
- Пользовательские макеты {:#custom-layouts }