State und Jetpack Compose

Der Status in einer App ist jeder Wert, der sich im Laufe der Zeit ändern kann. Dies ist eine sehr allgemeine und umfasst alles von einer Room-Datenbank bis hin zu .

Der Status aller Android-Apps wird dem Nutzer angezeigt. Einige Beispiele für Status in Android Apps:

  • Eine Snackbar, die anzeigt, wenn keine Netzwerkverbindung hergestellt werden kann.
  • Ein Blogpost und zugehörige Kommentare.
  • Wellenförmige Animationen auf Schaltflächen, die abgespielt werden, wenn ein Nutzer darauf klickt.
  • Sticker, die Nutzende auf einem Bild zeichnen können.

Mit Jetpack Compose kannst du genau angeben, wo und wie du sie speicherst und verwendest in einer Android-App. Dieser Leitfaden konzentriert sich auf die Verbindung zwischen Zustand und zusammensetzbaren Funktionen und den APIs, die Jetpack Compose bietet, um mit mehr Status zu arbeiten. leicht gemacht.

Status und Zusammensetzung

Die Erstellungsfunktion ist deklarativ. Daher kann sie nur durch Aufrufen der Methode mit neuen Argumenten zusammensetzbar. Diese Argumente sind Darstellungen der UI-Status Jedes Mal, wenn ein Status aktualisiert wird, findet eine Neuzusammensetzung statt. Als führen, werden Dinge wie TextField nicht automatisch aktualisiert, wie in und imperative XML-basierte Ansichten verwendet werden. Einer zusammensetzbaren Funktion muss explizit der neue Status mitgeteilt werden damit sie entsprechend aktualisiert wird.

@Composable
private fun HelloContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello!",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.bodyMedium
        )
        OutlinedTextField(
            value = "",
            onValueChange = { },
            label = { Text("Name") }
        )
    }
}

Wenn Sie diesen Befehl ausführen und versuchen, Text einzugeben, werden Sie feststellen, dass nichts passiert. Das sind da TextField nicht automatisch aktualisiert wird. Es wird aktualisiert, wenn sein value Änderungen der Parameter. Das liegt daran, wie Komposition und Neuzusammensetzung Schreiben.

Weitere Informationen zur anfänglichen Zusammensetzung und Neuzusammensetzung findest du unter In „Schreibe“ denken:

Status in zusammensetzbaren Funktionen

Zusammensetzbare Funktionen können den remember API zum Speichern eines Objekts im Arbeitsspeicher. Ein von remember berechneter Wert ist in der Komposition gespeichert, und der gespeicherte Wert wird bei der Neuzusammensetzung zurückgegeben. Mit remember können sowohl änderbare als auch unveränderliche Objekte gespeichert werden.

mutableStateOf eine beobachtbare MutableState<T> Dabei handelt es sich um einen beobachtbaren Typ, der in die Compose-Laufzeit integriert ist.

interface MutableState<T> : State<T> {
    override var value: T
}

Bei Änderungen an value wird die Neuzusammensetzung beliebiger zusammensetzbarer Funktionen geplant mit folgendem Text: value.

Es gibt drei Möglichkeiten, ein MutableState-Objekt in einer zusammensetzbaren Funktion zu deklarieren:

  • val mutableState = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (value, setValue) = remember { mutableStateOf(default) }

Diese Deklarationen sind äquivalent und werden als Syntax Zucker für unterschiedliche Verwendungen von Bundesstaaten. Sie sollten diejenige auswählen, mit der die in der zusammensetzbaren Funktion, die Sie schreiben, ist der lesefreundlichste Code.

Die Delegatsyntax by erfordert die folgenden Importe:

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

Sie können den gespeicherten Wert als Parameter für andere zusammensetzbare Funktionen oder sogar in Anweisungen ändern, welche zusammensetzbaren Funktionen angezeigt werden. Wenn beispielsweise Soll die Begrüßung nicht angezeigt werden, wenn der Name leer ist, verwenden Sie den Status in einem if-Anweisung:

