In Compose ist die Benutzeroberfläche unveränderlich. Sie kann nach dem Zeichnen nicht mehr aktualisiert werden. Was Sie steuern können, ist der Status Ihrer Benutzeroberfläche. Jedes Mal, wenn sich der Status der Benutzeroberfläche ändert, erstellt Compose die Teile des UI-Baums neu, die sich geändert haben. Kompositionen können Status akzeptieren und Ereignisse verfügbar machen. Beispielsweise akzeptiert ein TextField einen Wert und macht einen Callback onValueChange verfügbar, 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 unidirektionale Datenflussmuster gut zu Jetpack Compose. In diesem Leitfaden wird beschrieben, wie Sie das Muster für den unidirektionalen Datenfluss in Compose implementieren, wie Sie Ereignisse und State Holder implementieren und wie Sie mit ViewModels in Compose arbeiten.
Unidirektionaler Datenfluss
Ein unidirektionaler Datenfluss (UDF) ist ein Designmuster, bei dem der Status nach unten und Ereignisse nach oben fließen. Wenn Sie dem unidirektionalen Datenfluss folgen, können Sie die Composables, die den Status in der Benutzeroberfläche anzeigen, von den Teilen Ihrer App entkoppeln, die den Status speichern und ändern.
Die UI-Aktualisierungsschleife 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 Buttonklick, der an das ViewModel zur Verarbeitung übergeben wird, oder ein Ereignis wird von anderen Ebenen Ihrer App übergeben, z. B. um anzuzeigen, dass die Nutzersitzung abgelaufen ist.
- Status aktualisieren: Ein Event-Handler kann den Status ändern.
- Anzeigestatus: Der State Holder gibt den Status weiter und die UI zeigt ihn an.
Wenn Sie dieses Muster bei der Verwendung von Jetpack Compose befolgen, bietet das mehrere Vorteile:
- Testbarkeit: Durch die Entkopplung des Status von der Benutzeroberfläche, die ihn anzeigt, können beide leichter isoliert getestet werden.
- Statuskapselung: Da der Status nur an einer Stelle aktualisiert werden kann und es nur eine einzige „Source of Truth“ für den Status einer zusammensetzbaren Funktion gibt, ist es weniger wahrscheinlich, dass Fehler aufgrund inkonsistenter Status auftreten.
- Konsistenz der Benutzeroberfläche: Alle Statusaktualisierungen werden sofort in der Benutzeroberfläche angezeigt, da beobachtbare State Holder wie
StateFlowoderLiveDataverwendet werden.
Unidirektionaler Datenfluss in Jetpack Compose
Composables funktionieren auf Grundlage von Status und Ereignissen. Beispiel: Ein TextField wird nur aktualisiert, wenn der Parameter value aktualisiert wird, und es wird ein onValueChange-Callback bereitgestellt – ein Ereignis, das die Änderung des Werts in einen neuen Wert anfordert. Compose definiert das State-Objekt als Wertinhaber und Änderungen am Zustandswert 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 – von einem hartcodierten Wert, von einem ViewModel oder von der übergeordneten zusammensetzbaren Funktion. Sie müssen ihn nicht in einem State-Objekt speichern, aber Sie müssen den Wert aktualisieren, wenn onValueChange aufgerufen wird.
mutableStateOf(value)erstellt einMutableState, einen beobachtbaren Typ in Compose. Bei jeder Änderung des Werts wird die Neuzusammensetzung aller zusammensetzbaren Funktionen geplant, die diesen Wert lesen.rememberspeichert Objekte in der Komposition und vergisst das Objekt, wenn die komponierbare Funktion, dierememberaufgerufen hat, aus der Komposition entfernt wird.rememberSaveablebehält den Status bei Konfigurationsänderungen bei, indem er in einemBundlegespeichert wird.
Kompositionsfähige Parameter definieren
Beachten Sie beim Definieren der Zustandsparameter einer zusammensetzbaren Funktion die folgenden Fragen:
- Wie wiederverwendbar oder flexibel ist das Composable?
- Wie wirken sich die Zustandsparameter auf die Leistung dieser zusammensetzbaren Funktion aus?
Um die Entkopplung und Wiederverwendung zu fördern, sollte jede zusammensetzbare Funktion so wenig Informationen wie möglich enthalten. Wenn du beispielsweise eine zusammensetzbare Funktion für die Überschrift eines Nachrichtenartikels erstellst, solltest du 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 jedes Mal neu zusammengesetzt, wenn eine neue Instanz von News an Header(news) übergeben wird, auch wenn sich title und subtitle nicht geändert haben.
Überlegen Sie sich genau, wie viele Parameter Sie übergeben. Eine Funktion mit zu vielen Parametern ist weniger benutzerfreundlich. In diesem Fall ist es besser, sie 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 Status der Benutzeroberfläche aktualisieren.
Die UI-Ebene sollte den Status niemals außerhalb eines Event-Handlers ändern, da dies zu Inkonsistenzen und Fehlern in Ihrer Anwendung führen kann.
Übergeben Sie vorzugsweise unveränderliche Werte für Status- und Event-Handler-Lambdas. Dieser Ansatz bietet folgende Vorteile:
- Sie verbessern die Wiederverwendbarkeit.
- Sie prüfen, ob die Benutzeroberfläche den Wert des Status direkt ändert.
- Sie vermeiden Nebenläufigkeitsprobleme, da Sie dafür sorgen, dass der Status nicht von einem anderen Thread geändert wird.
- Oft wird dadurch die Codekomplexität reduziert.
Eine zusammensetzbare Funktion, die beispielsweise einen String und einen Lambda-Ausdruck als Parameter akzeptiert, kann aus vielen Kontexten aufgerufen werden und ist in hohem Maße wiederverwendbar. Angenommen, die obere App-Leiste in Ihrer App zeigt immer Text an und hat einen Button „Zurück“. Sie können eine allgemeinere zusammensetzbare Funktion MyAppTopAppBar definieren, die den Text und den Handler für den Button „Zurück“ 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 Ihre App einführen, wenn eine der folgenden Bedingungen erfüllt ist:
- Der Status der Benutzeroberfläche wird mithilfe von beobachtbaren State Holdern wie
StateFlowoderLiveDataverfügbar gemacht. - Der
ViewModelverarbeitet Ereignisse, die von der UI oder anderen Ebenen Ihrer App stammen, und aktualisiert den State Holder basierend auf den Ereignissen.
Wenn Sie beispielsweise einen Anmeldebildschirm implementieren, sollte durch Tippen auf die Schaltfläche Anmelden ein Fortschrittsindikator und ein Netzwerkaufruf angezeigt werden. Wenn die Anmeldung erfolgreich war, wird in der App ein anderer Bildschirm angezeigt. Bei einem Fehler wird eine Snackbar angezeigt. So würden Sie den Bildschirmstatus und das Ereignis modellieren:
Das Display hat vier Status:
- Abgemeldet: Der Nutzer hat sich noch nicht angemeldet.
- In Bearbeitung: Wenn deine App versucht, den Nutzer über einen Netzwerkaufruf anzumelden.
- Error: wenn bei der Anmeldung ein Fehler aufgetreten ist.
- Angemeldet: wenn der Nutzer angemeldet ist.
Sie können diese Zustände als versiegelte Klasse modellieren. ViewModel stellt den Status als State bereit, legt den Anfangsstatus fest und aktualisiert den Status nach Bedarf. Der ViewModel verarbeitet das Anmeldeereignis auch, indem er 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, um sich als Listener zu registrieren und den Wert als Status darzustellen.
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:
Beispiele
Empfehlungen für Sie
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Zustand und Jetpack Compose
- UI-Status in Compose speichern
- Nutzereingaben verarbeiten