In „Compose“ ist die UI unveränderlich. Sie kann nach dem Zeichnen nicht mehr aktualisiert werden. Sie können nur den Status Ihrer UI steuern. Jedes Mal, wenn sich der Status der UI ändert, werden bei Compose die geänderten Teile der UI-Struktur neu erstellt. Zusammensetzbare Funktionen können Statuswerte akzeptieren und Ereignisse offenlegen. Ein TextField
akzeptiert beispielsweise einen Wert und stellt eine Callback-onValueChange
bereit, die den Callback-Handler auffordert, den Wert zu ändern.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
Da zusammensetzbare Funktionen Zustands- und Freigabeereignisse akzeptieren, passt das Muster des unidirektionalen Datenflusses gut zu Jetpack Compose. In diesem Leitfaden erfahren Sie, wie Sie das Muster des unidirektionalen Datenflusses in Compose implementieren, Ereignisse und Statusinhaber implementieren und in Compose mit ViewModels arbeiten.
Unidirektionaler Datenfluss
Ein unidirektionaler Datenfluss (UDF) ist ein Designmuster, bei dem der Zustand nach unten und die Ereignisse nach oben fließen. 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.
Die Aktualisierungsschleife der Benutzeroberfläche für eine App mit unidirektionalem Datenfluss sieht so aus:
- Ereignis: Ein Teil der UI generiert ein Ereignis und übergibt es nach oben. Das kann beispielsweise ein Klick auf eine Schaltfläche sein, der zur Verarbeitung an die ViewModel übergeben wird. Es kann auch ein Ereignis von anderen Ebenen Ihrer App übergeben werden, um beispielsweise anzugeben, dass die Nutzersitzung abgelaufen ist.
- Aktualisierungsstatus: Ein Event-Handler kann den Status ändern.
- Anzeigestatus: Der Statusinhaber gibt den Status weiter und wird auf der UI angezeigt.
Dieses Muster bei der Verwendung von Jetpack Compose bietet mehrere Vorteile:
- Testbarkeit: Durch die Entkopplung des Status von der angezeigten Benutzeroberfläche lassen sich beide Elemente leichter isoliert testen.
- Datenkapselung: Da der Status nur an einem Ort aktualisiert werden kann und es nur eine zentrale Datenquelle für den Status einer zusammensetzbaren Funktion gibt, ist es weniger wahrscheinlich, dass es aufgrund von inkonsistenten Status zu Fehlern kommt.
- UI-Konsistenz: Alle Statusaktualisierungen werden sofort in der UI angezeigt, wenn beobachtbare Statusinhaber wie
StateFlow
oderLiveData
verwendet werden.
Unidirektionaler Datenfluss in Jetpack Compose
Zusammensetzbare Funktionen basieren auf Status und Ereignissen. Ein TextField
-Objekt wird beispielsweise nur dann aktualisiert, wenn sein value
-Parameter aktualisiert wird und ein onValueChange
-Callback verfügbar ist – ein Ereignis, bei dem die Änderung des Werts in einen neuen Wert angefordert wird. Bei der Zusammensetzung wird das State
-Objekt als Wertinhaber definiert. Änderungen am Statuswert lösen eine Neuzusammensetzung aus. Sie können den Status in einem remember { mutableStateOf(value) }
- oder rememberSaveable { mutableStateOf(value)
-Objekt enthalten, je nachdem, wie lange Sie sich den Wert merken müssen.
Der Typ der zusammensetzbaren Funktion TextField
ist String
. Dieser kann also von überall stammen – von einem hartcodierten Wert, aus einem ViewModel oder aus der übergeordneten zusammensetzbaren Funktion. Sie müssen ihn nicht in einem State
-Objekt enthalten, müssen aber den Wert aktualisieren, wenn onValueChange
aufgerufen wird.
Zusammensetzbare Parameter definieren
Beachten Sie beim Definieren der Statusparameter einer zusammensetzbaren Funktion die folgenden Fragen:
- Wie wiederverwendbar oder flexibel ist die zusammensetzbare Funktion?
- Wie wirken sich die Zustandsparameter auf die Leistung dieser zusammensetzbaren Funktion aus?
Um 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 den Header eines Nachrichtenartikels erstellst, solltest du statt des gesamten Nachrichtenartikels nur die Informationen übergeben, die angezeigt werden müssen:
@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 die Leistung. Beispiel: Wenn News
mehr Informationen als nur title
und subtitle
enthält und eine neue Instanz von News
an Header(news)
übergeben wird, wird die zusammensetzbare Funktion neu zusammengesetzt, auch wenn title
und subtitle
nicht geändert wurden.
Überlegen Sie sich genau, wie viele Parameter Sie übergeben. Eine Funktion mit zu vielen Parametern reduziert die Ergonomie der Funktion. Daher 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. Wenn sich durch diese Ereignisse der Status der UI ändert, sollte ViewModel
sie verarbeiten und den UI-Status aktualisieren.
Die UI-Ebene sollte niemals den Status außerhalb eines Event-Handlers ändern, da dies zu Inkonsistenzen und Programmfehlern in Ihrer Anwendung führen kann.
Übergeben unveränderlicher Werte für Status- und Event-Handler-Lambdas bevorzugt. Dieser Ansatz bietet folgende Vorteile:
- Sie verbessern die Wiederverwendbarkeit.
- Achten Sie darauf, dass 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 geändert wird.
- Oft verringern Sie die Komplexität des Codes.
Beispielsweise kann eine zusammensetzbare Funktion, die String
und Lambda als Parameter akzeptiert, aus vielen Kontexten aufgerufen werden und ist in hohem Maße wiederverwendbar. Angenommen, die obere App-Leiste in Ihrer App enthält immer Text und eine Schaltfläche „Zurück“. Sie können eine allgemeinere zusammensetzbare Funktion MyAppTopAppBar
definieren, die den Text und das Handle der Schaltfläche „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.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
ViewModels, Status und Ereignisse: Beispiel
Durch die Verwendung von ViewModel
und mutableStateOf
können Sie auch einen unidirektionalen Datenfluss in Ihre Anwendung einführen, wenn eine der folgenden Bedingungen zutrifft:
- Der Status Ihrer UI wird über beobachtbare Statusinhaber wie
StateFlow
oderLiveData
angegeben. - Der
ViewModel
verarbeitet Ereignisse, die von der UI oder anderen Ebenen Ihrer App stammen, und aktualisiert den Statusinhaber anhand der Ereignisse.
Wenn Sie beispielsweise bei der Implementierung eines Anmeldebildschirms auf die Schaltfläche Anmelden tippen, sollten in Ihrer App ein Fortschrittssymbol und ein Netzwerkaufruf angezeigt werden. Wenn die Anmeldung erfolgreich war, wechselt deine App zu einem anderen Bildschirm. Bei einem Fehler zeigt die App eine Snackbar an. So modellieren Sie den Bildschirmstatus und das Ereignis:
Der Bildschirm hat vier Status:
- Abgemeldet: Der Nutzer hat sich noch nicht angemeldet.
- In Bearbeitung: Ihre App versucht gerade, den Nutzer über einen Netzwerkaufruf anzumelden.
- Fehler: Bei der Anmeldung ist ein Fehler aufgetreten.
- Angemeldet: Der Nutzer ist angemeldet.
Sie können diese Zustände als versiegelte Klasse modellieren. Der ViewModel
zeigt den Status als State
an, legt den Anfangszustand fest und aktualisiert den Status nach Bedarf. ViewModel
verarbeitet außerdem das Anmeldeereignis und stellt dazu eine onSignIn()
-Methode bereit.
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:
Produktproben
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- State und Jetpack Compose
- UI-Status in „Compose“ speichern
- Nutzereingaben verarbeiten