Списки с помощью Compose для Wear OS


Списки позволяют пользователям выбирать элемент из предложенного набора вариантов на устройствах Wear OS.

Многие устройства Wear OS используют круглые экраны, что затрудняет просмотр элементов списка, расположенных в верхней и нижней части экрана. По этой причине Compose для Wear OS включает версию класса LazyColumn под названием TransformingLazyColumn , которая поддерживает анимацию масштабирования и морфинга. Когда элементы перемещаются к краям, они уменьшаются в размере и исчезают.

Для применения рекомендуемых эффектов масштабирования и прокрутки:

  1. Используйте Modifier.transformedHeight , чтобы Compose автоматически рассчитывал изменение высоты элемента при прокрутке экрана.
  2. Используйте transformation = SurfaceTransformation(transformationSpec) для применения визуальных эффектов, включая уменьшение масштаба содержимого элемента.
  3. Для компонентов, которые не принимают 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,
                )
            }
        }
    }
}

Add a snap-and-fling effect

Функция привязки гарантирует, что после завершения прокрутки или жеста «взмах» список стабилизируется с элементом, точно расположенным в определенной точке, обычно в центре экрана. На круглых экранах, где элементы масштабируются и меняются по мере удаления от центра, функция привязки особенно полезна для обеспечения того, чтобы наиболее важный элемент оставался полностью видимым и читаемым в оптимальной области просмотра.

Чтобы добавить эффект «защелкивания и запуска», установите параметр flingBehavior в значение TransformingLazyColumnDefaults.snapFlingBehavior(columnState) . Установите параметр rotaryScrollableBehavior соответствующим образом, используя RotaryScrollableDefaults.snapBehavior(columnState) для обеспечения единообразного взаимодействия при использовании физической заводной головки или безеля.

val columnState = rememberTransformingLazyColumnState()
ScreenScaffold(scrollState = columnState) {
    TransformingLazyColumn(
        state = columnState,
        flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState),
        rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState)
    ) {
        // ...
        // ...
    }
}

Reverse layout

По умолчанию прокручиваемый список привязывается к своему верхнему краю. Если пользователь прокрутил стандартный список до конца и в конец добавлен новый элемент, список сохраняет фокус на текущем элементе. Например, если пользователь видит элемент 10 внизу экрана, и добавлен элемент 11, фокус остается на элементе 10, а элемент 11 отображается за пределами экрана, ниже текущего элемента.

В таких сценариях использования, как мессенджеры или журналы событий в реальном времени, такое поведение обычно нежелательно. Когда появляются новые элементы, пользователи, как правило, хотят сразу видеть самый свежий контент, если они уже находятся в конце списка. Если же одновременно появляется много элементов, список должен пропускать этот шаг и отображать самый последний элемент внизу (это означает, что некоторые промежуточные элементы могут вообще не отображаться, если пользователь не прокрутит список вверх).

Для поддержки этих сценариев использования TransformingLazyColumn позволяет изменить расположение элементов, установив reverseLayout = true . Это изменяет привязку списка с верхнего края на нижний.

Для удобства установка параметра reverseLayout = true также меняет визуальный порядок элементов и направление прокрутки:

  • Элементы располагаются снизу вверх, то есть элемент с индексом 0 находится в нижней части экрана.
  • Scrolling up reveals items with higher indexes.

Чтобы добавить эффект «привязки и прокрутки» вместе с обратной компоновкой, можно объединить 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")
            }
        }
    }
}

На следующих изображениях показана разница между обычным списком и перевернутым списком:

Компонент TransformingLazyColumn с обычной компоновкой, отображающий элемент 1 вверху и остальные элементы в порядке возрастания.
Рисунок 1. Стандартная структура списка, где содержимое заполняется сверху вниз.
Компонент TransformingLazyColumn с обратной компоновкой, отображающий элемент 1 внизу, а остальные элементы — в порядке убывания вверху.
Рисунок 2. Перевернутая компоновка списка, где содержимое заполняется снизу вверх.
{% verbatim %} {% endverbatim %} {% verbatim %} {% endverbatim %}