@Composable
fun HelloContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        var name by remember { mutableStateOf("") }
        if (name.isNotEmpty()) {
            Text(
                text = "Hello, $name!",
                modifier = Modifier.padding(bottom = 8.dp),
                style = MaterialTheme.typography.bodyMedium
            )
        }
        OutlinedTextField(
            value = name,
            onValueChange = { name = it },
            label = { Text("Name") }
        )
    }
}

Mit remember können Sie den Zustand bei Neuzusammensetzungen beibehalten, aber das ist nicht der Fall. werden bei allen Konfigurationsänderungen beibehalten. Dazu müssen Sie rememberSaveable rememberSaveable speichert automatisch alle Werte, die in einem Bundle gespeichert. Für andere Werte können Sie ein benutzerdefiniertes Saver-Objekt übergeben.

Andere unterstützte Statustypen

Für das Schreiben müssen Sie nicht MutableState<T> verwenden, um einen Hold-Zustand zu verwenden. sie andere beobachtbare Typen unterstützt. Bevor Sie einen anderen beobachtbaren Typ lesen, Compose-Datei einfügen, müssen Sie es in eine State<T> konvertieren, damit zusammensetzbare Funktionen automatisch neu zusammensetzen, wenn sich der Zustand ändert.

Compose enthält Funktionen zum Erstellen von State<T> aus gängigen beobachtbaren Elementen die in Android-Apps verwendet werden. Bevor Sie diese Integrationen verwenden, fügen Sie den geeignete Artefakte wie unten beschrieben:

  • Flow: collectAsStateWithLifecycle()

    collectAsStateWithLifecycle() erfasst Werte aus einer Flow an den Lebenszyklus angepasst, damit Ihre App die App-Ressourcen zu schonen. Er steht für den letzten ausgegebenen Wert des „Schreiben“ State. Verwenden Sie diese API als empfohlene Methode zum Erfassen von Datenflüssen auf Android-Apps.

    Die folgende Abhängigkeit ist in der Datei build.gradle erforderlich. Sie sollte 2.6.0-beta01 oder höher sein):

Kotlin

dependencies {
      ...
      implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
}

Cool

dependencies {
      ...
      implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.2"
}
  • Flow: collectAsState()

    collectAsState ähnelt collectAsStateWithLifecycle, weil ebenfalls erfasst Werte aus einer Flow und wandelt sie in „Schreiben“ State um.

    Verwenden Sie collectAsState für plattformunabhängigen Code anstelle von collectAsStateWithLifecycle, das nur für Android verfügbar ist.

    Für collectAsState sind keine zusätzlichen Abhängigkeiten erforderlich, da dies verfügbar in compose-runtime.

  • LiveData: observeAsState()

    observeAsState() beginnt mit der Beobachtung dieses LiveData und stellt seine Werte dar über State.

    Die folgende Abhängigkeit ist in der Datei build.gradle erforderlich:

Kotlin

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-livedata:1.6.8")
}

Cool

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-livedata:1.6.8"
}

Kotlin

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava2:1.6.8")
}

Cool

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava2:1.6.8"
}

Kotlin

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava3:1.6.8")
}

Cool

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava3:1.6.8"
}
State<T>

Zustandsorientiert oder zustandslos

Eine zusammensetzbare Funktion, die remember zum Speichern eines Objekts verwendet, erstellt einen internen Status. damit die zusammensetzbare Funktion zustandsorientiert wird. HelloContent ist ein Beispiel für eine zustandsorientierte zusammensetzbar, da sie den name-Status intern enthält und ändert. Dies kann in Situationen nützlich sein, in denen der Aufrufer den Status nicht kontrollieren muss ohne den Status selbst verwalten zu müssen. Zusammensetzbare Funktionen mit sind in der Regel weniger wiederverwendbar und schwerer zu testen.

