Bir uygulamadaki durum, zaman içinde değişebilen herhangi bir değerdir. Bu çok geniş bir tanımdır ve oda veritabanından sınıftaki bir değişkene kadar her şeyi kapsar.
Tüm Android uygulamaları, durumu kullanıcıya gösterir. Aşağıda, Android uygulamalarındaki durum ile ilgili birkaç örnek verilmiştir:
- Ağ bağlantısı kurulamadığında gösterilen bir Snackbar.
- Bir blog yayını ve ilişkili yorumlar.
- Kullanıcı tıkladığında oynatılan düğmelerdeki dalgalı animasyonlar.
- Kullanıcının bir resmin üzerine çizebileceği çıkartmalar.
Jetpack Compose, bir Android uygulamasında durumu nerede ve nasıl depoladığınızı ve kullandığınızı açıkça belirtmenize yardımcı olur. Bu kılavuz, durum ile composable'lar arasındaki bağlantıya ve Jetpack Compose'un durumlarla daha kolay çalışmayı sunduğu API'lere odaklanmaktadır.
Durum ve bileşim
Oluşturma bildirim temellidir. Bu nedenle, aynı composable'ı yeni bağımsız değişkenlerle çağırarak güncellemenin tek yolu budur. Bu bağımsız değişkenler, kullanıcı arayüzü durumunu temsil eder. Bir eyalet her güncellendiğinde yeniden oluşturma işlemi gerçekleşir. Sonuç olarak TextField
gibi öğeler, zorunlu XML tabanlı görünümlerde olduğu gibi otomatik olarak güncellenmez. composable'ın uygun şekilde güncellenmesi için yeni durumu açıkça bildirilmelidir.
@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") } ) } }
Bu komutu çalıştırıp metin girmeye çalışırsanız hiçbir şey olmadığını görürsünüz. Çünkü TextField
kendini güncellemez. value
parametresi değiştiğinde güncellenir. Bu durum, kompozisyon ve yeniden kompozisyonun Oluşturma'daki işleyiş şeklinden kaynaklanır.
İlk beste ve yeniden kompozisyon hakkında daha fazla bilgi edinmek için Yazmada Düşünme bölümüne bakın.
Composable'lar içindeki durum
Oluşturulabilir işlevler, bir nesneyi bellekte depolamak için remember
API'yi kullanabilir. remember
tarafından hesaplanan bir değer, ilk beste sırasında Beste içinde depolanır ve depolanan değer, yeniden oluşturma sırasında döndürülür.
remember
hem değişken hem de sabit nesneleri depolamak için kullanılabilir.
mutableStateOf
, oluşturma çalışma zamanıyla entegre edilmiş gözlemlenebilir bir tür olan MutableState<T>
adlı gözlemlenebilir bir tür oluşturur.
interface MutableState<T> : State<T> {
override var value: T
}
value
üzerinde yapılan değişiklikler, value
değerini okuyan composable işlevlerin yeniden oluşturulmasını planlar.
Bir composable'da MutableState
nesnesi tanımlamanın üç yolu vardır:
val mutableState = remember { mutableStateOf(default) }
var value by remember { mutableStateOf(default) }
val (value, setValue) = remember { mutableStateOf(default) }
Bu beyanlar eşdeğerdir ve durumun farklı kullanımları için söz dizimi şekeri olarak sağlanır. Yazdığınız composable'da en kolay okunan kodu üreten kodu seçmeniz gerekir.
by
yetki söz dizimi için aşağıdaki içe aktarma işlemleri gerekir:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
Hatırlanan değeri diğer composable'lar için bir parametre olarak, hatta hangi composable'ların görüntüleneceğini değiştirmek için ifadelerde mantık olarak kullanabilirsiniz. Örneğin, ad boşsa karşılama mesajının gösterilmesini istemiyorsanız if
ifadesindeki durumu kullanın:
@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
, yeniden oluşturmalarda durumu korumanıza yardımcı olur ancak yapılandırma değişiklikleri genelinde durum korunmaz. Bunun için rememberSaveable
kullanmanız gerekir. rememberSaveable
, Bundle
içine kaydedilebilecek tüm değerleri otomatik olarak kaydeder. Diğer değerler için özel tasarruf nesnesi ekleyebilirsiniz.
Desteklenen diğer durum türleri
Oluşturma işlemi, durumu muhafaza etmek için MutableState<T>
kullanmanızı gerektirmez. Diğer gözlemlenebilir türleri destekler. Compose'da başka bir gözlemlenebilir türü okumadan önce composable'ların durum değiştiğinde otomatik olarak yeniden oluşturabilmesi için bunu State<T>
biçimine dönüştürmeniz gerekir.
Android uygulamalarında yaygın olarak kullanılan gözlemlenebilir türlerden State<T>
oluşturmanıza olanak tanıyan işlevlerle içerik gönderin. Bu entegrasyonları kullanmadan önce aşağıda belirtildiği gibi uygun yapıları ekleyin:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
,Flow
'teki değerleri yaşam döngüsüne duyarlı bir şekilde toplayarak uygulamanızın uygulama kaynaklarını korumasına olanak tanır. OluşturmaState
öğesinden yayınlanan en son değeri temsil eder. Android uygulamalarında akışları toplamak için önerilen yöntem olarak bu API'yi kullanın.build.gradle
dosyasında aşağıdaki dependency (bağımlılık) gereklidir (2.6.0-beta01 veya daha yeni olması gerekir):
Kotlin
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
}
Modern
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.2"
}
-
collectAsState
,Flow
öğesinden değer toplayıp Oluştur'aState
dönüştürdüğü içincollectAsStateWithLifecycle
ile benzerdir.Platformdan bağımsız kod için yalnızca Android'de bulunan
collectAsStateWithLifecycle
yerinecollectAsState
kullanın.compose-runtime
bölgesinde kullanılabildiğindencollectAsState
için ek bağımlılıklara gerek yoktur. -
observeAsState()
, buLiveData
öğesini gözlemlemeye başlar veState
aracılığıyla değerlerini temsil eder.build.gradle
dosyasında aşağıdaki bağımlılık gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.6.1")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.6.1"
}
-
subscribeAsState()
, RxJava2'nin reaktif akışlarını (ör.Single
,Observable
,Completable
) Oluşturma'yaState
dönüştüren uzantı işlevleridir.build.gradle
dosyasında aşağıdaki bağımlılık gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.6.1")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.6.1"
}
-
subscribeAsState()
, RxJava3'ün reaktif akışlarını (ör.Single
,Observable
,Completable
) Oluşturma'yaState
dönüştüren uzantı işlevleridir.build.gradle
dosyasında aşağıdaki bağımlılık gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.6.1")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.6.1"
}
Durum bilgili ve durum bilgisiz
Bir nesneyi depolamak için remember
kullanan bir composable, dahili durum oluşturarak composable'ı durum bilgili hale getirir. HelloContent
, name
durumunu içeride tutup değiştirdiği için durum bilgili composable'a örnektir. Bu özellik, arayan kişinin durumu kontrol etmesi gerekmediği ve kendi başına yönetmek zorunda kalmadan kullanabileceği durumlarda yararlı olabilir. Bununla birlikte, dahili durumu olan composable'ların tekrar kullanılabilirlik durumu daha azdır ve test edilmesi daha zordur.
Durum bilgisiz composable, herhangi bir durum içermeyen composable'dır. Durum bilgisizliği sağlamanın kolay bir yolu da eyalet kaldırma özelliğini kullanmaktır.
Yeniden kullanılabilir composable'lar geliştirirken genellikle aynı composable'ın hem durum bilgili hem de durum bilgisiz sürümünü sunmak istersiniz. Durum bilgili sürüm, durumla ilgilenmeyen arayanlar için kullanışlıdır. Durum bilgisiz sürüm ise, eyaleti kontrol etmesi veya kaldırması gereken arayanlar için gereklidir.
Yükseliş
Compose'da durum yükseltme, composable'ı durum bilgisiz hale getirmek için bir composable'ın arayanına durum taşıma kalıbıdır. Jetpack Compose'da durum yükseltme için genel kalıp, durum değişkenini iki parametreyle değiştirmektir:
value: T
: görüntülenecek geçerli değeronValueChange: (T) -> Unit
: Değerin değiştirilmesini isteyen bir etkinliktir. BuradaT
, önerilen yeni değerdir
Ancak, bu hizmet onValueChange
ile sınırlı değildir. composable için daha belirli etkinlikler uygunsa lambdas kullanarak tanımlamalısınız.
Bu şekilde kaldırılan eyaletin bazı önemli özellikleri vardır:
- Tek doğru kaynağı: Durumu kopyalamak yerine taşıyarak tek bir doğru kaynak olmasını sağlarız. Bu, hataları önlemenize yardımcı olur.
- Kapsüllü: Yalnızca durum bilgili composable'lar durumlarını değiştirebilir. Tamamen dahili.
- Paylaşılabilir: Kaldırılmış durumu birden fazla composable ile paylaşılabilir.
name
öğesini farklı bir composable'da okumak isterseniz kaldırma özelliği bunu yapmanıza olanak tanır. - Anlaşılabilir: Durum bilgisiz composable'ları çağıran kişiler, durumu değiştirmeden önce etkinlikleri yoksaymaya veya değiştirmeye karar verebilir.
- Ayrıştırılmış: Durum bilgisiz composable'ların durumu herhangi bir yerde depolanabilir. Örneğin, artık
name
öğesiniViewModel
içine taşımak mümkündür.
Örnekte, name
ve onValueChange
öğelerini HelloContent
konumundan çıkarıp ağacın yukarısına, HelloContent
adlı bir HelloScreen
composable'ına taşırsınız.
@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") }) } }
Eyalet HelloContent
dışına çıkarıldığında composable hakkında akıl yürütme, onu farklı durumlarda yeniden kullanmak ve test etmek daha kolay olur. HelloContent
, durumunun depolanma şeklinden ayrıştırılır. Ayrıştırma, HelloScreen
değerini değiştirir veya değiştirirseniz HelloContent
öğesinin uygulanma şeklini değiştirmenize gerek olmadığı anlamına gelir.
Durumun düştüğü, etkinliklerin yükseldiği kalıpa tek yönlü veri akışı denir. Bu durumda, eyalet HelloScreen
değerinden HelloContent
değerine, etkinlikler ise HelloContent
değerinden HelloScreen
değerine yükselir. Tek yönlü veri akışını gerçekleştirerek kullanıcı arayüzünde durumu görüntüleyen composable'ları, uygulamanızın durumunu depolayan ve değiştiren bölümlerinden ayırabilirsiniz.
Daha fazla bilgi edinmek için Eyalet nereden kaldırılır? sayfasına bakın.
Oluşturma'da durum geri yükleniyor
rememberSaveable
API, yeniden oluşturmalarda ve kaydedilen örnek durum mekanizmasını kullanarak etkinlik veya süreç yeniden oluşturma genelinde durumu koruduğu için remember
ile benzer şekilde davranır. Örneğin, ekran döndürüldüğünde
bu durum gerçekleşir.
Durumu depolamanın yolları
Bundle
öğesine eklenen tüm veri türleri otomatik olarak kaydedilir. Bundle
içine eklenemeyen bir öğeyi kaydetmek istiyorsanız yararlanabileceğiniz çeşitli seçenekler vardır.
Parselleştir
En basit çözüm, nesneye @Parcelize
ek açıklamasını eklemektir. Nesne ayrıştırılabilir hale gelir ve paketlenebilir. Örneğin, bu kod ayrıştırılabilir bir City
veri türü oluşturur ve duruma kaydeder.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
Harita Tasarruf Aracı
Herhangi bir nedenle @Parcelize
uygun değilse bir nesneyi sistemin Bundle
konumuna kaydedebileceği bir değer grubuna dönüştürmek için kendi kuralınızı tanımlamak üzere mapSaver
kullanabilirsiniz.
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")) } }
Liste Kaydeden
Harita için anahtarları tanımlamanızı önlemek amacıyla listSaver
ve dizinlerini anahtar olarak da kullanabilirsiniz:
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")) } }
Compose'da eyalet sahipleri
Basit durum yükseltme, composable işlevlerden yönetilebilir. Ancak artışları izlemek için gereken durum miktarı veya composable işlevlerde uygulama mantığı ortaya çıkarsa, mantık ve eyalet sorumluluklarını diğer sınıflara (eyalet sahiplerine) devretmek iyi bir uygulamadır.
Daha fazla bilgi edinmek için Compose'da eyalet yükseltme belgelerine veya daha genel olarak mimari kılavuzundaki Eyalet sahipleri ve Kullanıcı Arayüzü Durumu sayfasına bakın.
Yeniden tetikleyici, anahtarlar değiştiğinde hesaplamaları hatırlar
remember
API, sık sık MutableState
ile birlikte kullanılır:
var name by remember { mutableStateOf("") }
Burada, remember
işlevi kullanıldığında MutableState
değeri yeniden oluşturmalardan sonra da dayanabilir.
remember
genellikle calculation
lambda parametresini alır. remember
ilk çalıştırıldığında calculation
lambda'yı çağırır ve sonucunu depolar. Yeniden oluşturma sırasında remember
, en son depolanan değeri döndürür.
Önbelleğe alma durumunun yanı sıra, Beste'deki ilk kullanıma hazırlama veya hesaplama işlemi pahalı olan herhangi bir nesneyi veya işlemin sonucunu depolamak için remember
kullanabilirsiniz. Bu hesaplamayı her yeniden oluşturmada tekrarlamak istemeyebilirsiniz.
Örnek olarak, pahalı bir işlem olan bu ShaderBrush
nesnesinin oluşturulması verilebilir:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
remember
, Beste'den ayrılana kadar değeri saklar. Bununla birlikte, önbelleğe alınan değeri geçersiz kılmanın bir yolu vardır. remember
API, key
veya keys
parametresini de alır. Bu anahtarlardan herhangi biri değişirse işlevin bir sonraki yeniden derlemesinde remember
önbelleği geçersiz kılar ve hesaplama lambda blokunu tekrar yürütür. Bu mekanizma, Beste'deki bir nesnenin
yaşam süresi üzerinde kontrol sahibi olmanızı sağlar. Hesaplama, hatırlanan değer Beste'den ayrılana kadar değil, girişler değişene kadar geçerli kalır.
Aşağıdaki örnekler bu mekanizmanın nasıl çalıştığını gösterir.
Bu snippet'te ShaderBrush
oluşturulur ve Box
composable'ının arka plan boyası olarak kullanılır. remember
, daha önce açıklandığı gibi yeniden oluşturmak pahalı olduğu için ShaderBrush
örneğini depolar. remember
, avatarRes
parametresini key1
parametresi olarak alır. Bu, seçili arka plan resmidir. avatarRes
değişirse fırça, yeni resimle yeniden derlenir ve Box
öğesine yeniden uygulanır. Bu durum, kullanıcı bir seçiciden arka plan olarak başka bir resim seçtiğinde meydana gelebilir.
@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) ) { /* ... */ } }
Bir sonraki snippet'te eyalet, düz durum sahibi sınıfına
MyAppState
çekilir. Sınıfın bir örneğini remember
kullanarak başlatmak için bir rememberMyAppState
işlevi sunar. Bu tür işlevlerin, yeniden derlemelerden sonra başarılı olan bir örnek oluşturmak için kullanıma sunulması, Compose'da yaygın bir kalıptır. rememberMyAppState
işlevi, remember
için key
parametresi olarak işlev gören windowSizeClass
değerini alır. Bu parametre değişirse uygulamanın, düz durum tutucu sınıfını en son değerle yeniden oluşturması gerekir. Bu durum, örneğin, kullanıcı cihazı döndürürse oluşabilir.
@Composable private fun rememberMyAppState( windowSizeClass: WindowSizeClass ): MyAppState { return remember(windowSizeClass) { MyAppState(windowSizeClass) } } @Stable class MyAppState( private val windowSizeClass: WindowSizeClass ) { /* ... */ }
Compose, bir anahtarın değişip değişmediğine karar vermek ve depolanan değeri geçersiz kılmak için sınıfın eşittir uygulamasını kullanır.
Durumu, yeniden düzenlemenin ötesinde anahtarlarla depolayın
rememberSaveable
API, verileri Bundle
içinde depolayabilen, remember
etrafındaki bir sarmalayıcıdır. Bu API, eyaletin yalnızca yeniden oluşturma işleminden değil, aynı zamanda etkinlik yeniden oluşturma ve sistem tarafından başlatılan işlem ölümlerinden de kurtulmasına olanak tanır.
rememberSaveable
, remember
hizmetinin keys
aldığı amaçla input
parametre alır. Girişlerden herhangi biri değiştiğinde önbellek geçersiz kılınır. İşlev yeniden derlendiğinde rememberSaveable
, hesaplama lambda bloğunu yeniden yürütür.
Aşağıdaki örnekte rememberSaveable
, userTypedQuery
değerini typedQuery
değişene kadar depolar:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
Daha fazla bilgi
State ve Jetpack Compose hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara başvurun.
Numuneler
Codelab uygulamaları
Videolar
Bloglar
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken görüntülenir
- Compose kullanıcı arayüzünüzü tasarlama
- Oluşturma penceresinde kullanıcı arayüzü durumunu kaydetme
- Compose'daki yan etkiler