UI-Ebene

Die Rolle der Benutzeroberfläche besteht darin, die App-Daten auf dem Bildschirm anzuzeigen der primäre Interaktionspunkt der Nutzenden sein. Wenn sich die Daten ändern, entweder aufgrund einer Nutzerinteraktion (z. B. Drücken einer Taste) oder einer externen Eingabe (z. B. Netzwerkantwort) aktualisiert wird, sollte die Benutzeroberfläche entsprechend aktualisiert werden. Die Benutzeroberfläche ist also eine visuelle Darstellung des Anwendungsstatus aus der Datenschicht abgerufen werden.

Die Anwendungsdaten, die Sie aus der Datenschicht erhalten, befinden sich jedoch als die Informationen, die Sie anzeigen möchten. Zum Beispiel haben Sie benötigt nur einen Teil der Daten für die Benutzeroberfläche oder Sie müssen zwei verschiedene Datenquellen verwenden, um für den Nutzer relevante Informationen zu präsentieren. Unabhängig von der angewendeten Logik müssen Sie der Benutzeroberfläche alle Informationen vollständig gerendert werden muss. Die UI-Ebene ist die Pipeline, werden die Anwendungsdaten in ein Formular geändert, das von der Benutzeroberfläche dargestellt werden kann.

<ph type="x-smartling-placeholder">
</ph> In einer typischen Architektur hängen die UI-Elemente der UI-Ebene vom Zustand ab.
    die wiederum von Klassen aus der Datenschicht oder dem
    optionale Domain-Ebene.
<ph type="x-smartling-placeholder">
</ph> Abbildung 1: Die Rolle der UI-Ebene in der Anwendungsarchitektur

Eine grundlegende Fallstudie

Stellen Sie sich eine App vor, die Nachrichtenartikel abruft, die ein Nutzer lesen kann. Die App hat ein Artikelbildschirm, auf dem Artikel angezeigt werden, die zum Lesen verfügbar sind, und auf der angemeldeten Nutzern, um interessante Artikel als Lesezeichen zu speichern. In Anbetracht dessen, dass zu einem beliebigen Zeitpunkt viele Artikel befinden, sollte der Leser in der Lage sein, Artikel nach Kategorie sortiert. Zusammenfassend lässt sich sagen, dass Nutzer mit der App Folgendes tun können:

  • Zum Lesen verfügbare Artikel anzeigen.
  • Artikel nach Kategorie durchsuchen
  • Melden Sie sich an und speichern Sie bestimmte Artikel als Lesezeichen.
  • Zugriff auf einige Premium-Funktionen (sofern berechtigt).
<ph type="x-smartling-placeholder">
</ph>
<ph type="x-smartling-placeholder">
</ph> Abbildung 2: Eine Beispiel-Nachrichten-App für eine UI-Fallstudie.

In den folgenden Abschnitten wird dieses Beispiel als Fallstudie verwendet, um die Prinzipien des unidirektionalen Datenflusses und veranschaulichen die Probleme die diese Prinzipien im Kontext der App-Architektur für die UI lösen können. Ebene.

Architektur der UI-Ebene

Der Begriff UI bezieht sich auf UI-Elemente wie Aktivitäten und Fragmente, die Daten anzuzeigen, unabhängig von den APIs, die sie hierfür verwenden (Aufrufe oder Jetpack Compose). Da die Rolle der Daten ist das Speichern, Verwalten und Gewähren von Zugriff auf App-Daten enthält, muss die UI-Ebene folgende Schritte ausführen:

  1. App-Daten verarbeiten und in Daten umwandeln, die die Benutzeroberfläche problemlos rendern kann
  2. Daten, die über die UI gerendert werden können, werden für die Präsentation in UI-Elemente umgewandelt. für den Nutzer.
  3. Nutzereingabeereignisse aus diesen zusammengesetzten UI-Elementen verarbeiten und ihre Auswirkungen in den UI-Daten.
  4. Wiederholen Sie die Schritte 1 bis 3 so lange wie nötig.

Im weiteren Verlauf dieses Leitfadens wird gezeigt, wie Sie eine UI-Ebene implementieren, die führen Sie diese Schritte aus. In diesem Leitfaden werden insbesondere die folgenden Aufgaben und Konzepte behandelt:

  • So definieren Sie den UI-Status.
  • Unidirektionaler Datenfluss (Unidirektionaler Datenfluss, UDF) zur Erstellung und Verwaltung der Benutzeroberfläche Bundesstaat.
  • Anleitung zum Bereitstellen des UI-Status mit beobachtbaren Datentypen gemäß UDF-Prinzipien.
  • So implementieren Sie eine UI, die den beobachtbaren UI-Status verwendet.

