In Compose ist die Benutzeroberfläche unveränderlich. Sie kann nach dem Rendern nicht mehr aktualisiert werden. Sie können den Status Ihrer Benutzeroberfläche steuern. Jedes Mal, wenn sich der Status der Benutzeroberfläche ändert, erstellt Compose die Teile des UI-Baums neu, die sich geändert haben. Composables können Status akzeptieren und Ereignisse bereitstellen. Ein TextField akzeptiert beispielsweise einen Wert und stellt einen Callback onValueChange bereit, der den Callback-Handler auffordert, den Wert zu ändern.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
Da Composables Status akzeptieren und Ereignisse verfügbar machen, passt das Muster des unidirektionalen Datenflusses gut zu Jetpack Compose. In diesem Leitfaden geht es darum, wie Sie das unidirektionale Datenflussmuster in Compose implementieren, wie Sie Ereignisse und Statusinhaber implementieren und wie Sie mit ViewModels in Compose arbeiten.
Unidirektionaler Datenfluss
Ein unidirektionaler Datenfluss (Unidirectional Data Flow, UDF) ist ein Designmuster, bei dem der Status nach unten und Ereignisse nach oben fließen. Durch die Einhaltung des unidirektionalen Datenflusses können Sie Composables, die den Status in der Benutzeroberfläche anzeigen, von den Teilen Ihrer App entkoppeln, in denen der Status gespeichert und geändert wird.
Der UI-Aktualisierungszyklus für eine App mit unidirektionalem Datenfluss sieht so aus:
- Ereignis: Ein Teil der Benutzeroberfläche generiert ein Ereignis und übergibt es nach oben, z. B. ein Klick auf eine Schaltfläche, der an das ViewModel übergeben wird, um ihn zu verarbeiten. Oder ein Ereignis wird von anderen Ebenen Ihrer App übergeben, z. B. um anzugeben, dass die Nutzersitzung abgelaufen ist.
- Status aktualisieren: Ein Ereignishandler kann den Status ändern.
- Anzeigestatus: Der Statusinhaber übergibt den Status und die Benutzeroberfläche zeigt ihn an.
Wenn Sie dieses Muster bei der Verwendung von Jetpack Compose befolgen, ergeben sich mehrere Vorteile:
- Testbarkeit: Durch die Entkopplung des Status von der Benutzeroberfläche, auf der er angezeigt wird, lassen sich beide leichter isoliert testen.
- Statuskapselung: Da der Status nur an einer Stelle aktualisiert werden kann und es nur eine Quelle der Wahrheit für den Status einer Composable gibt, ist es weniger wahrscheinlich, dass Sie aufgrund inkonsistenter Status Fehler verursachen.
- Konsistenz der Benutzeroberfläche: Alle Statusaktualisierungen werden sofort in der Benutzeroberfläche widergespiegelt, da beobachtbare Status-Holder wie
StateFlowoderLiveDataverwendet werden.
Unidirektionaler Datenfluss in Jetpack Compose
Composables funktionieren auf Grundlage von Status und Ereignissen. Ein TextField wird beispielsweise nur aktualisiert, wenn der zugehörige value-Parameter aktualisiert wird und ein onValueChange-Callback verfügbar ist. Das ist ein Ereignis, bei dem der Wert in einen neuen Wert geändert werden soll. Mit „Compose“ wird das State-Objekt als Wert-Holder definiert. Änderungen am Statuswert lösen eine Neuzusammensetzung aus. Sie können den Status in einem remember { mutableStateOf(value) } oder einem rememberSaveable { mutableStateOf(value) speichern, je nachdem, wie lange Sie sich den Wert merken müssen.
Der Typ des Werts der zusammensetzbaren Funktion TextField ist String. Er kann also von überall stammen, z. B. von einem fest codierten Wert, von einem ViewModel oder von der übergeordneten zusammensetzbaren Funktion. Sie müssen den Wert nicht in einem State-Objekt speichern, aber Sie müssen ihn aktualisieren, wenn onValueChange aufgerufen wird.
Zusammensetzbare Parameter definieren
Beachten Sie beim Definieren der Statusparameter eines Composables die folgenden Fragen:
- Wie wiederverwendbar oder flexibel ist die Composable-Funktion?
- Wie wirken sich die Statusparameter auf die Leistung dieser Composable aus?
Um die Entkopplung und Wiederverwendung zu fördern, sollte jede Composable-Funktion so wenig Informationen wie möglich enthalten. Wenn Sie beispielsweise eine zusammensetzbare Funktion erstellen, die den Header eines Nachrichtenartikels enthält, sollten Sie nur die Informationen übergeben, die angezeigt werden müssen, und nicht den gesamten Nachrichtenartikel:
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
Manchmal verbessert die Verwendung einzelner Parameter auch die Leistung. Wenn News beispielsweise mehr Informationen als nur title und subtitle enthält, wird die zusammensetzbare Funktion bei jeder neuen Instanz von News, die an Header(news) übergeben wird, neu zusammengesetzt, auch wenn sich title und subtitle nicht geändert haben.
Überlegen Sie sich genau, wie viele Parameter Sie übergeben. Wenn eine Funktion zu viele Parameter hat, wird die Verwendung der Funktion erschwert. In diesem Fall ist es daher besser, die Parameter in einer Klasse zu gruppieren.
Ereignisse in Compose
Jede Eingabe in Ihre App sollte als Ereignis dargestellt werden: Tippen, Textänderungen und sogar Timer oder andere Aktualisierungen. Da diese Ereignisse den Status der Benutzeroberfläche ändern, sollte die ViewModel sie verarbeiten und den UI-Status aktualisieren.
Die UI-Ebene sollte den Status niemals außerhalb eines Ereignis-Handlers ändern, da dies zu Inkonsistenzen und Fehlern in Ihrer Anwendung führen kann.
Es wird empfohlen, unveränderliche Werte für Status- und Ereignishandler-Lambdas zu übergeben. Dieser Ansatz bietet folgende Vorteile:
- Sie verbessern die Wiederverwendbarkeit.
- Sie prüfen, ob die Benutzeroberfläche den Wert des Status nicht direkt ändert.
- Sie vermeiden Probleme mit der Nebenläufigkeit, da Sie dafür sorgen, dass der Status nicht von einem anderen Thread aus geändert wird.
- Oft wird die Codekomplexität reduziert.
Eine zusammensetzbare Funktion, die beispielsweise ein String und ein Lambda als Parameter akzeptiert, kann aus vielen Kontexten aufgerufen werden und ist sehr wiederverwendbar. Angenommen, in der oberen App-Leiste Ihrer App wird immer Text angezeigt und sie hat eine Schaltfläche „Zurück“. Sie können eine allgemeinere MyAppTopAppBar-Composable definieren, die den Text und das Back-Button-Handle als Parameter empfängt:
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.AutoMirrored.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
ViewModels, Status und Ereignisse: ein Beispiel
Mit ViewModel und mutableStateOf können Sie auch einen unidirektionalen Datenfluss in Ihrer App einführen, wenn eine der folgenden Bedingungen zutrifft:
- Der Status Ihrer Benutzeroberfläche wird über beobachtbare Status-Holder wie
StateFlowoderLiveDataverfügbar gemacht. - Die
ViewModelverarbeitet Ereignisse, die von der Benutzeroberfläche oder anderen Ebenen Ihrer App stammen, und aktualisiert den Status-Holder basierend auf den Ereignissen.
Wenn Sie beispielsweise einen Anmeldebildschirm implementieren, sollte durch Tippen auf die Schaltfläche Anmelden ein Fortschritts-Spinner und ein Netzwerkaufruf in Ihrer App angezeigt werden. Wenn die Anmeldung erfolgreich war, wird in Ihrer App ein anderer Bildschirm aufgerufen. Im Fehlerfall wird in der App eine Snackbar angezeigt. So modellieren Sie den Bildschirmstatus und das Ereignis:
Der Bildschirm hat vier Status:
- Abgemeldet: wenn der Nutzer noch nicht angemeldet ist.
- Wird ausgeführt: wenn Ihre App versucht, den Nutzer durch einen Netzwerkaufruf anzumelden.
- Fehler: Wenn bei der Anmeldung ein Fehler aufgetreten ist.
- Angemeldet: wenn der Nutzer angemeldet ist.
Sie können diese Status als versiegelte Klasse modellieren. Die ViewModel macht den Status als State verfügbar, legt den Anfangszustand fest und aktualisiert den Status nach Bedarf. Die ViewModel verarbeitet auch das Anmeldeereignis, indem sie eine onSignIn()-Methode bereitstellt.
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
Zusätzlich zur mutableStateOf API bietet Compose Erweiterungen für LiveData, Flow und Observable, die als Listener registriert werden können und den Wert als Status darstellen.
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
Weitere Informationen
Weitere Informationen zur Architektur in Jetpack Compose finden Sie in den folgenden Ressourcen:
Produktproben
Empfehlungen für Sie
- Hinweis: Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Zustand und Jetpack Compose
- UI-Status in Compose speichern
- Nutzereingabe verarbeiten