Listen mit Compose für Wear OS


Mit Listen können Nutzer auf Wear OS-Geräten ein Element aus einer Reihe von Optionen auswählen.

Viele Wear OS-Geräte haben runde Displays. Das macht es schwieriger, Listenelemente zu sehen, die oben und unten auf dem Display angezeigt werden. Aus diesem Grund enthält Compose for Wear OS eine Version der Klasse LazyColumn namens TransformingLazyColumn, die Skalierungs- und Morphing-Animationen unterstützt. Wenn Elemente an den Rand verschoben werden, werden sie kleiner und blenden aus.

So wenden Sie die empfohlenen Skalierungs- und Scrolling-Effekte an:

  1. Verwenden Sie Modifier.transformedHeight, damit Compose die Höhenänderung berechnen kann, während das Element durch den Bildschirm scrollt.
  2. Verwenden Sie transformation = SurfaceTransformation(transformationSpec), um die visuellen Effekte anzuwenden, einschließlich des Herunterskalierens des Inhalts des Elements.
  3. Verwenden Sie einen benutzerdefinierten TransformationSpec für Komponenten, die transformation nicht als Parameter akzeptieren, z. B. Text.

Die folgende Animation zeigt, wie sich ein Listenelement skaliert und seine Form ändert, wenn es sich dem oberen und unteren Bildschirmrand nähert:

Das folgende Code-Snippet zeigt, wie Sie mit dem Layout TransformingLazyColumn eine Liste erstellen, um Inhalte zu erstellen, die auf Wear OS-Bildschirmen verschiedener Größen gut aussehen.

Im Snippet wird auch die Verwendung des Modifikators minimumVerticalContentPadding veranschaulicht, den Sie für die Listenelemente festlegen sollten, um das richtige Padding oben und unten in der Liste anzuwenden.

Wenn Sie die Scrollmarkierung einblenden möchten, teilen Sie columnState zwischen ScreenScaffold und TransformingLazyColumn auf:

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

Snap-and-Fling-Effekt hinzufügen

Durch das Einrasten wird dafür gesorgt, dass die Liste nach einer Scroll- oder Wischbewegung so positioniert wird, dass ein Element genau an einer bestimmten Stelle, in der Regel in der Mitte des Bildschirms, angezeigt wird. Auf runden Displays, auf denen Elemente skaliert und transformiert werden, wenn sie sich vom Mittelpunkt entfernen, ist das Einrasten besonders nützlich, um sicherzustellen, dass das wichtigste Element im optimalen Sichtbereich vollständig sichtbar und lesbar bleibt.

Wenn Sie ein Snap-and-Fling-Verhalten hinzufügen möchten, legen Sie den Parameter flingBehavior auf TransformingLazyColumnDefaults.snapFlingBehavior(columnState) fest. Stelle die rotaryScrollableBehavior so ein, dass sie übereinstimmt. Verwende dazu RotaryScrollableDefaults.snapBehavior(columnState), um eine einheitliche Bedienung mit der physischen Krone oder Lünette zu ermöglichen.

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

Layout umkehren

Standardmäßig wird eine scrollbare Liste am oberen Rand verankert. Wenn ein Nutzer bis zum Ende einer Standardliste gescrollt hat und ein neues Element am Ende hinzugefügt wird, bleibt die Ansicht des Nutzers auf dem aktuellen Element erhalten. Wenn der Nutzer beispielsweise Element 10 unten auf dem Bildschirm sieht und Element 11 hinzugefügt wird, bleibt der Fokus auf Element 10 und Element 11 wird außerhalb des Bildschirms unter der aktuellen Ansicht angezeigt.

Für Anwendungsfälle wie Messaging-Apps oder Live-Logs ist dieses Verhalten in der Regel nicht erwünscht. Wenn neue Elemente eintreffen, möchten Nutzer in der Regel sofort die neuesten Inhalte sehen, wenn sie sich bereits am Ende der Liste befinden. Wenn viele Artikel gleichzeitig eintreffen, sollte in der Liste das neueste Element unten angezeigt werden. Das bedeutet, dass einige Elemente dazwischen möglicherweise gar nicht angezeigt werden, es sei denn, der Nutzer scrollt nach oben.

Um diese Anwendungsfälle zu unterstützen, können Sie mit TransformingLazyColumn das Layout umkehren, indem Sie reverseLayout = true festlegen. Dadurch wird der Anker der Liste von der oberen zur unteren Kante verschoben.

Wenn Sie reverseLayout = true festlegen, wird auch die visuelle Reihenfolge der Elemente und die Richtung der Scrollgesten umgekehrt:

  • Die Elemente werden von unten nach oben zusammengesetzt. Das bedeutet, dass Index 0 unten auf dem Bildschirm angezeigt wird.
  • Wenn Sie nach oben scrollen, werden Elemente mit höheren Indexwerten angezeigt.

Wenn Sie das Verhalten „Einrasten und schnippen“ zusammen mit dem umgekehrten Layout hinzufügen möchten, können Sie flingBehavior und rotaryScrollableBehavior wie im folgenden Snippet gezeigt kombinieren:

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

Die folgenden Bilder zeigen den Unterschied zwischen einer normalen Liste und einer umgekehrten Liste:

Eine TransformingLazyColumn mit normalem Layout, in der „Item 1“ oben und die Elemente in aufsteigender Reihenfolge angezeigt werden.
Abbildung 1. Ein Standardlistenlayout, bei dem der Inhalt von oben nach unten angezeigt wird.
Eine TransformingLazyColumn mit umgekehrter Anordnung, in der Item 1 unten und die Elemente in absteigender Reihenfolge nach oben angezeigt werden.
Abbildung 2. Ein umgekehrtes Listenlayout, bei dem der Inhalt von unten nach oben gefüllt wird.