Das Wichtigste ist die Definition des UI-Status.

UI-Status definieren

Weitere Informationen finden Sie in der zuvor beschriebenen Fallstudie. Kurz gesagt zeigt die Benutzeroberfläche Eine Liste von Artikeln und einige Metadaten zu jedem Artikel. Diese Informationen die die App dem Nutzer präsentiert, ist der UI-Status.

Mit anderen Worten: Wenn die Benutzeroberfläche das ist, was der Nutzer sieht, entspricht der UI-Status der App. was sie sehen sollten. Wie zwei Seiten derselben Medaille ist die Benutzeroberfläche die visuelle Darstellung des UI-Status. Alle Änderungen am Status der Benutzeroberfläche werden sofort wirksam in der UI angezeigt.

<ph type="x-smartling-placeholder">
</ph> Die Benutzeroberfläche ist das Ergebnis der Bindung von UI-Elementen auf dem Bildschirm mit dem UI-Status.
<ph type="x-smartling-placeholder">
</ph> Abbildung 3: UI ist das Ergebnis der Bindung von UI-Elementen auf dem Bildschirm mit dem UI-Status

Betrachten Sie die Fallstudie. um die Anforderungen der Nachrichten-App zu erfüllen, Informationen, die zum vollständigen Rendern der Benutzeroberfläche erforderlich sind, können in einem Die Datenklasse NewsUiState ist so definiert:

data class NewsUiState(
    val isSignedIn: Boolean = false,
    val isPremium: Boolean = false,
    val newsItems: List<NewsItemUiState> = listOf(),
    val userMessages: List<Message> = listOf()
)

data class NewsItemUiState(
    val title: String,
    val body: String,
    val bookmarked: Boolean = false,
    ...
)

Unveränderlichkeit

Die Definition des UI-Status im obigen Beispiel ist unveränderlich. Der Hauptvorteil von dass unveränderliche Objekte Garantien bezüglich des Zustands der Anwendung finden können. Dadurch kann sich die Benutzeroberfläche Rolle: zum Lesen des Status und zum entsprechenden Aktualisieren der UI-Elemente. Daher sollten Sie den Status der Benutzeroberfläche in der Benutzeroberfläche nie direkt ändern, es sei denn, die Benutzeroberfläche selbst ist der der alleinigen Quelle seiner Daten. Eine Verletzung dieses Grundsatzes führt zu mehreren Quellen von für dieselbe Information, was zu Dateninkonsistenzen und kleinen Insekten.

Wenn beispielsweise das Flag bookmarked in einem NewsItemUiState-Objekt aus der UI in der Fallstudie in der Klasse Activity aktualisiert wurden, wäre dieses Flag mit der Datenschicht als Quelle des Lesezeichen-Status Artikel. Unveränderliche Datenklassen sind sehr nützlich, um diese Art von oder Anti-Pattern.

Namenskonventionen in diesem Leitfaden

In diesem Handbuch werden UI-Zustandsklassen basierend auf der Funktionalität des oder einen Teil des Bildschirms, den sie beschreiben. Die Konvention lautet wie folgt:

functionality + UiState.

Der Status eines Bildschirms, auf dem Nachrichten angezeigt werden, kann z. B. aufgerufen werden, NewsUiState und der Status einer Nachricht in einer Liste von Nachrichtenartikeln kann ein NewsItemUiState.

Status mit unidirektionalem Datenfluss verwalten

Im vorherigen Abschnitt wurde festgelegt, dass der UI-Status ein unveränderlicher Snapshot die für das Rendern der UI erforderlichen Details enthält. Die dynamische Natur der Daten in Apps bedeutet, dass sich der Status im Laufe der Zeit ändern kann. Dies könnte auf eine Interaktionen oder anderen Ereignissen, die die zugrunde liegenden Daten verändern, in der App dargestellt werden.

