W sekcji „Tworzenie” interfejs jest niezmienny – nie można go zaktualizować po jego narysowaniu. Możesz kontrolować stan interfejsu użytkownika. Za każdym razem, gdy stan interfejsu użytkownika ulegnie zmianie, Compose ponownie tworzy te części drzewa interfejsu, które uległy zmianie. Komponenty mogą przyjmować stan i wyświetlać zdarzenia. Na przykład komponent TextField
może przyjmować wartość i wyświetlać funkcję wywołania zwrotnego onValueChange
, która prosi moduł obsługi funkcji wywołania zwrotnego o zmianę wartości.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
Ponieważ komponenty przyjmują stan i wyświetlają zdarzenia, wzór jednokierunkowego przepływu danych dobrze pasuje do Jetpack Compose. W tym przewodniku znajdziesz informacje o tym, jak w Compose zaimplementować wzór jednokierunkowego przepływu danych, jak wdrażać zdarzenia i holdery stanu oraz jak pracować z ViewModelami w Compose.
Jednokierunkowy przepływ danych
Jednokierunkowy przepływ danych (UDF) to wzór projektowania, w którym stan przepływa w dół, a zdarzenia w górę. Dzięki jednokierunkowemu przepływowi danych możesz odłączyć komponenty, które wyświetlają stan w interfejsie, od części aplikacji, które przechowują i zmieniają stan.
Cykl aktualizacji interfejsu użytkownika w przypadku aplikacji korzystającej z jednokierunkowego przepływu danych wygląda tak:
- Zdarzenie: część interfejsu generuje zdarzenie i przekazuje je w górę, np. kliknięcie przycisku przekazane do obsługi do ViewModel. Zdarzenie może też być przekazywane z innych warstw aplikacji, np. informujące o wygaśnięciu sesji użytkownika.
- Aktualizowanie stanu: może go zmienić moduł obsługi zdarzenia.
- Wyświetlanie stanu: obiekt stanu przekazuje stan, a interfejs użytkownika go wyświetla.

Stosowanie tego schematu podczas korzystania z Jetpack Compose daje kilka korzyści:
- Możliwość testowania: odłączenie stanu od interfejsu, który go wyświetla, ułatwia testowanie obu elementów oddzielnie.
- Opakowanie stanu: stan można zaktualizować tylko w jednym miejscu, a stan kompozytu ma tylko jedno źródło informacji, więc tworzenie błędów z powodu niezgodnych stanów jest mniej prawdopodobne.
- Spójność interfejsu: wszystkie aktualizacje stanu są natychmiast odzwierciedlane w interfejsie dzięki zastosowaniu obserwowalnych uchwytów stanu, takich jak
StateFlow
lubLiveData
.
Jednokierunkowy przepływ danych w Jetpack Compose
Elementy składane działają na podstawie stanu i zdarzeń. Na przykład obiekt TextField
jest aktualizowany tylko wtedy, gdy zostanie zaktualizowany parametr value
, i wyświetla wywołanie zwrotne onValueChange
, czyli zdarzenie, które prosi o zmianę wartości na nową. Składnik Compose definiuje obiekt State
jako uchwyt wartości, a zmiany wartości stanu powodują ponowne skompilowanie. Stan możesz przechowywać w remember { mutableStateOf(value) }
lub rememberSaveable { mutableStateOf(value)
w zależności od tego, jak długo musisz pamiętać wartość.
Typ wartości komponentu TextField
to String
, więc może ona pochodzić z dowolnego miejsca: z wartości zakodowanej na stałe, z ViewModel lub przekazana przez nadrzędny komponent. Nie musisz przechowywać go w obiekcie State
, ale musisz zaktualizować wartość, gdy wywoływana jest funkcja onValueChange
.
Definiowanie parametrów kompozytowych
Podczas definiowania parametrów stanu komponentu pamiętaj o tych kwestiach:
- Jak często można używać danego elementu i jak jest on elastyczny?
- Jak parametry stanu wpływają na działanie tego komponentu?
Aby zachęcić do rozdzielania i wielokrotnego używania, każdy element powinien zawierać jak najmniej informacji. Na przykład podczas tworzenia kompozytowego komponentu do wyświetlania nagłówka artykułu z wiadomościami warto przekazać tylko te informacje, które mają być wyświetlane, 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 użycie poszczególnych parametrów też poprawia wydajność. Jeśli na przykład News
zawiera więcej informacji niż tylko title
i subtitle
, za każdym razem, gdy do Header(news)
zostanie przekazany nowy egzemplarz News
, kompozyt zostanie ponownie skompilowany, nawet jeśli title
i subtitle
się nie zmieniły.
Uważnie rozważ liczbę parametrów, które przekazujesz. Funkcja z zbyt dużą liczbą parametrów zmniejsza ergonomię, dlatego w tym przypadku zaleca się grupowanie ich w klasie.
Wydarzenia w edytorze
Każde działanie użytkownika w aplikacji powinno być reprezentowane jako zdarzenie: kliknięcia, zmiany tekstu, a nawet zegary lub inne aktualizacje. Ponieważ te zdarzenia zmieniają stan interfejsu użytkownika, ViewModel
powinien je obsługiwać i aktualizować stan interfejsu.
Warstwa interfejsu użytkownika nigdy nie powinna zmieniać stanu poza modułem obsługi zdarzeń, ponieważ może to spowodować niespójności i błędy w aplikacji.
W przypadku funkcji lambda stanu i obsługi zdarzeń preferuj przekazywanie wartości niezmiennych. Ta metoda ma te zalety:
- zwiększa możliwość ponownego użycia.
- Upewnij się, że interfejs użytkownika nie zmienia bezpośrednio wartości stanu.
- Unikasz problemów z współbieżnością, ponieważ masz pewność, że stan nie jest modyfikowany z innego wątku.
- Często zmniejsza to złożoność kodu.
Na przykład kompozyt, który przyjmuje jako parametry String
i funkcję lambda, może być wywoływany z wielu kontekstów i jest bardzo wielokrotnego użytku. Załóżmy, że górny pasek aplikacji zawsze wyświetla tekst i przycisk Wstecz. Możesz zdefiniować bardziej ogólny komponent MyAppTopAppBar
, który otrzymuje tekst i identyfikator przycisku Wstecz jako parametry:
@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, stany i zdarzenia: przykład
Korzystając z elementów ViewModel
i mutableStateOf
, możesz też wprowadzić w aplikacji jednokierunkowy przepływ danych, jeśli spełniony jest co najmniej 1 z tych warunków:
- Stan interfejsu użytkownika jest widoczny za pomocą obserwowalnych uchwytów stanu, takich jak
StateFlow
lubLiveData
. ViewModel
obsługuje zdarzenia pochodzące z interfejsu użytkownika lub innych warstw aplikacji i aktualizuje stan zgodnie z tymi zdarzeniami.
Na przykład podczas implementowania ekranu logowania kliknięcie przycisku Zaloguj się powinno spowodować wyświetlenie w aplikacji wskaźnika postępu i wywołanie połączenia z siecią. Jeśli udało się zalogować, aplikacja przejdzie do innego ekranu. W przypadku błędu wyświetli się pasek informacji. Oto jak modelować stan ekranu i zdarzenie:
Ekran ma 4 stany:
- Wylogowany: użytkownik nie jest jeszcze zalogowany.
- W toku: gdy aplikacja próbuje zalogować użytkownika, wykonując wywołanie sieci.
- Błąd: gdy podczas logowania wystąpił błąd.
- Zalogowany: gdy użytkownik jest zalogowany.
Te stany możesz modelować jako zamkniętą klasę. ViewModel
udostępnia stan jako State
, ustawia stan początkowy i aktualizuje go w miarę potrzeby. 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
Compose zawiera rozszerzenia dla funkcji LiveData
, Flow
i Observable
, które rejestrują się jako słuchacze i reprezentują wartość jako stan.
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 obsługa JavaScript jest wyłączona
- Stan i Jetpack Compose
- Zapisywanie stanu interfejsu w funkcji Utwórz
- Przetwarzanie danych wejściowych użytkownika