Eine zustandslose zusammensetzbare Funktion ist eine zusammensetzbare Funktion, die keinen Zustand enthält. Eine einfache Zustandslosigkeit erreichen, ist die Verwendung von state Winde.

Bei der Entwicklung wiederverwendbarer zusammensetzbarer Funktionen sollten Sie häufig sowohl eine zustandsorientierte und eine zustandslose Version derselben zusammensetzbaren Funktion. Die zustandsorientierte Version ist praktisch für Aufrufer, für die der Status nicht relevant ist, und für zustandslose Version ist für Aufrufer erforderlich, die den Status steuern oder hochziehen müssen.

Staatliche Winden

Das Hochheben von Status in Compose ist ein Muster, das sich an den Aufrufer einer zusammensetzbaren Funktion bewegt um eine zusammensetzbare zustandslose Lösung zu erstellen. Das allgemeine Muster zum Einheben Jetpack Compose ersetzt die Statusvariable durch zwei Parameter:

  • value: T: der aktuell anzuzeigende Wert
  • onValueChange: (T) -> Unit: Ein Ereignis, das eine Änderung des Werts anfordert Dabei ist T der vorgeschlagene neue Wert

Sie sind jedoch nicht auf onValueChange beschränkt. Wenn spezifischere Ereignisse sollten Sie sie mithilfe von Lambdas definieren.

Ein Zustand, der auf diese Weise gezogen wird, hat einige wichtige Eigenschaften:

  • Single Source of Truth:Wenn wir den Zustand verschieben, anstatt ihn zu duplizieren, wird er dass es nur eine einzige Informationsquelle gibt. So lassen sich Fehler vermeiden.
  • Encapsulated (Verkapselt): Der Status kann nur von zustandsorientierten zusammensetzbaren Funktionen geändert werden. Es ist komplett intern arbeiten.
  • Gemeinsam nutzbar: Der Hub-Zustand kann mit mehreren zusammensetzbaren Funktionen geteilt werden. Wenn Sie wollte name in einer anderen zusammensetzbaren Funktion lesen. Mit Winden können Sie tun Sie das.
  • Interceptable (Interceptable):Aufrufer der zustandslosen zusammensetzbaren Funktionen können entscheiden, Ereignisse zu ignorieren oder zu ändern, bevor der Status geändert wird.
  • Entkoppelt:Der Status für die zustandslosen zusammensetzbaren Funktionen kann gespeichert werden. überall. Beispielsweise ist es jetzt möglich, name in einen ViewModel zu verschieben.

Im Beispielfall extrahieren Sie name und onValueChange aus HelloContent und verschieben Sie sie in der Baumstruktur in eine HelloScreen zusammensetzbare Funktion, ruft HelloContent auf.

@Composable
fun HelloScreen() {
    var name by rememberSaveable { mutableStateOf("") }

    HelloContent(name = name, onNameChange = { name = it })
}

@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
    Column(modifier = Modifier.padding(16.dp)) {
        Text(
            text = "Hello, $name",
            modifier = Modifier.padding(bottom = 8.dp),
            style = MaterialTheme.typography.bodyMedium
        )
        OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") })
    }
}

Durch Heben des Zustands aus HelloContent ist es einfacher, zusammensetzbar sind, in verschiedenen Situationen wiederverwenden und testen. HelloContent ist von der Speicherung seines Zustands entkoppelt ist. Eine Entkopplung bedeutet, dass Sie, wenn Sie Änderungen oder HelloScreen ersetzen, müssen Sie die Einstellungen für HelloContent nicht ändern implementiert.

Das Muster, bei dem der Zustand abfällt und die Ereignisse ansteigen, Unidirektionalen Datenfluss an. In diesem Fall sinkt der Status von HelloScreen. auf HelloContent und die Ereignisse steigen von HelloContent bis HelloScreen an. Von unidirektionalen Datenfluss folgen, können Sie zusammensetzbare Funktionen entkoppeln, die in der Benutzeroberfläche für die Teile Ihrer App, in denen der Status gespeichert und geändert wird.