Diese Interaktionen können von einem Vermittler profitieren, der sie verarbeitet, auf jedes Ereignis angewendete Logik und die erforderlichen Transformationen an die unterstützenden Datenquellen übergeben, um den UI-Status zu erstellen. Diese Interaktionen und Die Logik ist möglicherweise in der Benutzeroberfläche selbst enthalten, dies kann jedoch schnell unübersichtlich werden, wird die Benutzeroberfläche mehr, als ihr Name vermuten lässt: Sie wird zu Dateninhabern, Producer, Transformator und mehr. Außerdem kann dies die Testbarkeit beeinträchtigen da der resultierende Code ein eng gekoppeltes Amalgam ist, grenzen. Letztendlich profitiert die Benutzeroberfläche von reduzierter Belastung. Es sei denn, die Der Status der Benutzeroberfläche ist sehr einfach. Die einzige Aufgabe der Benutzeroberfläche sollte sein, UI-Status angezeigt wird.

In diesem Abschnitt wird der unidirektionale Datenfluss (Unidirektionale Datenfluss – UDF) erläutert, ein Architekturmuster um diese gesunde Trennung von Verantwortung durchzusetzen.

Inhaber der Bundesstaaten

Die Klassen, die für die Erstellung des UI-Status zuständig sind und die Klasse die für diese Aufgabe notwendige Logik heißt State Owners. Inhaber der Bundesstaaten kommen ins Spiel unterschiedlichen Größen je nach Umfang der entsprechenden UI-Elemente, von einem einzelnen Widget wie der untergeordneten App für den gesamten Bildschirm Navigationsziel.

Im letzteren Fall ist die typische Implementierung eine Instanz eines ViewModel. Je nach Anforderungen der Anwendung erfüllt, kann eine einfache Klasse ausreichen. Die Nachrichten-App von In der Fallstudie wird zum Beispiel die Klasse NewsViewModel als State Holder gesetzt, um den UI-Status für den in diesem Abschnitt angezeigten Bildschirm zu erzeugen.

Es gibt viele Möglichkeiten, die Codeabhängigkeit zwischen der UI und ihrem Status zu modellieren. Producer. Da die Interaktion zwischen der Benutzeroberfläche und ihrem ViewModel Klasse allgemein als Ereignis input und den darauffolgenden Status output verstanden werden. kann die Beziehung wie im folgenden Diagramm dargestellt werden:

<ph type="x-smartling-placeholder">
</ph> Anwendungsdaten fließen von der Datenschicht zum ViewModel. UI-Status
    Datenflüsse von ViewModel zu UI-Elementen und Ereignisse von der Benutzeroberfläche
    Elemente zurück an ViewModel.
<ph type="x-smartling-placeholder">
</ph> Abbildung 4: Diagramm zur Funktionsweise von UDFs in der Anwendungsarchitektur.

Das Muster, bei dem der Zustand nach unten und die Ereignisse nach oben fließen, wird als ein unidirektionaler Datenfluss (Unidirektionale Datenfluss, UDF). Die Auswirkungen dieses Musters auf die App Architektur:

  • ViewModel enthält den von der UI zu verwendenden Status und stellt ihn bereit. Die Benutzeroberfläche Zustand sind die durch ViewModel transformierten Anwendungsdaten.
  • Die Benutzeroberfläche benachrichtigt das ViewModel von Nutzerereignissen.
  • ViewModel übernimmt die Nutzeraktionen und aktualisiert den Status.
  • Der aktualisierte Status wird zum Rendern an die Benutzeroberfläche zurückgegeben.
  • Dies wird für jedes Ereignis wiederholt, das eine Zustandsänderung verursacht.

Für Navigationsziele oder Bildschirme funktioniert ViewModel mit Repositories oder um Daten abzurufen und in den UI-Zustand umzuwandeln, während Einbeziehung der Auswirkungen von Ereignissen, die Mutationen des Staates verursachen können Die Die zuvor erwähnte Fallstudie enthält eine Liste von Artikeln, die jeweils Titel, Beschreibung, Quelle, den Namen des Autors, das Erscheinungsdatum als Lesezeichen gespeichert wurde. Die Benutzeroberfläche jedes Artikelelements sieht so aus:

<ph type="x-smartling-placeholder">
</ph>
<ph type="x-smartling-placeholder">
</ph> Abbildung 5: Benutzeroberfläche eines Artikelelements in der Fallstudien-App

Ein Nutzer möchte einen Artikel als Lesezeichen speichern, ist ein Beispiel für ein Ereignis, bei dem Zustandsmutationen verursachen. Als Ersteller des Bundesstaates ist es die Verantwortung, die gesamte Logik zu definieren, die erforderlich ist, um alle Felder auszufüllen im Status der Benutzeroberfläche und verarbeiten die Ereignisse, die für eine vollständige Darstellung auf der Benutzeroberfläche erforderlich sind.

