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