Der Status in 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. 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 können Sie explizit angeben, wo und wie Sie den Status in einer Android-App speichern und verwenden. In dieser Anleitung geht es um die Verbindung zwischen Status und zusammensetzbaren Funktionen sowie auf die APIs, die Jetpack Compose für die einfachere Arbeit mit Statusfunktionen bietet.
Status und Zusammensetzung
Die Funktion "Compose" ist deklarativ. Daher kann sie nur durch Aufrufen derselben zusammensetzbaren Funktion mit neuen Argumenten aktualisiert werden. Diese Argumente sind Darstellungen des UI-Status. Jedes Mal, wenn ein Status aktualisiert wird, findet eine Neuzusammensetzung statt. Daher werden Dinge wie TextField
nicht automatisch aktualisiert, wie dies in imperativen XML-basierten Ansichten der Fall ist. 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 liegt daran, dass TextField
nicht automatisch aktualisiert wird, sondern wenn sich der value
-Parameter ändert. Dies liegt an der Funktionsweise von Komposition und Neuzusammensetzung in Composer.
Weitere Informationen zur anfänglichen Komposition und Neuzusammensetzung finden Sie unter Thinking in Compose.
Status in zusammensetzbaren Funktionen
Zusammensetzbare Funktionen können mit der remember
API ein Objekt im Arbeitsspeicher speichern. Ein von remember
berechneter Wert wird während der anfänglichen Zusammensetzung in der Zusammensetzung gespeichert. Der gespeicherte Wert wird bei der Neuzusammensetzung zurückgegeben.
Mit remember
können sowohl änderbare als auch unveränderliche Objekte gespeichert werden.
Mit mutableStateOf
wird ein beobachtbarer MutableState<T>
erstellt. 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 äquivalent und werden als Syntax Zucker für verschiedene Verwendungen von Zustand bereitgestellt. Sie sollten diejenige auswählen, mit der der Code in der von Ihnen geschriebenen zusammensetzbaren Funktion am einfachsten zu lesen ist.
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 als Logik in Anweisungen verwenden, um zu ändern, welche zusammensetzbaren Funktionen angezeigt werden. Wenn Sie beispielsweise die Begrüßung nicht anzeigen möchten, 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, der Status wird jedoch bei Konfigurationsänderungen nicht beibehalten. Dazu müssen Sie rememberSaveable
verwenden. rememberSaveable
speichert automatisch alle Werte, die in einem Bundle
gespeichert werden können. Für andere Werte können Sie ein benutzerdefiniertes Saver-Objekt übergeben.
Andere unterstützte Statustypen
Zum Speichern des Status muss MutableState<T>
nicht verwendet werden. Es unterstützt andere beobachtbare Typen. Bevor Sie einen anderen beobachtbaren Typ in Composer lesen, müssen Sie ihn in eine 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. Bevor Sie diese Integrationen verwenden, fügen Sie die entsprechenden Artefakte hinzu, wie unten beschrieben:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
erfasst Werte aus einemFlow
unter Beachtung des Lebenszyklus, sodass die Anwendung Anwendungsressourcen schont. Es stellt den letzten ausgegebenen Wert aus der ErstellungState
dar. 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 (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"
}
-
collectAsState
ähneltcollectAsStateWithLifecycle
, da damit auch Werte aus einerFlow
erfasst und in das Schreiben-State
-Element umgewandelt werden.Verwende
collectAsState
für plattformunabhängigen Code anstelle voncollectAsStateWithLifecycle
, da dieser nur für Android verfügbar ist.Für
collectAsState
sind keine zusätzlichen Abhängigkeiten erforderlich, da es incompose-runtime
verfügbar ist. -
observeAsState()
beginnt mit der Beobachtung 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.8")
}
Cool
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.6.8"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die die reaktiven Streams von RxJava2 (z.B.Single
,Observable
,Completable
) in das Compose-ObjektState
umwandeln.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.6.8")
}
Cool
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.6.8"
}
-
subscribeAsState()
sind Erweiterungsfunktionen, die die reaktiven Streams von RxJava3 (z.B.Single
,Observable
,Completable
) in das Compose-ObjektState
umwandeln.Die folgende Abhängigkeit ist in der Datei
build.gradle
erforderlich:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.6.8")
}
Cool
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.6.8"
}
Zustandsorientiert oder 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 den Status name
intern enthält und ändert. Dies kann in Situationen nützlich sein, in denen ein Aufrufer den Status nicht steuern muss, sondern ihn verwenden kann, ohne den Status selbst verwalten zu müssen. Zusammensetzbare Funktionen mit internem Status sind jedoch in der Regel weniger wiederverwendbar und schwerer zu testen.
Eine zustandslose zusammensetzbare Funktion ist eine zusammensetzbare Funktion, die keinen Zustand enthält. Eine einfache Möglichkeit, zustandslos zu arbeiten, ist die Verwendung von Zustandsheben.
Bei der Entwicklung wiederverwendbarer zusammensetzbarer Funktionen empfiehlt es sich häufig, sowohl eine zustandsorientierte als auch eine zustandslose Version derselben zusammensetzbaren Funktion zur Verfügung zu stellen. Die zustandsorientierte Version ist für Aufrufer geeignet, für die der Status nicht relevant ist, und die zustandslose Version für Aufrufer, die den Status steuern oder hochfahren müssen.
Staatliche Winden
Das Hochheben von Status in Compose ist ein Muster, das sich in den Aufrufer einer zusammensetzbaren Funktion bewegt, um eine zusammensetzbare zustandslose zu machen. Das allgemeine Muster für Zustandswinden in Jetpack Compose besteht darin, die Zustandsvariable durch zwei Parameter zu ersetzen:
value: T
: der aktuell anzuzeigende WertonValueChange: (T) -> Unit
: Ein Ereignis, das eine Änderung des Werts anfordert, wobeiT
der vorgeschlagene neue Wert ist
Sie sind jedoch nicht auf onValueChange
beschränkt. Wenn für die zusammensetzbare Funktion spezifischere Ereignisse geeignet sind, sollten Sie sie mithilfe von Lambdas definieren.
Ein Zustand, der auf diese Weise gezogen wird, hat einige wichtige Eigenschaften:
- Single Source of Truth:Durch das Verschieben des Zustands anstelle des Duplizierens wird sichergestellt, 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. Sie ist komplett intern.
- Gemeinsam nutzbar: Der Hub-Zustand kann mit mehreren zusammensetzbaren Funktionen geteilt werden. Wenn Sie
name
in einer anderen zusammensetzbaren Funktion lesen möchten, können Sie dies mit Winden erreichen. - Interceptable:Aufrufer der zustandslosen zusammensetzbaren Funktionen können Ereignisse vor dem Ändern des Status ignorieren oder ändern.
- Entkoppelt: Der Status für die zustandslosen zusammensetzbaren Funktionen kann überall gespeichert werden. Beispielsweise ist es jetzt möglich,
name
in einenViewModel
zu verschieben.
Im Beispielfall extrahieren Sie name
und onValueChange
aus HelloContent
und verschieben sie in der Baumstruktur nach oben in eine zusammensetzbare HelloScreen
-Funktion, 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
verschieben, ist es einfacher, die zusammensetzbare Funktion zu analysieren, sie in verschiedenen Situationen zu verwenden und zu testen. HelloContent
wird von der Speicherung seines Status entkoppelt. Wenn Sie HelloScreen
ändern oder ersetzen, müssen Sie die Implementierung von HelloContent
nicht ändern.
Das Muster, bei dem der Zustand abfällt 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
an. Indem Sie dem unidirektionalen Datenfluss folgen, können Sie zusammensetzbare Funktionen, die den Status in der UI anzeigen, von den Teilen Ihrer App entkoppeln, die ihren 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
, da sie den Status bei Neuzusammensetzungen und auch bei der Neuerstellung von Aktivitäten oder Prozessen mit dem Mechanismus für den gespeicherten Instanzstatus beibehält. Das passiert beispielsweise,
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, haben Sie mehrere Möglichkeiten.
Paket
Die einfachste Lösung besteht darin, dem Objekt die Annotation @Parcelize
hinzuzufügen. Das Objekt wird geparst und kann gebündelt werden. Mit diesem Code wird beispielsweise ein parzellenbarer City
-Datentyp erstellt und im Status 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
aus irgendeinem Grund nicht geeignet ist, können Sie mit mapSaver
Ihre eigene Regel zum Konvertieren eines Objekts in eine Gruppe 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")) } }
Listenspeicher
Damit Sie die Schlüssel für die Zuordnung nicht definieren müssen, können Sie auch listSaver
verwenden und die 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")) } }
Staatsinhaber in Compose
Einfache Winden können in den zusammensetzbaren Funktionen selbst verwaltet werden. Wenn jedoch die Menge an Zustand, mit der Erhöhungen verfolgt werden sollen, oder die Logik, die in zusammensetzbaren Funktionen ausgeführt werden soll, eintritt, empfiehlt es sich, die Logik- und Statuszuständigkeiten an andere Klassen zu delegieren: State Owners.
Weitere Informationen finden Sie in der Dokumentation zu State Hoisting in Compose oder allgemein auf der Seite State holder 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 sorgt die remember
-Funktion dafür, dass der MutableState
-Wert Neuzusammensetzungen überdauert.
Im Allgemeinen verwendet remember
einen Lambda-Parameter calculation
. Bei der ersten Ausführung von remember
wird das Lambda calculation
aufgerufen und das Ergebnis gespeichert. Bei der Neuzusammensetzung gibt remember
den zuletzt gespeicherten Wert zurück.
Neben dem Caching-Status können Sie remember
auch verwenden, um ein Objekt oder das Ergebnis eines Vorgangs in der Zusammensetzung zu speichern, dessen Initialisierung oder Berechnung aufwendig ist. Vielleicht möchten Sie diese Berechnung nicht bei jeder Neuzusammensetzung wiederholen.
Ein Beispiel ist das Erstellen dieses ShaderBrush
-Objekts. Dieser Vorgang ist kostspielig:
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 einen key
- oder keys
-Parameter. Wenn sich einer dieser Schlüssel ändert, wird bei der nächsten Neuzusammensetzung der Funktion remember
den Cache ungültig machen und den Lambda-Block für die Berechnung noch einmal ausführen. Mit diesem Mechanismus können Sie die Lebensdauer eines Objekts in der Zusammensetzung steuern. Die Berechnung bleibt gültig, bis sich die Eingaben ändern, und nicht so lange, bis der gemerkte Wert die Zusammensetzung verlässt.
Die folgenden Beispiele zeigen, wie dieser Mechanismus funktioniert.
In diesem Snippet wird ein ShaderBrush
erstellt und als Hintergrundpaint für eine zusammensetzbare Box
-Funktion verwendet. remember
speichert die Instanz ShaderBrush
, da die Neuerstellung, wie oben erläutert, kostspielig ist. remember
verwendet avatarRes
als key1
-Parameter. Dies ist das ausgewählte Hintergrundbild. Wenn sich avatarRes
ändert, wird der Pinsel mit dem neuen Bild neu zusammengesetzt und auf Box
angewendet. Dies kann vorkommen, wenn der Nutzer ein anderes Bild als Hintergrund in einer Auswahl 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 State Holder-Klasse
MyAppState
hochgezogen. Sie stellt eine rememberMyAppState
-Funktion zur Verfügung, um eine Instanz der Klasse mit remember
zu initialisieren. Ein gängiges Muster in Compose ist es, solche Funktionen zum Erstellen einer Instanz verfügbar zu machen, die Neuzusammensetzungen übersteht. Die Funktion rememberMyAppState
empfängt windowSizeClass
, das als key
-Parameter für remember
dient. Wenn sich dieser Parameter ändert, muss die Anwendung die einfache State Holder-Klasse mit dem neuesten Wert neu erstellen. Dies 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 ein Schlüssel geändert wurde, und den gespeicherten Wert entwertet.
Status mit Schlüsseln über die Neuzusammensetzung hinaus speichern
Die rememberSaveable
API ist ein Wrapper um remember
, der Daten in einem Bundle
speichern kann. Diese API ermöglicht es dem Status nicht nur, die Neuzusammensetzung, sondern auch die Wiederherstellung von Aktivitäten und den vom System initiierten Prozess zu überleben.
rememberSaveable
empfängt input
-Parameter für denselben Zweck wie remember
keys
. Der Cache wird ungültig, 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 Status 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 für das Schreiben erstellen
- UI-Status in „Compose“ speichern
- Nebeneffekte in der Funktion „Schreiben“