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