<ph type="x-smartling-placeholder">
</ph> Ein UI-Ereignis tritt auf, wenn der Nutzer ein Lesezeichen für einen Artikel speichert. ViewModel
    benachrichtigt die Datenschicht über die Statusänderung. Die Datenschicht hält
    und aktualisiert die Anwendungsdaten. Die neuen App-Daten mit den
    mit einem Lesezeichen versehene Artikel wird an ViewModel übergeben, das dann die
    neuen UI-Status und übergibt ihn zur Anzeige an die UI-Elemente.
<ph type="x-smartling-placeholder">
</ph> Abbildung 6: Diagramm zur Veranschaulichung des Zyklus von Ereignissen und Daten in einer UDF.

In den folgenden Abschnitten sehen wir uns die Ereignisse an, die zu Statusänderungen führen. und wie sie mit UDF verarbeitet werden können.

Arten von Logik

Das Speichern eines Artikels als Lesezeichen ist ein Beispiel für Geschäftslogik, da sie Mehrwert bietet. zu Ihrer App hinzufügen. Weitere Informationen hierzu finden Sie in den Daten finden Sie auf der Seite „Layer“. Es gibt jedoch verschiedene Arten von Logik, deren Definition wichtig ist:

  • Die Geschäftslogik ist die Implementierung von Produktanforderungen für Apps. Daten. Wie bereits erwähnt, können Sie beispielsweise einen Artikel im Fallstudien-App. Die Geschäftslogik wird normalerweise in der Domain oder den Daten platziert aber nie auf der UI-Ebene.
  • UI-Verhaltenslogik oder UI-Logik beschreibt, wie Statusänderungen auf auf dem Bildschirm. Ein Beispiel hierfür ist die Auswahl des richtigen Texts, der auf dem Bildschirm angezeigt werden soll. mit Android Resources, Zu einem bestimmten Bildschirm navigieren, wenn Nutzende auf eine Schaltfläche klicken, oder Anzeige einer User-Nachricht auf dem Bildschirm mithilfe eines Toast oder einen snackbar.

Die UI-Logik, insbesondere wenn sie UI-Typen wie Context, sollte in der UI vorhanden sein, nicht in das ViewModel. Wenn die Komplexität der Benutzeroberfläche zunimmt und Sie sie delegieren möchten Logik einer anderen Klasse zuzuweisen, um die Testbarkeit und die Trennung von Bedenken zu bevorzugen, können Sie können eine einfache Klasse als Statusinhaber erstellen. Einfache Klassen, die in der Benutzeroberfläche erstellt wurden Sie können Android SDK-Abhängigkeiten übernehmen, da sie dem Lebenszyklus der Benutzeroberfläche folgen. ViewModel-Objekte haben eine längere Lebensdauer.

Weitere Informationen zu staatlichen Inhabern und dazu, wie sie in den Kontext Unterstützung beim Erstellen der Benutzeroberfläche finden Sie unter Jetpack Compose State .

Vorteile von UDFs

UDF modelliert den Zyklus der Zustandsproduktion, wie in Abbildung 4 dargestellt. Außerdem trennt sie der Ort, an dem Zustandsänderungen ihren Ursprung haben, der Ort, an dem sie transformiert werden, und wo sie schließlich konsumiert werden. Durch diese Trennung kann die Benutzeroberfläche was der Name schon sagt: Informationen werden angezeigt, wenn Sie Zustandsänderungen beobachten, und Nutzerabsicht weitergeben, indem diese Änderungen an ViewModel weitergegeben werden.

Mit anderen Worten, eine UDF ermöglicht Folgendes:

  • Datenkonsistenz: Es gibt eine Single Source of Truth für die Benutzeroberfläche.
  • Testbarkeit: Die Zustandsquelle ist isoliert und daher testbar von der Benutzeroberfläche unabhängig.
  • Pflegebarkeit: Die Mutation des Zustands folgt einem klar definierten Muster, Mutationen sind sowohl das Ergebnis von Nutzerereignissen als auch den Datenquellen, die sie abrufen. aus.

UI-Status freigeben

