Listas con Compose para Wear OS


Las listas les permiten a los usuarios seleccionar un elemento de un conjunto de opciones en dispositivos Wear OS.

Muchos dispositivos Wear OS usan pantallas redondas, lo que dificulta la visualización de los elementos de la lista que aparecen cerca de la parte inferior y superior de la pantalla. Por este motivo, Compose para Wear OS incluye una versión de la clase LazyColumn llamada TransformingLazyColumn, que admite animaciones de escalamiento y transformación. Cuando los elementos se mueven hacia los bordes, se hacen más pequeños y se atenúan.

Para aplicar los efectos de desplazamiento y escalamiento recomendados, sigue estos pasos:

  1. Usa Modifier.transformedHeight para permitir que Compose calcule el cambio de altura a medida que el elemento se desplaza por la pantalla.
  2. Usa transformation = SurfaceTransformation(transformationSpec) para aplicar los efectos visuales, incluido el ajuste del tamaño del contenido del elemento.
  3. Usa un TransformationSpec personalizado para los componentes que no toman transformation como parámetro, como Text.

En la siguiente animación, se muestra cómo un elemento de lista se ajusta y cambia de forma cuando se acerca a la parte superior e inferior de la pantalla:

En el siguiente fragmento de código, se muestra cómo crear una lista con el diseño TransformingLazyColumn para crear contenido que se vea bien en una variedad de tamaños de pantalla de Wear OS.

El fragmento también demuestra el uso del modificador minimumVerticalContentPadding, que debes establecer en los elementos de la lista para aplicar el padding correcto en la parte superior e inferior de la lista.

Para mostrar el indicador de desplazamiento, comparte el columnState entre el ScreenScaffold y el 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,
                )
            }
        }
    }
}

Cómo agregar un efecto de ajustar y deslizar.

El ajuste garantiza que, cuando un usuario finaliza un gesto de desplazamiento o deslizamiento, la lista se detiene con un elemento posicionado con precisión en un punto específico, por lo general, el centro de la pantalla. En las pantallas redondas, en las que los elementos se escalan y transforman a medida que se alejan del centro, el ajuste es particularmente útil para garantizar que el elemento más relevante permanezca completamente visible y legible en el área de visualización óptima.

Para agregar un comportamiento de ajuste y desplazamiento, establece el parámetro flingBehavior en TransformingLazyColumnDefaults.snapFlingBehavior(columnState). Configura el rotaryScrollableBehavior para que coincida con RotaryScrollableDefaults.snapBehavior(columnState) y así obtener una experiencia coherente cuando uses la corona o el bisel físicos.

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

Diseño inverso

De forma predeterminada, una lista desplazable se fija a su borde superior. Si un usuario se desplazó hasta el final de una lista estándar y se agrega un elemento nuevo al final, la lista mantiene la vista del usuario en el elemento actual. Por ejemplo, si el usuario está viendo el elemento 10 en la parte inferior de la pantalla y se agrega el elemento 11, la vista sigue enfocada en el elemento 10 y el elemento 11 aparece fuera de la pantalla debajo de la vista actual.

En casos de uso como aplicaciones de mensajería o registros en vivo, este comportamiento no suele ser deseado. Cuando llegan elementos nuevos, los usuarios suelen querer ver el contenido más reciente de inmediato si ya están al final de la lista. Si llegan muchos elementos a la vez, la lista debe omitir la visualización del elemento más reciente en la parte inferior (lo que significa que es posible que no se muestren algunos elementos intermedios a menos que el usuario se desplace hacia arriba).

Para admitir estos casos de uso, TransformingLazyColumn te permite invertir el diseño configurando reverseLayout = true. Esto cambia el anclaje de la lista del borde superior al borde inferior.

Para mayor comodidad, establecer reverseLayout = true también invierte el orden visual de los elementos y la dirección de los gestos de desplazamiento:

  • Los elementos se componen de abajo hacia arriba, lo que significa que el índice 0 aparece en la parte inferior de la pantalla.
  • Si te desplazas hacia arriba, se revelan elementos con índices más altos.

Para agregar un comportamiento de ajuste y desplazamiento junto con el diseño inverso, puedes combinar flingBehavior y rotaryScrollableBehavior, como se muestra en el siguiente fragmento:

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")
            }
        }
    }
}

En las siguientes imágenes, se muestra la diferencia entre una lista normal y una invertida:

Un TransformingLazyColumn con diseño normal, que muestra el elemento 1 en la parte superior y los elementos en orden ascendente.
Figura 1: Diseño de lista estándar en el que el contenido se completa de arriba hacia abajo.
Un TransformingLazyColumn con diseño inverso, que muestra el elemento 1 en la parte inferior y los elementos en orden descendente hacia la parte superior.
Figura 2: Diseño de lista invertido en el que el contenido se completa de abajo hacia arriba.