Der Status einer App ist jeder Wert, der sich im Laufe der Zeit ändern kann. Dies ist eine sehr weit gefasste Definition und umfasst alles von einer Raumdatenbank bis zu einer Variablen in einer Klasse.
Der Status aller Android-Apps wird dem Nutzer angezeigt. Hier 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 Nutzer auf einem Bild zeichnen können.
Mit Jetpack Compose können Sie deutlich machen, wo und wie Sie Status in einer Android-App speichern und verwenden. Dieser Leitfaden konzentriert sich auf die Verbindung zwischen Status und zusammensetzbaren Funktionen sowie auf die APIs, die Jetpack Compose für die einfachere Arbeit mit Status anbietet.
Status und Zusammensetzung
Die Funktion „Compose“ ist deklarativ. Daher kann sie nur aktualisiert werden, indem dieselbe zusammensetzbare Funktion mit neuen Argumenten aufgerufen wird. Diese Argumente sind Darstellungen des UI-Status. Immer, wenn ein Status aktualisiert wird, findet eine Neuzusammensetzung statt. Deshalb werden Dinge wie TextField
nicht automatisch wie bei imperativen XML-basierten Ansichten aktualisiert. 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, passiert nichts. Das liegt daran, dass TextField
nicht automatisch aktualisiert wird, sondern aktualisiert wird, wenn sich der value
-Parameter ändert. Dies liegt an der Zusammensetzung und Neuzusammensetzung in der Funktion „Compose“.
Weitere Informationen zur anfänglichen Zusammensetzung und Neuzusammensetzung finden Sie unter In der Nachricht denken.
Status in zusammensetzbaren Funktionen
Zusammensetzbare Funktionen können die remember
API verwenden, um ein Objekt im Arbeitsspeicher zu speichern. Ein von remember
berechneter Wert wird bei der anfänglichen Zusammensetzung in der Zusammensetzung gespeichert und während der Neuzusammensetzung zurückgegeben.
Mit remember
können sowohl änderbare als auch unveränderliche Objekte gespeichert werden.
mutableStateOf
erstellt einen beobachtbaren MutableState<T>
. Dies ist ein beobachtbarer Typ, der in die compose-Laufzeit eingebunden ist.
interface MutableState<T> : State<T> {
override var value: T
}
Bei Änderungen an value
wird die Neuzusammensetzung aller zusammensetzbaren Funktionen geplant, die value
lesen.
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 gleichwertig und werden als Syntaxzucker für verschiedene Verwendungen des Zustands angegeben. Wählen Sie diejenige aus, die in der von Ihnen geschriebenen zusammensetzbaren Funktion den am einfachsten zu lesenden Code erzeugt.
Für die Delegate-Syntax by
sind folgende Importe erforderlich:
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 als Logik in Anweisungen verwenden, um zu ändern, welche zusammensetzbaren Funktionen angezeigt werden. Wenn Sie beispielsweise nicht möchten, dass die Begrüßung angezeigt wird, wenn der Name leer ist, verwenden Sie den Status in einer 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 Status bei Neuzusammensetzungen beibehalten, er wird jedoch nicht über Konfigurationsänderungen hinweg beibehalten. Dazu müssen Sie rememberSaveable
verwenden. rememberSaveable
speichert automatisch jeden Wert, der in einem Bundle
gespeichert werden kann. Für andere Werte können Sie ein benutzerdefiniertes Saver-Objekt übergeben.
Andere unterstützte Statustypen
Für das Composer-Programm müssen Sie den Status nicht mit MutableState<T>
speichern. Es unterstützt andere beobachtbare Typen. Bevor Sie einen anderen beobachtbaren Typ in Composer lesen, müssen Sie ihn in einen State<T>
konvertieren, damit zusammensetzbare Funktionen automatisch neu zusammengesetzt werden können, wenn sich der Status ändert.
Compose enthält Funktionen zum Erstellen von State<T>
aus gängigen beobachtbaren Typen, die in Android-Apps verwendet werden. Fügen Sie die entsprechenden Artefakte hinzu, bevor Sie diese Integrationen verwenden:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
erfasst Werte aus einemFlow
unter Berücksichtigung des Lebenszyklus, sodass Ihre Anwendung die Anwendungsressourcen schont. Er steht für den letzten ausgegebenen Wert aus dem Befehl „Compose“State
. Verwenden Sie diese API als empfohlene Methode zum Erfassen von Abläufen in Android-Apps.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich (muss 2.6.0-beta01 oder höher sein):
Kotlin
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
}
Groovig
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.2"
}
-
collectAsState
ähneltcollectAsStateWithLifecycle
, da auch Werte aus einerFlow
erfasst und in die Schaltfläche zum ErstellenState
umgewandelt werden.Verwenden Sie
collectAsState
für plattformunabhängigen Code anstelle voncollectAsStateWithLifecycle
(nur Android).Für
collectAsState
sind keine zusätzlichen Abhängigkeiten erforderlich, da die Funktion incompose-runtime
verfügbar ist. -
observeAsState()
beobachtet diesesLiveData
und stellt seine Werte überState
dar.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.6.1")
}
Groovig
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.6.1"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die die reaktiven Streams von RxJava2 (z.B.Single
,Observable
,Completable
) in ComposeState
umwandeln.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.6.1")
}
Groovig
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.6.1"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die die reaktiven Streams von RxJava3 (z.B.Single
,Observable
,Completable
) in ComposeState
umwandeln.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.6.1")
}
Groovig
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.6.1"
}
Zustandsorientiert im Vergleich zu zustandslos
Eine zusammensetzbare Funktion, die remember
zum Speichern eines Objekts verwendet, erstellt einen internen Status und macht die zusammensetzbare Funktion zustandsorientiert. HelloContent
ist ein Beispiel für eine zustandsorientierte zusammensetzbare Funktion, da sie ihren Status name
intern enthält und ändert. Dies kann in Situationen nützlich sein, in denen ein Aufrufer den Status nicht steuern muss und ihn verwenden kann, ohne den Status selbst verwalten zu müssen. Zusammensetzbare Funktionen mit internem Status sind jedoch in der Regel weniger wiederverwendbar und schwieriger zu testen.
Eine zustandslose zusammensetzbare Funktion ist eine zusammensetzbare Funktion, die keinen Status hat. Zustandslose Lösungen können auf einfache Weise mit Zustandshebungen erreicht werden.
Wenn Sie wiederverwendbare zusammensetzbare Funktionen entwickeln, möchten Sie häufig sowohl eine zustandsorientierte als auch eine zustandslose Version derselben zusammensetzbaren Funktion verfügbar machen. Die zustandsorientierte Version ist praktisch für Aufrufer, denen der Zustand nicht wichtig ist. Die zustandslose Version ist für Aufrufer erforderlich, die den Status steuern oder hochziehen müssen.
Staatliches Hochziehen
Das Hochstufen des Zustands in Compose ist ein Muster, bei dem der Status zum Aufrufer einer zusammensetzbaren Funktion verschoben wird, um eine zusammensetzbare Funktion zustandslos zu machen. Das allgemeine Muster für Zustandswinden in Jetpack Compose besteht darin, die Statusvariable durch zwei Parameter zu ersetzen:
value: T
: der aktuell anzuzeigende WertonValueChange: (T) -> Unit
:Ereignis, bei dem die Änderung des Werts angefordert wird, wobeiT
der vorgeschlagene neue Wert ist
Sie sind jedoch nicht auf onValueChange
beschränkt. Wenn spezifischere Ereignisse für die zusammensetzbare Funktion geeignet sind, sollten Sie sie mithilfe von Lambdas definieren.
Der Bundesstaat, der auf diese Weise aufgezogen wird, hat einige wichtige Eigenschaften:
- Single Source of Truth:Indem wir den Status verschieben, statt ihn zu duplizieren, stellen wir sicher, dass es nur eine zentrale Informationsquelle gibt. So lassen sich Programmfehler vermeiden.
- Kapselt:Nur zustandsorientierte zusammensetzbare Funktionen können ihren Status ändern. Es ist völlig intern.
- Teilbar: Der Status „Hochgezogen“ kann für mehrere zusammensetzbare Funktionen verwendet werden. Wenn Sie
name
in einer anderen zusammensetzbaren Funktion lesen möchten, ist das mit Winden möglich. - Interceptable: Aufrufer der zustandslosen zusammensetzbaren Funktionen können Ereignisse ignorieren oder ändern, bevor sie den Status ändern.
- Entkoppelt:Der Status für die zustandslosen zusammensetzbaren Funktionen kann an einem beliebigen Ort gespeichert werden. Beispielsweise ist es jetzt möglich,
name
in eineViewModel
zu verschieben.
Im Beispiel extrahieren Sie die name
und die onValueChange
aus HelloContent
und verschieben sie in der Baumstruktur nach oben in eine zusammensetzbare Funktion HelloScreen
, die HelloContent
aufruft.
@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") }) } }
Wenn Sie den Zustand aus HelloContent
hochziehen, ist es einfacher, die zusammensetzbare Funktion nachzuvollziehen, in verschiedenen Situationen wiederzuverwenden und zu testen. HelloContent
ist von der Speicherung des Status entkoppelt. Wenn Sie HelloScreen
ändern oder ersetzen, müssen Sie die Implementierung von HelloContent
bei der Entkopplung nicht ändern.
Das Muster, bei dem der Zustand abnimmt und die Ereignisse ansteigen, wird als unidirektionaler Datenfluss bezeichnet. In diesem Fall sinkt der Status von HelloScreen
auf HelloContent
und die Ereignisse steigen von HelloContent
auf HelloScreen
. Durch Befolgen des unidirektionalen Datenflusses können Sie zusammensetzbare Funktionen, die den Status in der UI anzeigen, von den Teilen Ihrer App entkoppeln, die den Status speichern und ändern.
Weitere Informationen finden Sie auf der Seite Windenstatus.
Status in „Compose“ wird wiederhergestellt
Die rememberSaveable
API verhält sich ähnlich wie remember
, weil sie den Status bei Neuzusammensetzungen und auch bei der Aktivitäts- oder Prozessneuerstellung unter Verwendung des gespeicherten Instanzstatusmechanismus beibehält. Das ist beispielsweise der Fall,
wenn der Bildschirm gedreht wird.
Möglichkeiten zum Speichern des Status
Alle Datentypen, die der Bundle
hinzugefügt werden, werden automatisch gespeichert. Wenn Sie etwas speichern möchten, das dem Bundle
nicht hinzugefügt werden kann, haben Sie mehrere Möglichkeiten.
Parieren
Die einfachste Lösung besteht darin, dem Objekt die Annotation @Parcelize
hinzuzufügen. Das Objekt wird parzelable und kann gebündelt werden. Durch diesen Code wird beispielsweise ein parzelabler City
-Datentyp erstellt und im Bundesstaat gespeichert.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
MapSaver
Wenn @Parcelize
nicht geeignet ist, können Sie mit mapSaver
eine eigene Regel zum Konvertieren eines Objekts in eine Reihe von Werten definieren, die das System in der 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")) } }
Listenspeicherung
Wenn Sie die Schlüssel für die Karte nicht definieren müssen, können Sie auch listSaver
verwenden und seine Indexe als Schlüssel verwenden:
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")) } }
Statusinhaber in Compose
Einfache Hebezüge können in den zusammensetzbaren Funktionen selbst verwaltet werden. Wenn jedoch die Logik zur Durchführung in zusammensetzbaren Funktionen ermittelt wird, empfiehlt es sich, die logischen und staatlichen Verantwortlichkeiten an andere Klassen zu delegieren: Inhaber von Bundesstaaten.
Weitere Informationen finden Sie in der Dokumentation zum Zustandswinden in der Compose-Dokumentation oder allgemeiner auf der Seite Zustandsinhaber und UI-Status im Architekturleitfaden.
Berechnungen neu auslösen, wenn Tasten sich ändern
Die remember
API wird häufig zusammen mit MutableState
verwendet:
var name by remember { mutableStateOf("") }
Hier sorgt die Funktion remember
dafür, dass der MutableState
-Wert Neuzusammensetzungen überdauert.
Im Allgemeinen verwendet remember
einen Lambda-Parameter calculation
. Wenn remember
zum ersten Mal ausgeführt wird, ruft es das Lambda calculation
auf und speichert das Ergebnis. Bei der Neuzusammensetzung gibt remember
den Wert zurück, der zuletzt gespeichert wurde.
Neben dem Caching-Status können Sie auch remember
verwenden, um Objekte oder Ergebnisse eines Vorgangs in der Zusammensetzung zu speichern, deren Initialisierung oder Berechnung aufwendig ist. Sie sollten diese Berechnung nicht bei jeder Neuzusammensetzung wiederholen.
Ein Beispiel hierfür ist das Erstellen dieses ShaderBrush
-Objekts, was ein teurer Vorgang ist:
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 Möglichkeit, den im Cache gespeicherten Wert zu entwerten. Die remember
API verwendet auch den Parameter key
oder keys
. Wenn sich einer dieser Schlüssel ändert, wird bei der nächsten Neuzusammensetzung remember
der Cache ungültig und die Berechnung wird noch einmal mit dem Lambda-Block ausgeführt. Dieser Mechanismus gibt Ihnen die Kontrolle über die Lebensdauer eines Objekts in der Komposition. Die Berechnung bleibt so lange gültig, bis sich die Eingaben ändern, und nicht so lange, bis der gespeicherte Wert die Zusammensetzung verlässt.
Die folgenden Beispiele zeigen, wie dieser Mechanismus funktioniert.
In diesem Snippet wird ein ShaderBrush
erstellt und als Hintergrundfarbe einer zusammensetzbaren Funktion Box
verwendet. remember
speichert die ShaderBrush
-Instanz, da die Neuerstellung teuer ist, wie oben erläutert. remember
verwendet avatarRes
als key1
-Parameter. Das ist das ausgewählte Hintergrundbild. Wenn sich avatarRes
ändert, wird der Pinsel mit dem neuen Bild neu zusammengesetzt und auf Box
angewendet. Das kann vorkommen, wenn der Nutzer in einer Auswahl ein anderes Bild als Hintergrund auswählt.
@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 einfache Zustands-Holder-Klasse
MyAppState
hochgezogen. Es stellt eine rememberMyAppState
-Funktion zur Verfügung, um eine Instanz der Klasse mit remember
zu initialisieren. Das Verfügbarmachen solcher Funktionen zum Erstellen einer Instanz, die Neuzusammensetzungen übersteht, ist ein gängiges Muster in Compose. Die Funktion rememberMyAppState
empfängt windowSizeClass
, das als key
-Parameter für remember
dient. Wenn sich dieser Parameter ändert, muss die Anwendung die Nur-Zustands-Holder-Klasse mit dem neuesten Wert neu erstellen. Das kann beispielsweise der Fall sein, 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 equals-Implementierung der Klasse, um zu entscheiden, ob sich ein Schlüssel geändert hat, und den gespeicherten Wert zu entwerten.
Status mit Schlüsseln über Neuzusammensetzung hinaus speichern
Die rememberSaveable
API ist ein Wrapper um remember
, mit dem Daten in einem Bundle
gespeichert werden können. Diese API ermöglicht es dem Zustand, nicht nur die Neuzusammensetzung, sondern auch die Wiederherstellung von Aktivitäten und das vom System initiierte Tod von Prozessen zu überleben.
rememberSaveable
empfängt input
-Parameter für denselben Zweck wie remember
keys
. Der Cache wird entwertet, wenn sich eine der Eingaben ändert. Bei der nächsten Neuzusammensetzung der Funktion führt rememberSaveable
den Lambda-Block für die Berechnung noch einmal aus.
Im folgenden Beispiel speichert rememberSaveable
userTypedQuery
, bis sich typedQuery
ändert:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
Weitere Informationen
Weitere Informationen zu State und Jetpack Compose finden Sie in den folgenden zusätzlichen Ressourcen.
Produktproben
Codelabs
Videos
Blogs
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Benutzeroberfläche zum Schreiben einer Nachricht erstellen
- UI-Status in „Compose“ speichern
- Nebeneffekte in Compose