Nachdem Sie den Status der Benutzeroberfläche definiert und festgelegt haben, wie Sie die Produktion verwalten möchten dieses Zustands erhalten, besteht der nächste Schritt darin, den erzeugten Status auf der Benutzeroberfläche zu präsentieren. Weil mit UDFs die Produktion eines Zustands verwalten, können Sie zu einem Stream, also mehreren Versionen des Zustands, im Laufe der Zeit. Daher sollten Sie den UI-Status in einem wie LiveData oder StateFlow. Der Grund dafür ist, damit die UI auf Statusänderungen reagieren kann, können Sie Daten manuell direkt aus ViewModel abrufen. Diese Typen haben auch die Vorteil, dass immer die neueste Version des UI-Status im Cache gespeichert ist, d. h. nützlich für die schnelle Wiederherstellung des Zustands nach Konfigurationsänderungen.

Aufrufe

class NewsViewModel(...) : ViewModel() {

    val uiState: StateFlow<NewsUiState> = …
}

Schreiben

class NewsViewModel(...) : ViewModel() {

    val uiState: NewsUiState = …
}

Eine Einführung in LiveData als Dateninhaber, der beobachtbar ist, finden Sie in diesem Codelab aus. Für eine ähnliche Eine Einführung in Kotlin-Abläufe finden Sie hier.

In Fällen, in denen die auf der Benutzeroberfläche angezeigten Daten relativ einfach sind, lohnt es sich oft, das Einbinden der Daten in einen UI-Zustandstyp, da dies die Beziehung zwischen die Emissionen des Staatsinhabers und das zugehörige Bildschirm- oder UI-Element zu analysieren. Da das UI-Element immer komplexer wird, ist es auch immer einfacher, das Element die Definition des UI-Status, um die zusätzlichen Informationen UI-Element rendern

Eine gängige Methode zum Erstellen eines UiState-Streams ist die Bereitstellung einer veränderlichen Sicherung Stream als unveränderlicher Stream aus ViewModel, z. B. wenn ein MutableStateFlow<UiState> als StateFlow<UiState>.

Aufrufe

class NewsViewModel(...) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    ...

}

Schreiben

class NewsViewModel(...) : ViewModel() {

    var uiState by mutableStateOf(NewsUiState())
        private set

    ...
}

ViewModel kann dann Methoden zur Verfügung stellen, die den Status intern verändern. um Updates zu veröffentlichen, die auf der Benutzeroberfläche verarbeitet werden sollen. Nehmen wir zum Beispiel den Fall, muss eine asynchrone Aktion ausgeführt werden. kann eine Koroutine mit der Methode viewModelScope und kann der änderbare Status nach Abschluss aktualisiert werden.

Aufrufe

class NewsViewModel(
    private val repository: NewsRepository,
    ...
) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                _uiState.update {
                    it.copy(newsItems = newsItems)
                }
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                _uiState.update {
                    val messages = getMessagesFromThrowable(ioe)
                    it.copy(userMessages = messages)
                 }
            }
        }
    }
}

Schreiben

class NewsViewModel(
    private val repository: NewsRepository,
    ...
) : ViewModel() {

   var uiState by mutableStateOf(NewsUiState())
        private set

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                uiState = uiState.copy(newsItems = newsItems)
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                val messages = getMessagesFromThrowable(ioe)
                uiState = uiState.copy(userMessages = messages)
            }
        }
    }
}

Im obigen Beispiel versucht die Klasse NewsViewModel, Artikel für einen Kategorie und spiegelt dann das Ergebnis des Versuchs wider – ob Erfolg oder Fehler: in dem Status der Benutzeroberfläche, in dem die Benutzeroberfläche entsprechend reagieren kann. Weitere Informationen finden Sie in der Bereich Fehler auf dem Bildschirm anzeigen mit weiteren Informationen Umgang mit Ihren Daten.

Zusätzliche Überlegungen

