Wenn Sie in einer Compose-Anwendung den UI-Status hochheben, hängt davon ab, ob die Benutzeroberfläche oder Geschäftslogik erfordert. In diesem Dokument werden diese beiden Szenarien durchführen.
Best Practice
Sie sollten den UI-Status auf den niedrigsten gemeinsamen Ancestor zwischen allen die die Daten lesen und schreiben. Bundesstaat sollte möglichst nahe am Standort liegen verbraucht. Vom Inhaber des Bundesstaats für Nutzer unveränderliche Status und Ereignisse sichtbar machen um den Status zu ändern.
Der niedrigste gemeinsame Ancestor kann auch außerhalb der Komposition liegen. Beispiel:
beim Hochziehen des Zustands in einem ViewModel
, da die Geschäftslogik beteiligt ist.
Auf dieser Seite wird diese bewährte Vorgehensweise ausführlich erläutert und es ist ein wichtiger Punkt zu beachten.
Arten von UI-Status und UI-Logik
Unten finden Sie Definitionen für UI-Status- und Logiktypen, die verwendet werden in diesem Dokument.
UI-Status
Der UI-Status ist die Eigenschaft, die die UI beschreibt. Es gibt zwei Arten von UI-Elementen. Bundesland:
- Der Bildschirm-UI-Status gibt an, was auf dem Bildschirm angezeigt werden muss. Beispiel:
NewsUiState
-Kurs kann die Nachrichtenartikel und andere benötigte Informationen enthalten. um die UI zu rendern. Dieser Zustand ist normalerweise mit anderen Schichten des da sie App-Daten enthält. - Der UI-Elementstatus bezieht sich auf Eigenschaften, die UI-Elementen inhärent sind und die
wie sie gerendert werden. Ein UI-Element kann ein- oder ausgeblendet sein und
eine bestimmte Schriftart,
Schriftgröße oder -farbe haben. In Android Views kann die Ansicht
verwaltet diesen Zustand selbst, da er inhärent zustandsorientiert ist und Methoden offenlegt,
ändern oder abfragen können. Ein Beispiel hierfür sind
get
undset
-Methoden der KlasseTextView
für den Text. Im Jetpack Compose-Objekt erstellen, liegt der Status außerhalb der zusammensetzbaren Funktion und Sie können sie sogar winden. aus der unmittelbaren Nähe der zusammensetzbaren Funktion in die aufrufende für eine Funktion oder einen Staatsinhaber sein. Ein Beispiel hierfür istScaffoldState
für denScaffold
zusammensetzbar.
Logik
Die Logik in einer Anwendung kann entweder Geschäftslogik oder UI-Logik sein:
- Die Geschäftslogik ist die Implementierung von Produktanforderungen für Apps. Daten. Sie können beispielsweise einen Artikel in einer Newsreader-App als Lesezeichen speichern, tippt auf die Schaltfläche. Diese Logik zum Speichern eines Lesezeichens in einer Datei oder Datenbank normalerweise in den Domänen- oder Datenebenen platziert. Der staatliche Inhaber ist in der Regel delegiert diese Logik an diese Layer, indem die von ihnen bereitgestellten Methoden aufgerufen werden.
- UI-Logik bezieht sich darauf, wie der UI-Status auf dem Bildschirm angezeigt wird. Für Beispiel: Der richtige Suchleistenhinweis wird angezeigt, wenn der Nutzer eine Kategorie, das Scrollen zu einem bestimmten Element in einer Liste oder die Navigationslogik wenn Nutzende auf eine Schaltfläche klicken.
UI-Logik
Wenn die UI-Logik den Status lesen oder schreiben muss, sollten Sie den Status gemäß seinem Lebenszyklus der UI zuordnen. Um dies zu erreichen, sollten Sie den Zustand in einer zusammensetzbaren Funktion auf der richtigen Ebene heben. Alternativ können Sie tun Sie dies in einer einfachen State Holder-Klasse, die sich auch auf den UI-Lebenszyklus bezieht.
Im Folgenden finden Sie eine Beschreibung der beiden Lösungen und eine Erklärung, wann welche eingesetzt werden sollten.
Zusammensetzbare Informationen als State Owner
Die Angabe von UI-Logik und UI-Elementstatus in zusammensetzbaren Funktionen ist ein guter Ansatz, Zustand und Logik einfach ist. Sie können den Status intern auf eine zusammensetzbare oder Winde nach Bedarf.
Kein Hebevorgang erforderlich
Der Status des Windes ist nicht immer erforderlich. Der Status kann intern in einer zusammensetzbaren Funktion beibehalten werden. wenn sie von keiner anderen zusammensetzbaren Funktion gesteuert werden muss. Dieses Snippet enthält eine zusammensetzbare Funktion, die durch Tippen maximiert und minimiert wird:
@Composable fun ChatBubble( message: Message ) { var showDetails by rememberSaveable { mutableStateOf(false) } // Define the UI element expanded state ClickableText( text = AnnotatedString(message.content), onClick = { showDetails = !showDetails } // Apply simple UI logic ) if (showDetails) { Text(message.timestamp) } }
Die Variable showDetails
ist der interne Status für dieses UI-Element. Es ist nur
die in dieser zusammensetzbaren Funktion gelesen und
geändert wird. Die darauf angewendete Logik ist sehr einfach.
Das Hochziehen des Bundesstaats in diesem Fall
braucht daher keinen großen Nutzen.
intern lassen. Dadurch wird die zusammensetzbare Funktion zu „Owner“ und „Single“
und die Informationsquelle des erweiterten Zustands.
Heben in zusammensetzbaren Materialien
Wenn Sie den Status Ihres UI-Elements mit anderen zusammensetzbaren Funktionen teilen und die UI anwenden müssen an verschiedenen Stellen hinzufügen, können Sie sie in der UI-Hierarchie weiter nach oben schieben. Außerdem sind Ihre zusammensetzbaren Funktionen dadurch leichter wiederverwendbar und leichter zu testen.
Das folgende Beispiel ist eine Chat-App, die zwei Funktionen implementiert:
- Mit der Schaltfläche
JumpToBottom
scrollen Sie in der Nachrichtenliste nach unten. Die Taste die Benutzeroberflächenlogik für den Listenstatus ausführt. - Die Liste „
MessagesList
“ wird nach unten gescrollt, nachdem der Nutzer neue Nachrichten gesendet hat Nachrichten. UserInput führt Benutzeroberflächenlogik für den Listenstatus aus.
Die zusammensetzbare Hierarchie sieht so aus:
<ph type="x-smartling-placeholder">Der Status LazyColumn
wird auf den Unterhaltungsbildschirm gezogen, damit die App
UI-Logik ausführen und den Status aus allen zusammensetzbaren Funktionen lesen, für die er erforderlich ist:
Die zusammensetzbaren Funktionen sind also:
<ph type="x-smartling-placeholder">Der Code lautet wie folgt:
@Composable private fun ConversationScreen(/*...*/) { val scope = rememberCoroutineScope() val lazyListState = rememberLazyListState() // State hoisted to the ConversationScreen MessagesList(messages, lazyListState) // Reuse same state in MessageList UserInput( onMessageSent = { // Apply UI logic to lazyListState scope.launch { lazyListState.scrollToItem(0) } }, ) } @Composable private fun MessagesList( messages: List<Message>, lazyListState: LazyListState = rememberLazyListState() // LazyListState has a default value ) { LazyColumn( state = lazyListState // Pass hoisted state to LazyColumn ) { items(messages, key = { message -> message.id }) { item -> Message(/*...*/) } } val scope = rememberCoroutineScope() JumpToBottom(onClicked = { scope.launch { lazyListState.scrollToItem(0) // UI logic being applied to lazyListState } }) }
LazyListState
wird so hoch angehoben, wie es für die UI-Logik erforderlich ist.
angewendet. Da sie in einer zusammensetzbaren Funktion initialisiert wird, wird sie im
Komposition nach ihrem Lebenszyklus.
lazyListState
wird in der Methode MessagesList
definiert, wobei der Parameter
Standardwert rememberLazyListState()
. Dies ist ein gängiges Muster beim Schreiben.
Zusammensetzbare Funktionen sind dadurch wiederverwendbar und flexibler. Sie können dann die zusammensetzbare Funktion
in verschiedenen Teilen der App, die den Status möglicherweise nicht steuern müssen. Dies ist
Dies ist normalerweise der Fall, wenn Sie
eine zusammensetzbare Funktion testen oder in der Vorschau ansehen. Genau so
LazyColumn
definiert seinen Status.
Klasse Inhaberklasse als Inhaber eines US-Bundesstaats
Wenn eine zusammensetzbare Funktion komplexe UI-Logik enthält, die einen oder mehrere Status umfasst eines UI-Elements verwendet, sollte diese Zuständigkeit an die Statusangabe holders, z. B. eine einfache State Holder-Klasse. Dadurch wird die Logik der zusammensetzbaren Funktion besser isoliert testbar und verringert die Komplexität. Bei diesem Ansatz werden die Prinzip der Trennung von Bedenken: Die zusammensetzbare Funktion hat die Verantwortung. der Ausgabe von UI-Elementen und der Statusinhaber enthält UI-Logik und Elementstatus.
Einfache State Holder-Klassen bieten praktische Funktionen für Aufrufer Ihrer zusammensetzbaren Funktion, damit sie diese Logik nicht selbst schreiben müssen.
Diese einfachen Klassen werden in der Komposition erstellt und gespeichert. Da sie
die dem Lebenszyklus der zusammensetzbaren Funktion folgen, können sie Typen annehmen, die vom
Erstellen Sie eine Bibliothek, z. B. rememberNavController()
oder rememberLazyListState()
.
Ein Beispiel hierfür ist der einfache Statusinhaber LazyListState
.
Klasse, implementiert in Compose zur Steuerung der UI-Komplexität von LazyColumn
oder LazyRow
.
// LazyListState.kt @Stable class LazyListState constructor( firstVisibleItemIndex: Int = 0, firstVisibleItemScrollOffset: Int = 0 ) : ScrollableState { /** * The holder class for the current scroll position. */ private val scrollPosition = LazyListScrollPosition( firstVisibleItemIndex, firstVisibleItemScrollOffset ) suspend fun scrollToItem(/*...*/) { /*...*/ } override suspend fun scroll() { /*...*/ } suspend fun animateScrollToItem() { /*...*/ } }
LazyListState
kapselt den Status von LazyColumn
, in dem die
scrollPosition
für dieses UI-Element. Es stellt auch Methoden zum Ändern des
Scrollposition by – z. B. das Scrollen zu einem bestimmten Element.
Wie Sie sehen, steigt die Erhöhung der Verantwortlichkeiten einer zusammensetzbaren Funktion für einen staatlichen Inhaber. Die Verantwortlichkeiten können in der Benutzeroberflächenlogik oder nur in die Menge des Zustands, den Sie im Auge behalten möchten.
Ein weiteres gängiges Muster ist die Verwendung einer einfachen State Holder-Klasse zur Verarbeitung der Komplexität der zusammensetzbaren Stammfunktionen in der App. Mit einer solchen Klasse können Sie der Zustand auf App-Ebene wie den Navigationsstatus und die Bildschirmgröße. Eine vollständige Eine Beschreibung dazu finden Sie auf der Seite UI-Logik und Statusinhaber.
Geschäftslogik
Wenn zusammensetzbare Funktionen und einfache Zustandsinhaberklassen für die UI-Logik und UI-Elementstatus. Ein Inhaber eines Bildschirmebenenstatus ist für Folgendes verantwortlich: Aufgaben:
- Zugriff auf die Geschäftslogik der Anwendung bereitstellen, die normalerweise in anderen Hierarchieebenen wie den Geschäfts- und Datenebenen.
- Vorbereiten der Anwendungsdaten für die Präsentation in einem bestimmten Bildschirm was zum UI-Status des Bildschirms wird.
ViewModels als Inhaber des Bundesstaats
Aufgrund der Vorteile von AAC ViewModels in der Android-Entwicklung für den Zugriff auf die Geschäftslogik und die Vorbereitung der Anwendungsdaten für die Präsentation auf dem Bildschirm.
Wenn du den UI-Status in der ViewModel
senkst, verschiebst du ihn aus der
Komposition
ViewModels werden nicht als Teil der Komposition gespeichert. Sie werden vom
Framework und sind einem ViewModelStoreOwner
zugeordnet, das ein
Aktivität, Fragment, Navigationsdiagramm oder Ziel eines Navigationsdiagramms. Für
Weitere Informationen zu ViewModel
-Bereichen finden Sie in der Dokumentation.
Die ViewModel
ist dann die „Source of Truth“ und der niedrigste gemeinsame Ancestor für
UI-Status
Status der Benutzeroberfläche des Bildschirms
Wie in den obigen Definitionen beschrieben, wird der Bildschirm-UI-Status durch die Anwendung
Regeln. Da der Inhaber der Bildschirmebene dafür verantwortlich ist,
bedeutet, dass der Bildschirm-UI-Status typischerweise auf Bildschirmebene gezogen wird.
Inhaber, in diesem Fall ein ViewModel
.
Betrachte die ConversationViewModel
einer Chat-App und wie sie den Bildschirm freigibt
UI-Status und Ereignisse zum Ändern:
class ConversationViewModel( channelId: String, messagesRepository: MessagesRepository ) : ViewModel() { val messages = messagesRepository .getLatestMessages(channelId) .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = emptyList() ) // Business logic fun sendMessage(message: Message) { /* ... */ } }
Zusammensetzbare Elemente verarbeiten den Bildschirm-UI-Status, der in ViewModel
hochgezogen ist. Sie sollten
fügen Sie die Instanz ViewModel
in die Zusammensetzungen auf Bildschirmebene ein,
Zugriff auf Geschäftslogik.
Hier sehen Sie ein Beispiel für eine ViewModel
, die in einer zusammensetzbaren Funktion auf Bildschirmebene verwendet wird.
Hier verarbeitet die zusammensetzbare Funktion ConversationScreen()
den Status der Bildschirm-UI.
in ViewModel
:
@Composable private fun ConversationScreen( conversationViewModel: ConversationViewModel = viewModel() ) { val messages by conversationViewModel.messages.collectAsStateWithLifecycle() ConversationScreen( messages = messages, onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) } ) } @Composable private fun ConversationScreen( messages: List<Message>, onSendMessage: (Message) -> Unit ) { MessagesList(messages, onSendMessage) /* ... */ }
Property-Drilldown
Bei „Property-Drilldown“ werden Daten durch mehrere verschachtelte untergeordnete Elemente weitergegeben. an die Stelle, an der sie gelesen werden.
Ein typisches Beispiel für die Aufschlüsselung von Eigenschaften in Compose ist, wenn Sie fügen Sie den Inhaber der Bildschirmebene auf der obersten Ebene ein, für zusammensetzbare Funktionen für untergeordnete Elemente. Dies könnte zusätzlich zu einer Überlastung zusammensetzbaren Funktionssignaturen.
Auch wenn Ereignisse als einzelne Lambda-Parameter verfügbar gemacht werden, der Funktionssignatur, maximiert sie die Sichtbarkeit dessen, was die zusammensetzbare Funktion Verantwortlichkeiten haben. Sie sehen auf einen Blick, was passiert.
Die Property-Aufschlüsselung wird gegenüber dem Erstellen von Wrapper-Klassen zum Kapseln vorgezogen. Status und Ereignisse an einem Ort, da dies die Sichtbarkeit der zusammensetzbaren Verantwortlichkeiten. Wenn Sie keine Wrapper-Klassen haben, wahrscheinlich nur die Parameter übergeben, die sie benötigen. Dies ist am besten Übung.
Dieselbe Best Practice gilt, wenn es sich bei diesen Ereignissen um Navigationsereignisse handelt. Sie können Weitere Informationen dazu findest du in der Navigationsdokumentation.
Wenn Sie ein Leistungsproblem festgestellt haben, können Sie das Lesen auch auf später verschieben. des Bundesstaates. Weitere Informationen finden Sie in der Dokumentation zur Leistung.
Status des UI-Elements
Sie können den Status des UI-Elements auf den Inhaber der Bildschirmebene heben, wenn die sie lesen oder schreiben muss.
Am Beispiel einer Chat-App zeigt die App Nutzervorschläge in einer
Gruppenchat, wenn der Nutzer @
und einen Hinweis eingibt. Diese Vorschläge stammen aus dem
und die Logik zum Berechnen einer Liste
von Nutzervorschlägen berücksichtigt,
Geschäftslogik. Die Funktion sieht so aus:
Der ViewModel
, der diese Funktion implementiert, würde so aussehen:
class ConversationViewModel(/*...*/) : ViewModel() { // Hoisted state var inputMessage by mutableStateOf("") private set val suggestions: StateFlow<List<Suggestion>> = snapshotFlow { inputMessage } .filter { hasSocialHandleHint(it) } .mapLatest { getHandle(it) } .mapLatest { repository.getSuggestions(it) } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = emptyList() ) fun updateInput(newInput: String) { inputMessage = newInput } }
inputMessage
ist eine Variable, die den Status TextField
speichert. Jedes Mal, wenn das
Nutzer eine neue Eingabe eingibt, ruft die App die Geschäftslogik auf, um suggestions
zu generieren.
suggestions
ist der Status der Bildschirm-UI und wird von der Editor-Benutzeroberfläche übernommen, indem folgende Daten erfasst werden:
aus dem StateFlow
.
Warnung
Für bestimmte Status von UI-Elementen in „Compose“ ist es möglicherweise erforderlich, dass das Heben auf ViewModel
besondere Überlegungen. Einige Statusinhaber von UI-Elementen vom Typ „Compose“
explodieren, um den Status zu ändern. Es kann sich dabei um Sperrfunktionen handeln,
Animationen auslösen. Diese Aussetzerfunktionen können Ausnahmen auslösen, wenn Sie
aus einem CoroutineScope
, das nicht dem
Komposition
Angenommen, der Inhalt der App-Leiste ist dynamisch und Sie müssen ihn abrufen und aktualisieren
nachdem sie geschlossen wurde. Heben Sie den Zustand der Leiste an,
Den ViewModel
, damit Sie sowohl die UI als auch die Geschäftslogik für dieses Element aufrufen können
vom Inhaber des Bundesstaats.
Wenn Sie jedoch die Methode close()
von DrawerState
mit der Methode
viewModelScope
aus der Compose-UI führt zu einer Laufzeitausnahme des Typs
IllegalStateException
mit der Nachricht "a
MonotonicFrameClock
ist hier nicht verfügbar
CoroutineContext”
.
Verwende eine CoroutineScope
, die sich auf die Komposition bezieht, um dieses Problem zu beheben. Es bietet eine
MonotonicFrameClock
in der CoroutineContext
, die für den
aussetzen.
Um diesen Absturz zu beheben, ändern Sie den CoroutineContext
der Koroutine in der
ViewModel
auf einen Wert für die Komposition. Das könnte so aussehen:
class ConversationViewModel(/*...*/) : ViewModel() { val drawerState = DrawerState(initialValue = DrawerValue.Closed) private val _drawerContent = MutableStateFlow(DrawerContent.Empty) val drawerContent: StateFlow<DrawerContent> = _drawerContent.asStateFlow() fun closeDrawer(uiScope: CoroutineScope) { viewModelScope.launch { withContext(uiScope.coroutineContext) { // Use instead of the default context drawerState.close() } // Fetch drawer content and update state _drawerContent.update { content } } } } // in Compose @Composable private fun ConversationScreen( conversationViewModel: ConversationViewModel = viewModel() ) { val scope = rememberCoroutineScope() ConversationScreen(onCloseDrawer = { conversationViewModel.closeDrawer(uiScope = scope) }) }
Weitere Informationen
Weitere Informationen zu Status und Jetpack Compose finden Sie hier zusätzliche Ressourcen.
Produktproben
Codelabs
Videos
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- UI-Status in „Compose“ speichern
- Listen und Raster
- Benutzeroberfläche für das Schreiben erstellen