Weitere Informationen finden Sie auf der Seite Windenstatus.

Status in Compose wird wiederhergestellt

Die rememberSaveable API verhält sich ähnlich wie remember, weil sie Beibehaltung des Zustands bei Neuzusammensetzungen und auch bei Aktivitäten oder Prozessen Wiederherstellung mithilfe des Mechanismus für den gespeicherten Instanzstatus. Das ist zum Beispiel der Fall, wenn der Bildschirm gedreht wird.

Möglichkeiten zum Speichern des Status

Alle Datentypen, die dem Bundle hinzugefügt werden, werden automatisch gespeichert. Wenn Sie etwas speichern möchten, das Bundle nicht hinzugefügt werden kann, gibt es mehrere Optionen.

Paket

Die einfachste Lösung besteht darin, @Parcelize an das Objekt an. Das Objekt wird geparst und kann gebündelt werden. Für Beispiel: Dieser Code erstellt einen parierbaren Datentyp City und speichert ihn im Bundesstaat.

@Parcelize
data class City(val name: String, val country: String) : Parcelable

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

MapSaver

Wenn @Parcelize aus irgendeinem Grund nicht geeignet ist, kannst du mapSaver verwenden, um definieren Sie Ihre eigene Regel zum Konvertieren eines Objekts in eine Gruppe von Werten, System in Bundle speichern kann.

data class City(val name: String, val country: String)

val CitySaver = run {
    val nameKey = "Name"
    val countryKey = "Country"
    mapSaver(
        save = { mapOf(nameKey to it.name, countryKey to it.country) },
        restore = { City(it[nameKey] as String, it[countryKey] as String) }
    )
}

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

Listenspeicher

Um zu vermeiden, dass die Schlüssel für die Karte definiert werden müssen, können Sie auch listSaver verwenden. und verwenden ihre Indizes als Schlüssel:

data class City(val name: String, val country: String)