Zusätzlich zur vorherigen Anleitung sollten Sie bei der Bereitstellung von UI Folgendes beachten: Bundesland:

  • Ein UI-Statusobjekt sollte zueinander in Beziehung stehende Zustände verarbeiten. Dies führt zu weniger Inkonsistenzen und erleichtert zu verstehen. Wenn Sie die Liste der Nachrichtenartikel und die Anzahl der Lesezeichen einblenden kann es passieren, dass einer der beiden aktualisiert wurde, die andere aber nicht. Wenn Sie einen einzelnen Stream verwenden, sind beide Elemente auf dem neuesten Stand gehalten werden. Darüber hinaus kann eine gewisse Geschäftslogik eine Kombination aus Quellen. So kann es beispielsweise sein, dass Sie nur dann eine Lesezeichenschaltfläche anzeigen müssen, Der Nutzer ist angemeldet und ein Abonnent eines Premium-Nachrichtendiensts. So könnten Sie eine UI-Zustandsklasse definieren:

    data class NewsUiState(
        val isSignedIn: Boolean = false,
        val isPremium: Boolean = false,
        val newsItems: List<NewsItemUiState> = listOf()
    )
    
    val NewsUiState.canBookmarkNews: Boolean get() = isSignedIn && isPremium
    

    In dieser Deklaration wird die Sichtbarkeit der Lesezeichenschaltfläche zweier weiterer Eigenschaften. Da die Geschäftslogik immer komplexer wird, eine einzelne UiState-Klasse, bei der alle Properties sofort verfügbar sind immer wichtiger wird.

  • Benutzeroberflächenstatus: einzelner Stream oder mehrere Streams? Das zentrale Leitprinzip um auszuwählen, ob der UI-Status in einem einzelnen Stream oder in mehreren „streams“ ist der vorherige Aufzählungspunkt: die Beziehung zwischen den Elementen ausgegeben. Der größte Vorteil einer einzelnen Streams ist die Bequemlichkeit Datenkonsistenz: Die Konsumenten in Bundesstaaten haben immer die neuesten Informationen, jederzeit verfügbar sind. Es gibt jedoch Fälle, in denen separate Statusstreams aus ViewModel können geeignet sein:

    • Nicht verwandte Datentypen:Einige Status, die zum Rendern der UI erforderlich sind, können völlig unabhängig voneinander sind. In solchen Fällen steigen die Kosten Die Bündelung dieser unterschiedlichen Staaten könnte die Vorteile überwiegen. insbesondere wenn einer dieser Status häufiger aktualisiert wird als der andere.

    • UiState-Unterschiede: Je mehr Felder ein UiState-Objekt enthält, desto wahrscheinlicher ist es, dass der Stream aufgrund eines seiner Felder eine Ausgabe auslöst die gerade aktualisiert werden. Weil es für Ansichten keinen unterschiedlichen Mechanismus gibt, ob aufeinanderfolgende Emissionen unterschiedlich oder gleich sind, jede Emissionen führt zu einer Aktualisierung der Ansicht. Das bedeutet, dass die Risikominderung mithilfe der Flow APIs oder Methoden wie distinctUntilChanged() auf der LiveData ist möglicherweise erforderlich.

UI-Status abrufen

Zur Verarbeitung des Streams von UiState-Objekten in der UI verwenden Sie das Terminal. für den Typ der beobachtbaren Daten, den Sie verwenden. Beispiel: LiveData verwenden Sie die observe()-Methode und für Kotlin-Abläufe die collect()-Methode oder ihre Varianten.

Wenn Sie beobachtbare Dateninhaber in der Benutzeroberfläche konsumieren, achten Sie darauf, der Benutzeroberfläche berücksichtigt. Das ist wichtig, weil die Benutzeroberfläche den Status der Benutzeroberfläche nicht beobachten, wenn die Ansicht dem Nutzer. Weitere Informationen zu diesem Thema finden Sie in diesem Blog. Blogpost. Bei Verwendung von LiveData übernimmt LifecycleOwner implizit den Lebenszyklus Bedenken. Bei der Verwendung von Abläufen sollten Sie Coroutine-Bereich und der repeatOnLifecycle API:

Aufrufe

class NewsActivity : AppCompatActivity() {

    private val viewModel: NewsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

Schreiben

@Composable
fun LatestNewsScreen(
    viewModel: NewsViewModel = viewModel()
) {
    // Show UI elements based on the viewModel.uiState
}

Laufende Vorgänge anzeigen

Eine einfache Möglichkeit zur Darstellung des Ladestatus in einer UiState-Klasse ist die Verwendung einer boolesches Feld:

data class NewsUiState(
    val isFetchingArticles: Boolean = false,
    ...
)

Der Wert dieses Flags gibt an, ob eine Fortschrittsanzeige im UI.

Aufrufe

class NewsActivity : AppCompatActivity() {

    private val viewModel: NewsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Bind the visibility of the progressBar to the state
                // of isFetchingArticles.
                viewModel.uiState
                    .map { it.isFetchingArticles }
                    .distinctUntilChanged()
                    .collect { progressBar.isVisible = it }
            }
        }
    }
}

