Interfejs Compose jest stały – nie można go już zaktualizować. Możesz kontrolować stan interfejsu użytkownika. Za każdym razem, gdy zmieni się stan interfejsu, funkcja Utwórz odtwarza te części drzewa UI, które uległy zmianie. Obiekty kompozycyjne mogą akceptować stan i ujawniać zdarzenia, np. TextField
akceptuje wartość i udostępnia wywołanie zwrotne onValueChange
, które żąda od modułu obsługi wywołania zwrotnego zmiany wartości.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
Ponieważ obiekty kompozycyjne akceptują zdarzenia stanu i ujawniania, jednokierunkowy wzorzec przepływu danych dobrze pasuje do Jetpack Compose. Opisano w nim sposób wdrażania jednokierunkowego wzorca przepływu danych w Compose, jak implementowaniu zdarzeń i stanów oraz posługiwaniu się modelami ViewModel w oknie Compose.
Jednokierunkowy przepływ danych
Jednokierunkowy przepływ danych (UDF) to wzorzec projektowy, w którym stan przebiega w dół, a zdarzenia napływają. Przepływ danych w jednym kierunku pozwala oddzielić elementy kompozycyjne wyświetlające stan w interfejsie od części aplikacji, które przechowują i zmieniają stan.
Pętla aktualizacji UI aplikacji korzystającej z jednokierunkowego przepływu danych wygląda tak:
- Zdarzenie: część interfejsu użytkownika generuje zdarzenie i przekazuje je wyżej, np. kliknięcie przycisku, które jest przekazywane do modelu ViewModel w celu obsługi, lub zdarzenie jest przekazywane z innych warstw aplikacji, np. wskazujące, że sesja użytkownika wygasła.
- Stan aktualizacji: moduł obsługi zdarzeń może zmienić stan.
- Stan wyświetlania: właściciel stanu przekazuje stan, a interfejs go wyświetla.
Przestrzeganie tego wzorca podczas korzystania z Jetpack Compose ma kilka zalet:
- Testowanie: stan odłączenia od interfejsu użytkownika, który go wyświetla, ułatwia testowanie obu rodzajów danych.
- Objaśnienie stanu: ponieważ stan może być aktualizowany tylko w jednym miejscu i istnieje tylko jedno źródło wiarygodnych informacji o stanie funkcji kompozycyjnej, jest mniejsze prawdopodobieństwo, że z powodu niespójnych stanów wystąpią błędy.
- Spójność interfejsu: wszystkie aktualizacje stanu są natychmiast odzwierciedlane w interfejsie przez korzystanie z obserwowalnych właścicieli stanu, takich jak
StateFlow
czyLiveData
.
Jednokierunkowy przepływ danych w Jetpack Compose
Elementy kompozycyjne działają w zależności od stanu i zdarzeń. Na przykład element TextField
jest aktualizowany tylko po zaktualizowaniu parametru value
i udostępnia wywołanie zwrotne onValueChange
, czyli zdarzenie, które prosi o zmianę wartości na nową. Tworzenie określa obiekt State
jako posiadacz wartości, a zmiana wartości stanu wywołuje zmianę kompozycji. W zależności od tego, jak długo chcesz przechowywać ten stan, możesz go przechowywać w elemencie remember { mutableStateOf(value) }
lub rememberSaveable { mutableStateOf(value)
.
Typ wartości elementu kompozycyjnego TextField
to String
, więc może ona pochodzić z dowolnego miejsca – z zakodowanej na stałe wartości z modelu ViewModel lub przekazywanej z nadrzędnego elementu kompozycyjnego. Nie musisz jej przechowywać w obiekcie State
, ale trzeba zaktualizować wartość po wywołaniu metody onValueChange
.
Zdefiniuj parametry kompozycyjne
Podczas określania parametrów stanu funkcji kompozycyjnej pamiętaj o tych kwestiach:
- W jakim stopniu funkcja wielokrotnego użytku i elastyczność są kompozycyjne?
- Jak parametry stanu wpływają na wydajność tego elementu kompozycyjnego?
Aby umożliwić oddzielenie i ponowne wykorzystanie, każdy element kompozycyjny powinien zawierać jak najmniej informacji. Gdy np. tworzysz kompozycję do przechowywania nagłówka artykułu z wiadomościami, staraj się przekazywać tylko te informacje, które mają być widoczne, a nie cały artykuł:
@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. }
Czasami korzystanie z pojedynczych parametrów poprawia też wydajność – jeśli np. element News
zawiera więcej informacji niż tylko parametr title
i subtitle
, to za każdym razem, gdy do Header(news)
zostanie przekazane nowe wystąpienie elementu News
, funkcja kompozycyjna utworzy nową kompozycję, nawet jeśli elementy title
i subtitle
się nie zmieniły.
Zwracaj szczególną uwagę na liczbę przekazywanych parametrów. Funkcja ze zbyt dużą liczbą parametrów obniża jej ergonomię, więc w tym przypadku preferowane jest zgrupowanie ich w klasie.
Zdarzenia w oknie tworzenia wiadomości
Wszystkie dane wejściowe aplikacji powinny być reprezentowane jako zdarzenia: kliknięcia, zmiany tekstu, a nawet minutniki i inne aktualizacje. Ponieważ te zdarzenia zmieniają stan interfejsu użytkownika, to obiekt ViewModel
powinien je obsługiwać i aktualizować stan UI.
Warstwa interfejsu nie powinna nigdy zmieniać stanu poza modułem obsługi zdarzeń, ponieważ może to powodować niespójności i błędy w aplikacji.
Preferuj przekazywanie wartości stałych w lambdach stanu i modułów obsługi zdarzeń. Takie podejście ma następujące korzyści:
- W ten sposób usprawnisz możliwość wielokrotnego wykorzystania.
- Dopilnuj, aby interfejs użytkownika nie zmieniał bezpośrednio wartości stanu.
- Pozwala to uniknąć problemów z równoczesnością, ponieważ dba o to, aby stan nie był mutowany z innego wątku.
- Często pozwala to zmniejszyć złożoność kodu.
Na przykład funkcja kompozycyjna, która akceptuje funkcje String
i lambda jako parametry, może być wywoływana z wielu kontekstów i bardzo nadaje się do wielokrotnego użytku. Załóżmy, że na górnym pasku aplikacji zawsze wyświetla się tekst i zawiera przycisk Wstecz. Możesz zdefiniować bardziej ogólny element kompozycyjny MyAppTopAppBar
, który odbiera jako parametry tekst i uchwyt przycisku Wstecz:
@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 ) } }, // ... ) }
Modele ViewModels, stany i zdarzenia: przykład
Korzystając z ViewModel
i mutableStateOf
, możesz też wprowadzić w swojej aplikacji jednokierunkowy przepływ danych, jeśli spełniony jest jeden z tych warunków:
- Stan interfejsu jest udostępniany za pomocą obserwowalnych właścicieli stanu, takich jak
StateFlow
lubLiveData
. ViewModel
obsługuje zdarzenia pochodzące z interfejsu użytkownika lub innych warstw aplikacji i aktualizuje identyfikator stanu na podstawie zdarzeń.
Gdy np. implementujesz ekran logowania, kliknięcie przycisku Zaloguj się powinno powodować wyświetlanie ikony postępu i wywołanie sieci. Jeśli logowanie się uda, aplikacja przejdzie na inny ekran, a w przypadku błędu pojawi się pasek powiadomień. Oto jak modeluje się stan ekranu i zdarzenie:
Ekran ma 4 stany:
- Wylogowano: użytkownik, który jeszcze się nie zalogował.
- W toku: gdy aplikacja próbuje zalogować użytkownika, wykonując wywołanie sieciowe.
- Błąd: podczas logowania wystąpił błąd.
- Zalogowany: gdy użytkownik jest zalogowany.
Te stany możesz modelować jako zapieczętowaną klasę. ViewModel
podaje stan jako State
, ustawia stan początkowy i w razie potrzeby aktualizuje stan. ViewModel
obsługuje też zdarzenie logowania, udostępniając metodę onSignIn()
.
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
Oprócz interfejsu API mutableStateOf
funkcja Compose udostępnia rozszerzenia dla LiveData
, Flow
i Observable
, które umożliwiają zarejestrowanie się jako detektor i reprezentowanie wartości jako stanu.
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() // ... }
Więcej informacji
Więcej informacji o architekturze w Jetpack Compose znajdziesz w tych materiałach:
Próbki
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy JavaScript jest wyłączony
- State i Jetpack Compose
- Zapisywanie stanu interfejsu użytkownika w momencie tworzenia
- Obsługuj dane wejściowe użytkownika