Списки позволяют пользователям выбирать элемент из предложенного набора вариантов на устройствах Wear OS.
Многие устройства Wear OS используют круглые экраны, что затрудняет просмотр элементов списка, расположенных в верхней и нижней части экрана. По этой причине Compose для Wear OS включает версию класса LazyColumn под названием TransformingLazyColumn , которая поддерживает анимацию масштабирования и морфинга. Когда элементы перемещаются к краям, они уменьшаются в размере и исчезают.
Для применения рекомендуемых эффектов масштабирования и прокрутки:
- Используйте
Modifier.transformedHeight, чтобы Compose автоматически рассчитывал изменение высоты элемента при прокрутке экрана. - Используйте
transformation = SurfaceTransformation(transformationSpec)для применения визуальных эффектов, включая уменьшение масштаба содержимого элемента. - Для компонентов, которые не принимают
transformationв качестве параметра, например, дляText, используйте собственныйTransformationSpec.
Следующая анимация показывает, как элемент списка масштабируется и меняет форму при приближении к верхней и нижней части экрана:
Приведённый ниже фрагмент кода демонстрирует, как создать список с использованием макетаTransformingLazyColumn для отображения контента, который отлично выглядит на экранах Wear OS различных размеров . В приведенном фрагменте кода также демонстрируется использование модификатора minimumVerticalContentPadding , который следует установить для элементов списка, чтобы применить правильный отступ вверху и внизу списка.
Чтобы отобразить индикатор прокрутки, используйте общее columnState для ScreenScaffold и TransformingLazyColumn :
val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold( scrollState = columnState ) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding ) { item { ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) { Text(text = "Header") } } // ... other items item { Button( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding), transformation = SurfaceTransformation(transformationSpec), onClick = { /* ... */ }, icon = { Icon( imageVector = Icons.Default.Build, contentDescription = "build", ) }, ) { Text( text = "Build", maxLines = 1, overflow = TextOverflow.Ellipsis, ) } } } }
Добавьте эффект хлопка и броска.
Функция привязки гарантирует, что после завершения прокрутки или жеста «взмах» список стабилизируется с элементом, точно расположенным в определенной точке, обычно в центре экрана. На круглых экранах, где элементы масштабируются и меняются по мере удаления от центра, функция привязки особенно полезна для обеспечения того, чтобы наиболее важный элемент оставался полностью видимым и читаемым в оптимальной области просмотра. Чтобы добавить эффект «защелкивания и запуска», установите параметр flingBehavior в значение TransformingLazyColumnDefaults.snapFlingBehavior(columnState) . Установите параметр rotaryScrollableBehavior соответствующим образом, используя RotaryScrollableDefaults.snapBehavior(columnState) для обеспечения единообразного взаимодействия при использовании физической заводной головки или безеля.
val columnState = rememberTransformingLazyColumnState() ScreenScaffold(scrollState = columnState) { contentPadding -> TransformingLazyColumn( state = columnState, flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState), rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState) ) { // ... // ... } }
Обратная компоновка
По умолчанию прокручиваемый список привязывается к своему верхнему краю. Если пользователь прокрутил стандартный список до конца и в конец добавлен новый элемент, список сохраняет фокус на текущем элементе. Например, если пользователь видит элемент 10 внизу экрана, и добавлен элемент 11, фокус остается на элементе 10, а элемент 11 отображается за пределами экрана, ниже текущего элемента.
В таких сценариях использования, как мессенджеры или журналы событий в реальном времени, такое поведение обычно нежелательно. Когда появляются новые элементы, пользователи, как правило, хотят сразу видеть самый свежий контент, если они уже находятся в конце списка. Если же одновременно появляется много элементов, список должен пропускать этот шаг и отображать самый последний элемент внизу (это означает, что некоторые промежуточные элементы могут вообще не отображаться, если пользователь не прокрутит список вверх).
Для поддержки этих сценариев использования TransformingLazyColumn позволяет изменить расположение элементов, установив reverseLayout = true . Это изменяет привязку списка с верхнего края на нижний.
Для удобства установка параметра reverseLayout = true также меняет визуальный порядок элементов и направление прокрутки:
- Элементы располагаются снизу вверх, то есть элемент с индексом 0 находится в нижней части экрана.
- Прокрутка вверх отображает элементы с более высокими индексами.
Чтобы добавить эффект «привязки и прокрутки» вместе с обратной компоновкой, можно объединить flingBehavior и rotaryScrollableBehavior как показано в следующем фрагменте кода:
val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold(scrollState = columnState) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding, reverseLayout = true, modifier = Modifier.fillMaxWidth() ) { items(10) { index -> Button( label = { Text( text = "Item ${index + 1}" ) }, onClick = {}, modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) } item { // With reverseLayout = true, the last item declared appears at the top. ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) { Text("Header") } } } }
На следующих изображениях показана разница между обычным списком и перевернутым списком:


Краевые кнопки в списках
Для Material 3 можно добавить EdgeButton — кнопку, примыкающую к краю списка. Однако следует избегать добавления этого элемента в TransformingLazyColumn , а использовать слот edgeButton в ScreenScaffold .
Использование слота edgeButton гарантирует правильное расположение кнопки в нижней части экрана и корректное поведение при прокрутке списка.
Следующий фрагмент кода демонстрирует, как реализовать EdgeButton :
val columnState = rememberTransformingLazyColumnState() ScreenScaffold( scrollState = columnState, edgeButton = { EdgeButton( onClick = { /* TODO */ }, modifier = Modifier.scrollable( columnState, orientation = Orientation.Vertical, reverseDirection = true, // Apply overscroll to the EdgeButton for proper scrolling behavior. overscrollEffect = rememberOverscrollEffect(), ) ) { Text("More") } } ) { contentPadding -> TransformingLazyColumn( contentPadding = contentPadding, state = columnState, ) { // ... // ... } }
SwipeToReveal в списках
Компонент SwipeToReveal позволяет получать доступ к действиям для элемента списка, например, Card или Chip , с помощью свайпа. Свайп обычно отображает одну или две кнопки действий (например, «Удалить» или «Ещё») сбоку.
При использовании SwipeToReveal внутри элемента TransformingLazyColumn следуйте этим рекомендациям:
- Сброс при прокрутке : Когда пользователь прокручивает список, сбрасывайте все открытые элементы в закрытое состояние.
- Единая высота : установите высоту кнопок действий в соответствии с высотой внутреннего элемента, который выводится с помощью свайпа (будь то
ButtonилиCard), чтобы обеспечить единообразный внешний вид. - Преобразовать контейнер : Применить модификатор
transformedHeightиtransformationSpecк самому компонентуSwipeToReveal. - Не применяйте двойное преобразование : не применяйте
transformedHeightили модификаторыtransformationк внутреннему элементу, который выводится с помощью свайпа (карточке или кнопке внутри контейнераSwipeToReveal).
Пользовательские составные элементы в списках
При создании пользовательских компонентов поверхности для TransformingLazyColumn следуйте этим рекомендациям, чтобы ваш компонент плавно масштабировался, затухал и изменял форму вблизи краев экрана:
- Expose
SurfaceTransformation: Принимает необязательный параметрSurfaceTransformation(по умолчаниюnull), соответствующий стандартным компонентам Wear Compose Material 3, таким какCardиButton. Это позволяет вызывающим функциям вTransformingLazyColumnпередаватьSurfaceTransformation(transformationSpec)в то время как компонент будет работать нормально вне списка. - Примените
Modifier.transformedHeightпервым в вызывающем коде : при размещении вашего пользовательского компонуемого элемента вTransformingLazyColumnпередайтеModifier.transformedHeight(this, transformationSpec)в качестве первого модификатора в цепочке модификаторов вызывающего кода. В то время какSurfaceTransformationприменяет визуальные эффекты масштабирования и затухания,transformedHeightимеет решающее значение для того, чтобы указать макету списка пересчитывать высоту элемента по мере его уменьшения. - Применяйте слои преобразования,
modifierвызывающего объекта и инструмент отрисовки в указанном порядке :- Слой преобразования контейнера : Начните цепочку модификаторов корневого контейнера с
Modifier.graphicsLayerиapplyContainerTransformation(), чтобы и фон, и содержимое отображались в масштабированном, наклонном пространстве координат. -
modifierвызывающего объекта : Примените параметрmodifierпереданный вызывающим объектом (включаяModifier.transformedHeight), перед любым внутренним изменением размера или добавлением отступов. - Ограничение формы при отсутствии преобразования : если
transformationравноnull, применитеModifier.clip(shape)перед отрисовкой фона. Объект Painter, возвращаемый функциейcreateContainerPainter()ограничивает себя по форме, в отличие от обычного объекта Painter, поэтому без этого фон будет отрисован с квадратными углами, выходящими за пределы списка. - Инструмент для создания фона с эффектом морфинга : рисуйте фон внутри слоя-контейнера, используя
Modifier.drawBehindи инструмент, созданный с помощьюcreateContainerPainter(). - Слой преобразования содержимого : Примените второй слой
Modifier.graphicsLayerсapplyContentTransformation()и обрежьте его по форме контейнера, чтобы внутреннее содержимое исчезало раньше по мере приближения к рамке.
- Слой преобразования контейнера : Начните цепочку модификаторов корневого контейнера с
Следующий фрагмент кода демонстрирует, как реализовать пользовательский компонент BoardingPassCard , который применяет эти преобразования в указанном порядке:
@Composable fun BoardingPassCard( flightNumber: String, origin: String, destination: String, gate: String, seat: String, departureTime: String, modifier: Modifier = Modifier, transformation: SurfaceTransformation? = null, shape: Shape = RoundedCornerShape(18.dp), statusBadge: @Composable () -> Unit = {} ) { // 1. Create morphing container painter val backgroundPainter = ColorPainter(MaterialTheme.colorScheme.surfaceContainer) val finalPainter = if (transformation != null) { remember(transformation, backgroundPainter, shape) { transformation.createContainerPainter(backgroundPainter, shape, border = null) } } else { backgroundPainter } Column( modifier = Modifier // 2a. Container layer: Scales, fades, and tilts the whole card surface .then( if (transformation != null) { Modifier.graphicsLayer { transformation.run { applyContainerTransformation() } } } else Modifier ) // 2b. Caller modifier: Includes Modifier.transformedHeight in a list .then(modifier) .fillMaxWidth() // 2c. Shape clip: Only needed without a transformation, because the // painter from createContainerPainter clips itself to the shape .then(if (transformation == null) Modifier.clip(shape) else Modifier) // 2d. Morphing background: Drawn inside the transformed container layer .drawBehind { with(finalPainter) { draw(size) } } // 2e. Content layer: Fades content earlier and clips children to shape .then( if (transformation != null) { Modifier.graphicsLayer { this.shape = shape this.clip = true transformation.run { applyContentTransformation() } } } else Modifier ) .padding(horizontal = 14.dp, vertical = 10.dp) ) { // Card content goes here } }
Затем вы можете использовать BoardingPassCard внутри TransformingLazyColumn , передав Modifier.transformedHeight в качестве первого модификатора вместе с SurfaceTransformation(transformationSpec) :
@Composable fun BoardingPassListSample(flights: List<FlightInfo>) { val listState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold(scrollState = listState) { contentPadding -> TransformingLazyColumn( state = listState, contentPadding = contentPadding, modifier = Modifier.fillMaxSize() ) { items(flights.size) { index -> val flight = flights[index] BoardingPassCard( flightNumber = flight.number, origin = flight.origin, destination = flight.destination, gate = flight.gate, seat = flight.seat, departureTime = flight.time, modifier = Modifier .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding( CardDefaults.minimumVerticalListContentPadding ), transformation = SurfaceTransformation(transformationSpec) ) } } } }
Рекомендуем вам
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Codelab по Compose для Wear OS
- Списки и сетки
- Использование представлений в Compose
Списки позволяют пользователям выбирать элемент из предложенного набора вариантов на устройствах Wear OS.
Многие устройства Wear OS используют круглые экраны, что затрудняет просмотр элементов списка, расположенных в верхней и нижней части экрана. По этой причине Compose для Wear OS включает версию класса LazyColumn под названием TransformingLazyColumn , которая поддерживает анимацию масштабирования и морфинга. Когда элементы перемещаются к краям, они уменьшаются в размере и исчезают.
Для применения рекомендуемых эффектов масштабирования и прокрутки:
- Используйте
Modifier.transformedHeight, чтобы Compose автоматически рассчитывал изменение высоты элемента при прокрутке экрана. - Используйте
transformation = SurfaceTransformation(transformationSpec)для применения визуальных эффектов, включая уменьшение масштаба содержимого элемента. - Для компонентов, которые не принимают
transformationв качестве параметра, например, дляText, используйте собственныйTransformationSpec.
Следующая анимация показывает, как элемент списка масштабируется и меняет форму при приближении к верхней и нижней части экрана:
Приведённый ниже фрагмент кода демонстрирует, как создать список с использованием макетаTransformingLazyColumn для отображения контента, который отлично выглядит на экранах Wear OS различных размеров . В приведенном фрагменте кода также демонстрируется использование модификатора minimumVerticalContentPadding , который следует установить для элементов списка, чтобы применить правильный отступ вверху и внизу списка.
Чтобы отобразить индикатор прокрутки, используйте общее columnState для ScreenScaffold и TransformingLazyColumn :
val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold( scrollState = columnState ) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding ) { item { ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) { Text(text = "Header") } } // ... other items item { Button( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding), transformation = SurfaceTransformation(transformationSpec), onClick = { /* ... */ }, icon = { Icon( imageVector = Icons.Default.Build, contentDescription = "build", ) }, ) { Text( text = "Build", maxLines = 1, overflow = TextOverflow.Ellipsis, ) } } } }
Добавьте эффект хлопка и броска.
Функция привязки гарантирует, что после завершения прокрутки или жеста «взмах» список стабилизируется с элементом, точно расположенным в определенной точке, обычно в центре экрана. На круглых экранах, где элементы масштабируются и меняются по мере удаления от центра, функция привязки особенно полезна для обеспечения того, чтобы наиболее важный элемент оставался полностью видимым и читаемым в оптимальной области просмотра. Чтобы добавить эффект «защелкивания и запуска», установите параметр flingBehavior в значение TransformingLazyColumnDefaults.snapFlingBehavior(columnState) . Установите параметр rotaryScrollableBehavior соответствующим образом, используя RotaryScrollableDefaults.snapBehavior(columnState) для обеспечения единообразного взаимодействия при использовании физической заводной головки или безеля.
val columnState = rememberTransformingLazyColumnState() ScreenScaffold(scrollState = columnState) { contentPadding -> TransformingLazyColumn( state = columnState, flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState), rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState) ) { // ... // ... } }
Обратная компоновка
По умолчанию прокручиваемый список привязывается к своему верхнему краю. Если пользователь прокрутил стандартный список до конца и в конец добавлен новый элемент, список сохраняет фокус на текущем элементе. Например, если пользователь видит элемент 10 внизу экрана, и добавлен элемент 11, фокус остается на элементе 10, а элемент 11 отображается за пределами экрана, ниже текущего элемента.
В таких сценариях использования, как мессенджеры или журналы событий в реальном времени, такое поведение обычно нежелательно. Когда появляются новые элементы, пользователи, как правило, хотят сразу видеть самый свежий контент, если они уже находятся в конце списка. Если же одновременно появляется много элементов, список должен пропускать этот шаг и отображать самый последний элемент внизу (это означает, что некоторые промежуточные элементы могут вообще не отображаться, если пользователь не прокрутит список вверх).
Для поддержки этих сценариев использования TransformingLazyColumn позволяет изменить расположение элементов, установив reverseLayout = true . Это изменяет привязку списка с верхнего края на нижний.
Для удобства установка параметра reverseLayout = true также меняет визуальный порядок элементов и направление прокрутки:
- Элементы располагаются снизу вверх, то есть элемент с индексом 0 находится в нижней части экрана.
- Прокрутка вверх отображает элементы с более высокими индексами.
Чтобы добавить эффект «привязки и прокрутки» вместе с обратной компоновкой, можно объединить flingBehavior и rotaryScrollableBehavior как показано в следующем фрагменте кода:
val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold(scrollState = columnState) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding, reverseLayout = true, modifier = Modifier.fillMaxWidth() ) { items(10) { index -> Button( label = { Text( text = "Item ${index + 1}" ) }, onClick = {}, modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) } item { // With reverseLayout = true, the last item declared appears at the top. ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) { Text("Header") } } } }
На следующих изображениях показана разница между обычным списком и перевернутым списком:


Краевые кнопки в списках
Для Material 3 можно добавить EdgeButton — кнопку, примыкающую к краю списка. Однако следует избегать добавления этого элемента в TransformingLazyColumn , а использовать слот edgeButton в ScreenScaffold .
Использование слота edgeButton гарантирует правильное расположение кнопки в нижней части экрана и корректное поведение при прокрутке списка.
Следующий фрагмент кода демонстрирует, как реализовать EdgeButton :
val columnState = rememberTransformingLazyColumnState() ScreenScaffold( scrollState = columnState, edgeButton = { EdgeButton( onClick = { /* TODO */ }, modifier = Modifier.scrollable( columnState, orientation = Orientation.Vertical, reverseDirection = true, // Apply overscroll to the EdgeButton for proper scrolling behavior. overscrollEffect = rememberOverscrollEffect(), ) ) { Text("More") } } ) { contentPadding -> TransformingLazyColumn( contentPadding = contentPadding, state = columnState, ) { // ... // ... } }
SwipeToReveal в списках
Компонент SwipeToReveal позволяет получать доступ к действиям для элемента списка, например, Card или Chip , с помощью свайпа. Свайп обычно отображает одну или две кнопки действий (например, «Удалить» или «Ещё») сбоку.
При использовании SwipeToReveal внутри элемента TransformingLazyColumn следуйте этим рекомендациям:
- Сброс при прокрутке : Когда пользователь прокручивает список, сбрасывайте все открытые элементы в закрытое состояние.
- Единая высота : установите высоту кнопок действий в соответствии с высотой внутреннего элемента, который выводится с помощью свайпа (будь то
ButtonилиCard), чтобы обеспечить единообразный внешний вид. - Преобразовать контейнер : Применить модификатор
transformedHeightиtransformationSpecк самому компонентуSwipeToReveal. - Не применяйте двойное преобразование : не применяйте
transformedHeightили модификаторыtransformationк внутреннему элементу, который выводится с помощью свайпа (карточке или кнопке внутри контейнераSwipeToReveal).
Пользовательские составные элементы в списках
При создании пользовательских компонентов поверхности для TransformingLazyColumn следуйте этим рекомендациям, чтобы ваш компонент плавно масштабировался, затухал и изменял форму вблизи краев экрана:
- Expose
SurfaceTransformation: Принимает необязательный параметрSurfaceTransformation(по умолчаниюnull), соответствующий стандартным компонентам Wear Compose Material 3, таким какCardиButton. Это позволяет вызывающим функциям вTransformingLazyColumnпередаватьSurfaceTransformation(transformationSpec)в то время как компонент будет работать нормально вне списка. - Примените
Modifier.transformedHeightпервым в вызывающем коде : при размещении вашего пользовательского компонуемого элемента вTransformingLazyColumnпередайтеModifier.transformedHeight(this, transformationSpec)в качестве первого модификатора в цепочке модификаторов вызывающего кода. В то время какSurfaceTransformationприменяет визуальные эффекты масштабирования и затухания,transformedHeightимеет решающее значение для того, чтобы указать макету списка пересчитывать высоту элемента по мере его уменьшения. - Применяйте слои преобразования,
modifierвызывающего объекта и инструмент отрисовки в указанном порядке :- Слой преобразования контейнера : Начните цепочку модификаторов корневого контейнера с
Modifier.graphicsLayerиapplyContainerTransformation(), чтобы и фон, и содержимое отображались в масштабированном, наклонном пространстве координат. -
modifierвызывающего объекта : Примените параметрmodifierпереданный вызывающим объектом (включаяModifier.transformedHeight), перед любым внутренним изменением размера или добавлением отступов. - Ограничение формы при отсутствии преобразования : если
transformationравноnull, применитеModifier.clip(shape)перед отрисовкой фона. Объект Painter, возвращаемый функциейcreateContainerPainter()ограничивает себя по форме, в отличие от обычного объекта Painter, поэтому без этого фон будет отрисован с квадратными углами, выходящими за пределы списка. - Инструмент для создания фона с эффектом морфинга : рисуйте фон внутри слоя-контейнера, используя
Modifier.drawBehindи инструмент, созданный с помощьюcreateContainerPainter(). - Слой преобразования содержимого : Примените второй слой
Modifier.graphicsLayerсapplyContentTransformation()и обрежьте его по форме контейнера, чтобы внутреннее содержимое исчезало раньше по мере приближения к рамке.
- Слой преобразования контейнера : Начните цепочку модификаторов корневого контейнера с
Следующий фрагмент кода демонстрирует, как реализовать пользовательский компонент BoardingPassCard , который применяет эти преобразования в указанном порядке:
@Composable fun BoardingPassCard( flightNumber: String, origin: String, destination: String, gate: String, seat: String, departureTime: String, modifier: Modifier = Modifier, transformation: SurfaceTransformation? = null, shape: Shape = RoundedCornerShape(18.dp), statusBadge: @Composable () -> Unit = {} ) { // 1. Create morphing container painter val backgroundPainter = ColorPainter(MaterialTheme.colorScheme.surfaceContainer) val finalPainter = if (transformation != null) { remember(transformation, backgroundPainter, shape) { transformation.createContainerPainter(backgroundPainter, shape, border = null) } } else { backgroundPainter } Column( modifier = Modifier // 2a. Container layer: Scales, fades, and tilts the whole card surface .then( if (transformation != null) { Modifier.graphicsLayer { transformation.run { applyContainerTransformation() } } } else Modifier ) // 2b. Caller modifier: Includes Modifier.transformedHeight in a list .then(modifier) .fillMaxWidth() // 2c. Shape clip: Only needed without a transformation, because the // painter from createContainerPainter clips itself to the shape .then(if (transformation == null) Modifier.clip(shape) else Modifier) // 2d. Morphing background: Drawn inside the transformed container layer .drawBehind { with(finalPainter) { draw(size) } } // 2e. Content layer: Fades content earlier and clips children to shape .then( if (transformation != null) { Modifier.graphicsLayer { this.shape = shape this.clip = true transformation.run { applyContentTransformation() } } } else Modifier ) .padding(horizontal = 14.dp, vertical = 10.dp) ) { // Card content goes here } }
Затем вы можете использовать BoardingPassCard внутри TransformingLazyColumn , передав Modifier.transformedHeight в качестве первого модификатора вместе с SurfaceTransformation(transformationSpec) :
@Composable fun BoardingPassListSample(flights: List<FlightInfo>) { val listState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold(scrollState = listState) { contentPadding -> TransformingLazyColumn( state = listState, contentPadding = contentPadding, modifier = Modifier.fillMaxSize() ) { items(flights.size) { index -> val flight = flights[index] BoardingPassCard( flightNumber = flight.number, origin = flight.origin, destination = flight.destination, gate = flight.gate, seat = flight.seat, departureTime = flight.time, modifier = Modifier .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding( CardDefaults.minimumVerticalListContentPadding ), transformation = SurfaceTransformation(transformationSpec) ) } } } }
Рекомендуем вам
- Примечание: текст ссылки отображается, когда JavaScript отключен.
- Codelab по Compose для Wear OS
- Списки и сетки
- Использование представлений в Compose