Schreiben

@Composable
fun LatestNewsScreen(
    modifier: Modifier = Modifier,
    viewModel: NewsViewModel = viewModel()
) {
    Box(modifier.fillMaxSize()) {

        if (viewModel.uiState.isFetchingArticles) {
            CircularProgressIndicator(Modifier.align(Alignment.Center))
        }

        // Add other UI elements. For example, the list.
    }
}

Fehler auf dem Bildschirm anzeigen

Die Anzeige von Fehlern auf der Benutzeroberfläche ähnelt der Anzeige laufender Vorgänge, da Sie lassen sich problemlos durch boolesche Werte darstellen, die ihre Präsenz oder Abwesenheit. Fehler können jedoch auch eine zugehörige Nachricht zur Weiterleitung enthalten. oder eine damit verknüpfte Aktion, die den fehlgeschlagenen Vorgang wiederholt . Während ein laufender Vorgang entweder geladen wird oder nicht müssen möglicherweise mit Datenklassen modelliert werden, für den Fehlerkontext geeignete Metadaten.

Nehmen wir zum Beispiel das Beispiel aus dem vorherigen Abschnitt, in dem ein Fortschrittsanzeige beim Abrufen von Artikeln. Wenn dieser Vorgang zu einem Fehler führt, können Nutzende eine oder mehrere Nachrichten anzeigen, in denen beschrieben wird, falsch.

data class Message(val id: Long, val message: String)

data class NewsUiState(
    val userMessages: List<Message> = listOf(),
    ...
)

Die Fehlermeldungen werden dem Nutzer dann möglicherweise in Form einer Elemente wie Snackbars. Da dies mit der Erstellung und Nutzung von UI-Ereignissen zusammenhängt, lesen Sie den Abschnitt UI- finden Sie weitere Informationen.

Threading und Nebenläufigkeit

Alle in einem ViewModel ausgeführten Arbeiten sollten hauptsicher sein und können sicher über das im Hauptthread. Das liegt daran, dass die Daten- und Domainebenen das Verschieben der Arbeit in einen anderen Thread.

Wenn ein ViewModel lange andauernde Vorgänge ausführt, diese Logik in einen Hintergrundthread zu verschieben. Kotlin-Koroutinen sind eine gute Möglichkeit, und die Jetpack-Architekturkomponenten bieten integrierten Support. Weitere Informationen zur Verwendung von gemeinsamen Abläufen in Android-Apps Siehe Kotlin-Koroutinen unter Android

Änderungen in der App-Navigation sind oft auf ereignisbasierte Emissionen zurückzuführen. Beispiel: Nachdem sich eine SignInViewModel-Klasse angemeldet hat, kann der UiState einen Das Feld isSignedIn wurde auf true festgelegt. Trigger wie diese sollten z. B. die im Abschnitt Consume UI state (UI-Status verwenden) mit der Ausnahme, dass die Verbrauchsimplementierung auf den Navigationskomponente:

Paging

Die Paging Library in der UI mit dem Typ PagingData verarbeitet. Grund: PagingData steht für Elemente und enthält Elemente, die sich im Laufe der Zeit ändern können. kein unveränderlicher Typ ist. Er sollte nicht in einem unveränderlichen UI-Status dargestellt werden. Stattdessen sollten Sie es unabhängig aus ViewModel in einem eigenen . Im Codelab zum Seitenaufbau unter Android finden Sie ein konkretes Beispiel.

Animationen

Um fließende und reibungslose Navigationsübergänge auf oberster Ebene zu schaffen, bevor die Animation gestartet wird, bis die Daten auf dem zweiten Bildschirm geladen sind. Das Android-Ansichts-Framework bietet Hooks, um Übergänge zwischen Fragmenten zu verzögern. Ziele mit den postponeEnterTransition() und startPostponedEnterTransition() APIs Mit diesen APIs kann sichergestellt werden, dass die UI-Elemente der zweiten (normalerweise ein aus dem Netzwerk abgerufenes Bild) können angezeigt werden. bevor die Benutzeroberfläche den Übergang zu diesem Bildschirm animiert. Weitere Informationen und Implementierungshinweise finden Sie in der Android Motion- Stichprobe.

Produktproben

In den folgenden Google-Beispielen wird die Verwendung der UI-Ebene veranschaulicht. Sehen Sie sich diese Tipps in der Praxis an: