Wischen, um die Benachrichtigung zu schließen oder zu aktualisieren

Mit der Komponente SwipeToDismissBox können Nutzer ein Element schließen oder aktualisieren, indem sie es nach links oder rechts wischen.

API-Oberfläche

Verwenden Sie das SwipeToDismissBox-Composable, um Aktionen zu implementieren, die durch Wischgesten ausgelöst werden. Wichtige Parameter:

  • state: Der SwipeToDismissBoxState-Zustand, der erstellt wurde, um den Wert zu speichern, der durch Berechnungen für das Wischelement erzeugt wird. Wenn dieser Wert erzeugt wird, werden Ereignisse ausgelöst.
  • backgroundContent: Eine anpassbare zusammensetzbare Funktion, die hinter dem Elementinhalt angezeigt wird und sichtbar wird, wenn der Inhalt weggewischt wird.

Einfaches Beispiel: Aktualisieren oder Schließen durch Wischen

Die Snippets in diesem Beispiel zeigen eine Wisch-Implementierung, die das Element entweder aktualisiert, wenn von Anfang bis Ende gewischt wird, oder das Element verwirft, wenn von Ende bis Anfang gewischt wird.

data class TodoItem(
    val itemDescription: String,
    var isItemDone: Boolean = false
)

@Composable
fun TodoListItem(
    todoItem: TodoItem,
    onToggleDone: (TodoItem) -> Unit,
    onRemove: (TodoItem) -> Unit,
    modifier: Modifier = Modifier,
) {
    val swipeToDismissBoxState = rememberSwipeToDismissBoxState(
        confirmValueChange = {
            if (it == StartToEnd) onToggleDone(todoItem)
            else if (it == EndToStart) onRemove(todoItem)
            // Reset item when toggling done status
            it != StartToEnd
        }
    )

    SwipeToDismissBox(
        state = swipeToDismissBoxState,
        modifier = modifier.fillMaxSize(),
        backgroundContent = {
            when (swipeToDismissBoxState.dismissDirection) {
                StartToEnd -> {
                    Icon(
                        if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank,
                        contentDescription = if (todoItem.isItemDone) "Done" else "Not done",
                        modifier = Modifier
                            .fillMaxSize()
                            .background(Color.Blue)
                            .wrapContentSize(Alignment.CenterStart)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                EndToStart -> {
                    Icon(
                        imageVector = Icons.Default.Delete,
                        contentDescription = "Remove item",
                        modifier = Modifier
                            .fillMaxSize()
                            .background(Color.Red)
                            .wrapContentSize(Alignment.CenterEnd)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                Settled -> {}
            }
        }
    ) {
        ListItem(
            headlineContent = { Text(todoItem.itemDescription) },
            supportingContent = { Text("swipe me to update or remove.") }
        )
    }
}

Wichtige Punkte zum Code

  • swipeToDismissBoxState verwaltet den Komponentenstatus. Der confirmValueChange-Callback wird ausgelöst, sobald die Interaktion mit dem Element abgeschlossen ist. Der Callback-Text verarbeitet die verschiedenen möglichen Aktionen. Der Callback gibt einen booleschen Wert zurück, der der Komponente mitteilt, ob eine Schließanimation angezeigt werden soll. In diesem Fall:
    • Wenn das Element von Anfang bis Ende gewischt wird, wird der onToggleDone-Lambda-Ausdruck aufgerufen und der aktuelle todoItem übergeben. Dies entspricht dem Aktualisieren des To-do-Elements.
    • Wenn das Element von rechts nach links gewischt wird, wird das Lambda onRemove aufgerufen und das aktuelle todoItem übergeben. Dies entspricht dem Löschen des To-Do-Elements.
    • it != StartToEnd: Diese Zeile gibt true zurück, wenn die Wischrichtung nicht StartToEnd ist, andernfalls false. Wenn false zurückgegeben wird, wird verhindert, dass die SwipeToDismissBox nach einer Wischgeste zum Ein- oder Ausschalten sofort verschwindet. So kann eine visuelle Bestätigung oder Animation angezeigt werden.
  • SwipeToDismissBox ermöglicht horizontales Wischen für jedes Element. Im Ruhezustand wird der innere Inhalt der Komponente angezeigt. Wenn ein Nutzer jedoch mit dem Wischen beginnt, wird der Inhalt wegbewegt und die backgroundContent wird angezeigt. Sowohl der normale Inhalt als auch der backgroundContent erhalten die vollständigen Einschränkungen des übergeordneten Containers, in dem sie gerendert werden sollen. content wird über backgroundContent gezeichnet. In diesem Fall:
    • backgroundContent wird als Icon mit einer Hintergrundfarbe basierend auf SwipeToDismissBoxValue implementiert:
    • Blue beim Wischen nach StartToEnd – eine Aufgabe umschalten.
    • Red beim Wischen von EndToStart – Löschen eines To-do-Elements.
    • Für Settled wird im Hintergrund nichts angezeigt. Wenn das Element nicht gewischt wird, wird im Hintergrund nichts angezeigt.
    • Auch die angezeigte Icon passt sich der Wischrichtung an:
    • StartToEnd zeigt das Symbol CheckBox an, wenn die Aufgabe erledigt ist, und das Symbol CheckBoxOutlineBlank, wenn sie nicht erledigt ist.
    • Für EndToStart wird das Symbol Delete angezeigt.

@Composable
private fun SwipeItemExample() {
    val todoItems = remember {
        mutableStateListOf(
            TodoItem("Pay bills"), TodoItem("Buy groceries"),
            TodoItem("Go to gym"), TodoItem("Get dinner")
        )
    }

    LazyColumn {
        items(
            items = todoItems,
            key = { it.itemDescription }
        ) { todoItem ->
            TodoListItem(
                todoItem = todoItem,
                onToggleDone = { todoItem ->
                    todoItem.isItemDone = !todoItem.isItemDone
                },
                onRemove = { todoItem ->
                    todoItems -= todoItem
                },
                modifier = Modifier.animateItem()
            )
        }
    }
}

Wichtige Punkte zum Code

  • mutableStateListOf(...) erstellt eine beobachtbare Liste, die TodoItem-Objekte enthalten kann. Wenn ein Element zu dieser Liste hinzugefügt oder daraus entfernt wird, werden die davon abhängigen Teile der Benutzeroberfläche von Compose neu zusammengesetzt.
    • In mutableStateListOf() werden vier TodoItem-Objekte mit den entsprechenden Beschreibungen initialisiert: „Rechnungen bezahlen“, „Lebensmittel kaufen“, „Ins Fitnessstudio gehen“ und „Abendessen holen“.
  • LazyColumn zeigt eine vertikal scrollbare Liste von todoItems an.
  • onToggleDone = { todoItem -> ... } ist eine Callback-Funktion, die in TodoListItem aufgerufen wird, wenn der Nutzer ein Objekt als erledigt markiert. Das Attribut isItemDone der todoItem wird aktualisiert. Da todoItems ein mutableStateListOf ist, löst diese Änderung eine Neuzusammensetzung aus, wodurch die Benutzeroberfläche aktualisiert wird.
  • onRemove = { todoItem -> ... } ist eine Callback-Funktion, die ausgelöst wird, wenn der Nutzer den Artikel entfernt. Dadurch wird die spezifische todoItem aus der Liste todoItems entfernt. Dies führt auch zu einer Neuzusammensetzung und das Element wird aus der angezeigten Liste entfernt.
  • Auf jedes TodoListItem-Element wird ein animateItem-Modifikator angewendet, sodass der placementSpec-Modifikator aufgerufen wird, wenn das Element geschlossen wurde. Dadurch wird das Entfernen des Elements sowie die Neuanordnung anderer Elemente in der Liste animiert.

Ergebnis

Das folgende Video zeigt die grundlegende Funktion „Zum Schließen wischen“ aus den vorherigen Snippets:

Abbildung 1: Eine einfache Implementierung von „Wischen zum Schließen“, mit der ein Element als erledigt markiert und eine Animation zum Schließen für ein Element in einer Liste angezeigt werden kann.

Den vollständigen Beispielcode finden Sie in der GitHub-Quelldatei.

Erweitertes Beispiel: Hintergrundfarbe beim Wischen animieren

In den folgenden Snippets wird gezeigt, wie Sie einen Positionsschwellenwert einbinden, um die Hintergrundfarbe eines Elements beim Wischen zu animieren.

data class TodoItem(
    val itemDescription: String,
    var isItemDone: Boolean = false
)

@Composable
fun TodoListItemWithAnimation(
    todoItem: TodoItem,
    onToggleDone: (TodoItem) -> Unit,
    onRemove: (TodoItem) -> Unit,
    modifier: Modifier = Modifier,
) {
    val swipeToDismissBoxState = rememberSwipeToDismissBoxState(
        confirmValueChange = {
            if (it == StartToEnd) onToggleDone(todoItem)
            else if (it == EndToStart) onRemove(todoItem)
            // Reset item when toggling done status
            it != StartToEnd
        }
    )

    SwipeToDismissBox(
        state = swipeToDismissBoxState,
        modifier = modifier.fillMaxSize(),
        backgroundContent = {
            when (swipeToDismissBoxState.dismissDirection) {
                StartToEnd -> {
                    Icon(
                        if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank,
                        contentDescription = if (todoItem.isItemDone) "Done" else "Not done",
                        modifier = Modifier
                            .fillMaxSize()
                            .drawBehind {
                                drawRect(lerp(Color.LightGray, Color.Blue, swipeToDismissBoxState.progress))
                            }
                            .wrapContentSize(Alignment.CenterStart)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                EndToStart -> {
                    Icon(
                        imageVector = Icons.Default.Delete,
                        contentDescription = "Remove item",
                        modifier = Modifier
                            .fillMaxSize()
                            .background(lerp(Color.LightGray, Color.Red, swipeToDismissBoxState.progress))
                            .wrapContentSize(Alignment.CenterEnd)
                            .padding(12.dp),
                        tint = Color.White
                    )
                }
                Settled -> {}
            }
        }
    ) {
        OutlinedCard(shape = RectangleShape) {
            ListItem(
                headlineContent = { Text(todoItem.itemDescription) },
                supportingContent = { Text("swipe me to update or remove.") }
            )
        }
    }
}

Wichtige Punkte zum Code

  • drawBehind zeichnet direkt in den Canvas hinter dem Inhalt des Icon-Kompositionselements.
    • drawRect() zeichnet ein Rechteck auf dem Canvas und füllt die gesamten Grenzen des Zeichenbereichs mit dem angegebenen Color.
  • Beim Wischen wird die Hintergrundfarbe des Elements mithilfe von lerp fließend geändert.
    • Bei einem Wischen von StartToEnd ändert sich die Hintergrundfarbe allmählich von Hellgrau zu Blau.
    • Bei einer Wischgeste von EndToStart ändert sich die Hintergrundfarbe allmählich von Hellgrau zu Rot.
    • Der Übergang von einer Farbe zur nächsten wird durch swipeToDismissBoxState.progress bestimmt.
  • OutlinedCard fügt eine subtile visuelle Trennung zwischen den Listenelementen hinzu.

@Composable
private fun SwipeItemWithAnimationExample() {
    val todoItems = remember {
        mutableStateListOf(
            TodoItem("Pay bills"), TodoItem("Buy groceries"),
            TodoItem("Go to gym"), TodoItem("Get dinner")
        )
    }

    LazyColumn {
        items(
            items = todoItems,
            key = { it.itemDescription }
        ) { todoItem ->
            TodoListItemWithAnimation(
                todoItem = todoItem,
                onToggleDone = { todoItem ->
                    todoItem.isItemDone = !todoItem.isItemDone
                },
                onRemove = { todoItem ->
                    todoItems -= todoItem
                },
                modifier = Modifier.animateItem()
            )
        }
    }
}

Wichtige Punkte zum Code

  • Wichtige Informationen zu diesem Code finden Sie im vorherigen Abschnitt unter Wichtige Punkte. Dort wird ein identisches Code-Snippet beschrieben.

Ergebnis

Das folgende Video zeigt die erweiterte Funktionalität mit animierter Hintergrundfarbe:

Abbildung 2: Eine Implementierung der Wischgeste zum Einblenden oder Löschen mit animierten Hintergrundfarben und einem längeren Schwellenwert, bevor die Aktion registriert wird.

Den vollständigen Beispielcode finden Sie in der GitHub-Quelldatei.

Zusätzliche Ressourcen