val CitySaver = listSaver<City, Any>(
    save = { listOf(it.name, it.country) },
    restore = { City(it[0] as String, it[1] as String) }
)

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable(stateSaver = CitySaver) {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

Staatsinhaber in Compose

Einfache Winden können direkt in den zusammensetzbaren Funktionen verwaltet werden. Wenn jedoch die Menge des Zustands, der Anstiege im Blick behalten soll, oder die Logik, in zusammensetzbaren Funktionen auftreten, empfiehlt es sich, Logik und Verantwortlichkeiten gegenüber anderen Klassen: Inhabern des Bundesstaats.

Weitere Informationen finden Sie in der Dokumentation zu State Hoisting in Compose oder allgemein in der Weitere Informationen finden Sie auf der Seite State holders and UI State im Architekturleitfaden.

„Berechnungen merken“ noch einmal auslösen, wenn sich Tasten ändern

Die remember API wird häufig zusammen mit MutableState verwendet:

var name by remember { mutableStateOf("") }

Hier behält der Wert MutableState mit der Funktion remember bei und Neuzusammensetzungen.

Im Allgemeinen verwendet remember einen Lambda-Parameter calculation. Wenn remember wird die Lambda-Funktion calculation aufgerufen, und das Ergebnis wird gespeichert. Währenddessen Zusammensetzung gibt remember den zuletzt gespeicherten Wert zurück.

Neben dem Caching-Status können Sie mit remember auch beliebige Objekte oder Ergebnis eines Vorgangs in der Komposition, deren Initialisierung oder zu berechnen. Vielleicht möchten Sie diese Berechnung nicht bei jeder Neuzusammensetzung wiederholen. Ein Beispiel ist die Erstellung dieses ShaderBrush-Objekts, das eine teure Vorgang:

val brush = remember {
    ShaderBrush(
        BitmapShader(
            ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(),
            Shader.TileMode.REPEAT,
            Shader.TileMode.REPEAT
        )
    )
}

remember speichert den Wert, bis er die Komposition verlässt. Es gibt jedoch eine den im Cache gespeicherten Wert zu entwerten. Die remember API verwendet auch einen key- oder keys-Parameter. Wenn sich eine dieser Tasten ändert, wird das nächste Mal, wenn die Funktion Zusammensetzung, remember entwertet den Cache und führt die Berechnung aus Lambda-Blockade wieder aktivieren. Mit diesem Mechanismus können Sie die Lebensdauer eines -Objekt in der Komposition. Die Berechnung bleibt gültig, bis die Eingaben bis der gespeicherte Wert die Komposition verlässt.

Die folgenden Beispiele zeigen, wie dieser Mechanismus funktioniert.

In diesem Snippet wird ein ShaderBrush erstellt und als Hintergrund verwendet Farbe einer Box zusammensetzbaren Funktion. remember speichert die Instanz ShaderBrush da die Neuerstellung teuer ist. remember Take avatarRes als key1-Parameter. Dies ist das ausgewählte Hintergrundbild. Wenn avatarRes ändert, wird der Pinsel mit dem neuen Bild neu zusammengesetzt und auf Box. Dies kann passieren, wenn der Nutzer ein anderes Bild als in einer Auswahl aus.

@Composable
private fun BackgroundBanner(
    @DrawableRes avatarRes: Int,
    modifier: Modifier = Modifier,
    res: Resources = LocalContext.current.resources
) {
    val brush = remember(key1 = avatarRes) {
        ShaderBrush(
            BitmapShader(
                ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(),
                Shader.TileMode.REPEAT,
                Shader.TileMode.REPEAT
            )
        )
    }

    Box(
        modifier = modifier.background(brush)
    ) {
        /* ... */
    }
}

Im nächsten Snippet wird der Zustand in eine plain State Holder Class hochgezogen. MyAppState Sie stellt eine rememberMyAppState-Funktion zur Verfügung, um ein Instanz der Klasse mit remember. Die Bereitstellung solcher Funktionen zum Erstellen eines -Instanz, die Neuzusammensetzungen überlebt, ist ein gängiges Muster in Compose. Die rememberMyAppState-Funktion empfängt windowSizeClass, die als key-Parameter für remember Wenn sich dieser Parameter ändert, muss die App erstellen Sie die Halteklasse des einfachen Zustands mit dem neuesten Wert. Das kann in folgenden Fällen passieren: z. B. wenn der Nutzer das Gerät dreht.

@Composable
private fun rememberMyAppState(
    windowSizeClass: WindowSizeClass
): MyAppState {
    return remember(windowSizeClass) {
        MyAppState(windowSizeClass)
    }
}

@Stable
class MyAppState(
    private val windowSizeClass: WindowSizeClass
) { /* ... */ }

Compose verwendet die Klasse equals-Implementierung, um zu entscheiden, ob ein Schlüssel geändert und ungültig gemacht werden.

Status mit Schlüsseln über die Neuzusammensetzung hinaus speichern

Die rememberSaveable API ist ein Wrapper um remember, der speichern kann, in einem Bundle. Mit dieser API kann der Status nicht nur Neuzusammensetzung, aber auch Erholung und vom System ausgelöste Todesfälle. rememberSaveable empfängt input-Parameter für denselben Zweck, der remember erhält keys. Der Cache wird entwertet, wenn eine der Eingaben ändern. Bei der nächsten Neuzusammensetzung der Funktion wird rememberSaveable noch einmal ausgeführt der Lambda-Blockade berechnet.

Im folgenden Beispiel speichert rememberSaveable userTypedQuery bis typedQuery-Änderungen:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

Weitere Informationen

Weitere Informationen zu Status und Jetpack Compose finden Sie hier zusätzliche Ressourcen.

Produktproben

Codelabs

Videos

Blogs