Stan w aplikacji to dowolna wartość, która może się z czasem zmieniać. To bardzo ogólne określenie i obejmuje wszystko, od bazy danych sal po zmienną w zajęcia.
Wszystkie aplikacje na Androida wyświetlają stan. Kilka przykładów stanu w Androidzie Aplikacje:
- pasek powiadomień, który wyświetla się, gdy nie można nawiązać połączenia sieciowego;
- Post na blogu i związane z nim komentarze.
- Faliste animacje przycisków odtwarzanych po kliknięciu przez użytkownika.
- Naklejki, które użytkownik może narysować na zdjęciu.
W Jetpack Compose możesz wyraźnie określić, gdzie i jak przechowujesz i używasz plików w aplikacji na Androida. Ten przewodnik skupia się na połączeniu między stanem za pomocą funkcji kompozycyjnych oraz interfejsów API oferowanych przez Jetpack Compose do większej pracy ze stanami .
Stan i kompozycja
Funkcja tworzenia wiadomości ma charakter deklaratywny, dlatego jedynym sposobem jej zaktualizowania jest wywołanie funkcji
funkcję kompozycyjną z nowymi argumentami. Te argumenty reprezentują
Stan interfejsu. Po każdej aktualizacji stanu następuje zmiana kompozycji. Jako
W efekcie aplikacje takie jak TextField
nie aktualizują się automatycznie
imperatywnych widoków XML. Funkcja kompozycyjna musi mieć jawną informację o nowym stanie
aby została odpowiednio zaktualizowana.
@Composable private fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField( value = "", onValueChange = { }, label = { Text("Name") } ) } }
Jeśli go uruchomisz i spróbujesz wpisać tekst, nic się nie stanie. To jest
bo TextField
nie aktualizuje się samodzielnie – gdy jest value
zmian parametrów. Wynika to ze sposobu, w jaki działają kompozycja i rekompozycja
Utwórz.
Więcej informacji na temat początkowej kompozycji i zmiany kompozycji znajdziesz tutaj: myśl w tworzeniu wiadomości
Stan w elementach kompozycyjnych
Funkcje kompozycyjne mogą korzystać z funkcji
remember
Interfejs API do przechowywania obiektu w pamięci. Wartość obliczona przez funkcję remember
to
przechowywane w Kompozycji podczas
a środkowa wartość jest zwracana podczas jej ponownego komponowania.
remember
może służyć do przechowywania zarówno obiektów zmiennych, jak i stałych.
mutableStateOf
tworzy obiekt dostrzegalny
MutableState<T>
,
czyli dostrzegalny typ zintegrowany ze środowiskiem wykonawczym tworzenia.
interface MutableState<T> : State<T> {
override var value: T
}
Wszelkie zmiany w value
planują ponowne kompozycje funkcji kompozycyjnych
z informacją o treści: value
.
Obiekt MutableState
można zadeklarować w funkcji kompozycyjnej na 3 sposoby:
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
Te deklaracje są równoważne i są podane jako cukier składni dla różnego rodzaju używania państwa. Wybierz taki, który generuje najłatwiejszy do odczytania kod w tworzonej przez Ciebie funkcji kompozycyjnej.
Składnia delegata by
wymaga tych importów:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
Zapamiętanej wartości możesz użyć jako parametru dla innych elementów kompozycyjnych, a nawet
funkcji logicznych w instrukcjach, aby zmieniać wyświetlane elementy kompozycyjne. Na przykład, jeśli
Nie chcesz wyświetlać powitania, jeśli nazwa jest pusta, wpisz stan w
Instrukcja if
:
@Composable fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { var name by remember { mutableStateOf("") } if (name.isNotEmpty()) { Text( text = "Hello, $name!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } ) } }
remember
pomaga w zachowaniu stanu w przypadku zmian, ale już nie
i zachowywane w ramach zmian konfiguracji. Aby to zrobić, musisz użyć
rememberSaveable
rememberSaveable
automatycznie zapisuje każdą wartość, która może zostać
zapisano w folderze Bundle
. W przypadku innych wartości możesz przekazać obiekt wygaszacza niestandardowego.
Inne obsługiwane typy stanu
Tworzenie nie wymaga używania MutableState<T>
do zatrzymywania stanu; jego
obsługuje inne dostrzegalne typy. Przed odczytaniem innego dostrzegalnego typu w
Compose musisz przekonwertować do formatu State<T>
, aby funkcje kompozycyjne mogły
automatycznie komponować ponownie po zmianie stanu.
Twórz statki z funkcjami, które tworzą State<T>
na podstawie wspólnych obserwowalnych funkcji
używane w aplikacjach na Androida. Zanim zaczniesz korzystać z tych integracji, dodaj
odpowiednie artefakty opisane poniżej:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
zbiera wartości z:Flow
w sposób uwzględniający cykl życia, dzięki czemu aplikacja może i oszczędzać zasoby aplikacji. Reprezentuje ostatnią wartość wyemitowaną przez UtwórzState
. Używaj tego interfejsu API jako zalecanego sposobu gromadzenia przepływów w Aplikacje na Androida.W pliku
build.gradle
wymagana jest ta zależność (powinna być w wersji 2.6.0-beta01 lub nowszej):
Kotlin
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
}
Odlotowe
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.2"
}
-
collectAsState
jest podobny docollectAsStateWithLifecycle
, ponieważ również zbiera wartości z tabeliFlow
i przekształca je w ikonę UtwórzState
.W przypadku kodu niezależnego od platformy zamiast tagu
collectAsState
collectAsStateWithLifecycle
, która jest dostępna tylko na Androidzie.W przypadku funkcji
collectAsState
nie są wymagane dodatkowe zależności, ponieważ jest ona dostępne w języku:compose-runtime
. -
observeAsState()
zaczyna obserwować ten element (LiveData
) i przedstawia jego wartości przezState
.W pliku
build.gradle
wymagana jest ta zależność:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.6.8")
}
Odlotowe
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.6.8"
}
-
subscribeAsState()
to funkcje rozszerzeń, które przekształcają reakcję reaktywnej RxJava2 strumienie (np.Single
,Observable
,Completable
) do okna UtwórzState
.W pliku
build.gradle
wymagana jest ta zależność:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.6.8")
}
Odlotowe
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.6.8"
}
-
subscribeAsState()
to funkcje rozszerzeń, które przekształcają reakcję reaktywnej RxJava3 strumienie (np.Single
,Observable
,Completable
) do okna UtwórzState
.W pliku
build.gradle
wymagana jest ta zależność:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.6.8")
}
Odlotowe
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.6.8"
}
Stanowa a bezstanowa
Funkcja kompozycyjna, która używa remember
do przechowywania obiektu, tworzy stan wewnętrzny,
zmienia się w stanową element kompozycyjny. HelloContent
to przykład klasy stanowej
jest kompozycyjna, ponieważ przechowuje i zmienia swój stan name
wewnętrznie. Może to spowodować
Jest przydatne w sytuacjach, w których rozmówca nie musi kontrolować stanu i może
bez konieczności samodzielnego zarządzania organami władzy. Jednak elementy kompozycyjne z
które trudniej jest przetestować
i rzadziej nadaje się do użytku wewnętrznego.
Funkcja bezstanowa kompozycyjna to funkcja kompozycyjna, która nie zachowuje żadnego stanu. Łatwy do uzyskania bezstanowego rozwiązania jest m.in. użycie funkcji state dźwignię.
Gdy tworzysz elementy kompozycyjne wielokrotnego użytku, często warto udostępnić zarówno stanowy, i bezstanowej wersji elementu kompozycyjnego. Wersja stanowa to co jest wygodne dla osób, które nie dbają o stan, wersja jest niezbędna dla elementów wywołujących, które muszą kontrolować lub podnosić stan.
Podnośnik stanu
Przenoszenie stanu w funkcji tworzenia wiadomości to wzorzec przenoszenia stanu do wywołującego funkcji kompozycyjnej przekształcenia elementów kompozycyjnych do stanu bezstanowego. Ogólny wzorzec budowania stanu W Jetpack Compose należy zastąpić zmienną stanu dwoma parametrami:
value: T
: bieżąca wartość do wyświetleniaonValueChange: (T) -> Unit
: zdarzenie, które prosi o zmianę wartości; gdzieT
to nowa wartość proponowana
Nie dotyczy Cię jednak ograniczenie do onValueChange
. Jeśli zdarzenia są bardziej szczegółowe
dla funkcji kompozycyjnej, określ je za pomocą funkcji lambda.
Stan, który jest przenoszony w ten sposób, ma kilka ważnych właściwości:
- Jedno źródło danych: przeniesienie stanu, zamiast jego duplikowania, dzięki czemu jest pewna, że istnieje tylko jedno źródło wiarygodnych danych. Pomoże to uniknąć błędów.
- Zamknięta: tylko stanowe elementy kompozycyjne mogą zmieniać swój stan. Jest całkowicie wewnętrznych.
- Możliwość udostępniania: stan przeciągania można udostępnić wielu elementom kompozycyjnym. Jeśli
chcesz przeczytać treść „
name
” w innym narzędziu kompozycyjnym, podnoszącym aby to zrobić. - Możliwe do współdziałania: osoby wywołujące bezstanowe funkcje kompozycyjne mogą ignoruj lub zmodyfikuj zdarzenia przed zmianą stanu.
- Rozłączone: stan bezstanowych elementów kompozycyjnych może być przechowywany.
w dowolnym miejscu. Można na przykład przenieść
name
doViewModel
.
W tym przykładzie wyodrębnisz name
i onValueChange
z
HelloContent
i przenieś je w górę do kompozycji HelloScreen
, która
Wywołuje połączenie HelloContent
.
@Composable fun HelloScreen() { var name by rememberSaveable { mutableStateOf("") } HelloContent(name = name, onNameChange = { name = it }) } @Composable fun HelloContent(name: String, onNameChange: (String) -> Unit) { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello, $name", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") }) } }
Podnosząc stan z HelloContent
, łatwiej jest wyciągnąć wnioski
które można komponować,
używać ponownie w różnych sytuacjach i testować. HelloContent
to
oddzielone od sposobu przechowywania stanu. Usunięcie powiązania oznacza, że jeśli zmodyfikujesz lub
zastąpisz HelloScreen
, nie musisz zmieniać sposobu działania typu HelloContent
.
Wzorzec, w którym następuje spadek stanu do momentu uporządkowania zdarzeń, jest nazywany
jednokierunkowego przepływu danych. W tym przypadku stan zmienia się z HelloScreen
do HelloContent
, a liczba zdarzeń wzrasta od HelloContent
do HelloScreen
. Według
zgodnie z jednokierunkowym przepływem danych, możesz odłączyć elementy kompozycyjne, które wyświetlają
stan w interfejsie z tych części aplikacji, które zapisują i zmieniają stan.
Więcej informacji znajdziesz na stronie Gdzie przenieść stan.
Przywracanie stanu w funkcji tworzenia wiadomości
Interfejs API rememberSaveable
działa podobnie do remember
, ponieważ
Zachowuje stan w ramach zmian, a także w ramach aktywności lub procesu
za pomocą mechanizmu
zapisanego stanu instancji. Dzieje się tak na przykład:
po obróceniu ekranu.
Sposoby zapisywania stanu
Wszystkie typy danych dodane do usługi Bundle
są zapisywane automatycznie. Jeśli
Chcesz zapisać coś, czego nie można dodać do listy Bundle
, istnieje kilka metod
.
Parcelyzacja
Najprostszym rozwiązaniem jest dodanie funkcji
@Parcelize
do obiektu. Obiekt stanie się częścią pakietu i można go połączyć w pakiet. Dla:
przykładowo, ten kod tworzy typ danych City
z możliwością parcelacji i zapisuje go w
stanu.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
Zapisywanie mapy
Jeśli z jakiegoś powodu @Parcelize
jest nieodpowiedni, możesz użyć mapSaver
, aby
zdefiniować własną regułę konwertowania obiektu na zbiór wartości,
system może zapisywać w Bundle
.
data class City(val name: String, val country: String) val CitySaver = run { val nameKey = "Name" val countryKey = "Country" mapSaver( save = { mapOf(nameKey to it.name, countryKey to it.country) }, restore = { City(it[nameKey] as String, it[countryKey] as String) } ) } @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
Zapisywanie listy
Aby uniknąć konieczności definiowania kluczy mapy, możesz też użyć funkcji listSaver
i użyj ich jako kluczy:
data class City(val name: String, val country: String) val CitySaver = listSaver<City, Any>( save = { listOf(it.name, it.country) }, restore = { City(it[0] as String, it[1] as String) } ) @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
Posiadacze stanów w usłudze Compose
Prostym przenoszeniem stanów można zarządzać bezpośrednio w funkcjach kompozycyjnych. Jeśli jednak ilość stanu, której należy śledzić wzrost, lub logika w przypadku funkcji kompozycyjnych, warto przydzielić w zakresie logiki i obowiązków stanowych w odniesieniu do innych klas: właścicieli państw.
Zapoznaj się z dokumentacją na temat zbierania stanów w funkcji tworzenia wiadomości lub, ogólniej, Strona Właściciele stanów i stan interfejsu w przewodniku po architekturze, aby dowiedzieć się więcej.
Ponownie wywołuj zapamiętywanie obliczeń po zmianie kluczy
Interfejs remember
API jest często używany razem z MutableState
:
var name by remember { mutableStateOf("") }
W tym przypadku użycie funkcji remember
sprawia, że wartość MutableState
obowiązuje dalej
przekomponowania.
Ogólnie funkcja remember
przyjmuje parametr lambda calculation
. Gdy remember
to pierwsze uruchomienie, wywołuje funkcję lambda calculation
i zapisuje jej wynik. W trakcie
przekomponowanie, funkcja remember
zwraca ostatnio zapisaną wartość.
Oprócz stanu buforowania za pomocą remember
możesz też przechowywać dowolne obiekty
w wyniku operacji w Kompozycji, której zainicjowanie jest kosztowne
obliczeń. Możesz nie chcieć powtarzać tych obliczeń przy każdej zmianie kompozycji.
Przykładem może być utworzenie drogiego obiektu ShaderBrush
operacja:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
Funkcja remember
przechowuje wartość, dopóki nie opuści Kompozycji. Występuje jednak
unieważnienia wartości z pamięci podręcznej. Interfejs API remember
pobiera też key
lub
keys
. Jeśli któryś z tych klawiszy się zmieni, następnym razem, gdy funkcja
ponowne kompilowanie danych, remember
unieważnia pamięć podręczną i wykonuje obliczenia
blok lambda. Mechanizm ten daje Ci kontrolę nad czasem trwania
w kompozycji. Obliczenie będzie obowiązywać do momentu wprowadzenia danych wejściowych
zmiany, a nie do momentu, gdy zapamiętana wartość opuści Kompozycję.
Poniższe przykłady pokazują, jak działa ten mechanizm.
W tym fragmencie kodu utworzony jest ShaderBrush
, który jest używany jako tło.
obraz kompozycyjnego Box
. remember
przechowuje instancję ShaderBrush
ponieważ odtwarzanie jest drogie, jak wyjaśniliśmy wcześniej. remember
podejmowania
avatarRes
jako parametr key1
, który jest wybranym obrazem tła. Jeśli
avatarRes
, pędzel ponownie komponuje się z nowym obrazem i stosuje się ponownie do
Box
. Może się tak zdarzyć, gdy użytkownik wybierze inny obraz
z selektora.
@Composable private fun BackgroundBanner( @DrawableRes avatarRes: Int, modifier: Modifier = Modifier, res: Resources = LocalContext.current.resources ) { val brush = remember(key1 = avatarRes) { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) } Box( modifier = modifier.background(brush) ) { /* ... */ } }
W następnym fragmencie kodu stan został przeniesiony do klasy zwykłej posiadacza stanu
MyAppState
Udostępnia funkcję rememberMyAppState
do zainicjowania
instancji klasy za pomocą metody remember
. Ujawnienie takich funkcji w celu utworzenia
które przetrwają ponowne kompozycje, jest typowym wzorcem w Compose.
Funkcja rememberMyAppState
odbiera windowSizeClass
, który służy jako
parametr key
dla remember
. Jeśli ten parametr się zmieni, aplikacja musi:
odtwórz prostą klasę posiadacza stanu z najnowszą wartością. Może się tak zdarzyć, jeśli:
na przykład obróci urządzenie.
@Composable private fun rememberMyAppState( windowSizeClass: WindowSizeClass ): MyAppState { return remember(windowSizeClass) { MyAppState(windowSizeClass) } } @Stable class MyAppState( private val windowSizeClass: WindowSizeClass ) { /* ... */ }
Funkcja tworzenia korzysta z implementacji klasy jest równe, aby określić, czy klucz ma zmienił i unieważnia przechowywane wartości.
Zapisuj stan z kluczami poza możliwością zmiany kompozycji
Interfejs API rememberSaveable
jest otoką wokół remember
, która może przechowywać
w Bundle
. Ten interfejs API sprawia, że stan może nie tylko przetrwać
rekompozycja, ale też rekreacyjne i zainicjowane przez system śmierć.
Funkcja rememberSaveable
otrzymuje parametry input
w tym samym celu, co
remember
otrzymuje keys
. Pamięć podręczna zostaje unieważniona, gdy dowolne dane wejściowe
. Przy następnym uruchomieniu funkcji rememberSaveable
wykona ponowne wykonanie
blok lambda do obliczeń.
W poniższym przykładzie rememberSaveable
przechowuje userTypedQuery
do
typedQuery
zmiany:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
Więcej informacji
Więcej informacji o stanie i Jetpack Compose znajdziesz w tych artykułach: z dodatkowymi zasobami.
Próbki
Ćwiczenia z programowania
Filmy
Blogi
.Polecane dla Ciebie
- Uwaga: tekst linku wyświetla się, gdy JavaScript jest wyłączony
- Tworzenie architektury interfejsu tworzenia wiadomości
- Zapisywanie stanu interfejsu w narzędziu Compose
- Efekty uboczne w funkcji Compose