Bir uygulamadaki durum, zaman içinde değişebilen herhangi bir değerdir. Bu çok geniş bir tanımdır ve Room veritabanından sınıftaki bir değişkene kadar her şeyi kapsar.
Tüm Android uygulamaları, durumu kullanıcıya gösterir. Android uygulamalarındaki duruma ilişkin birkaç örnek:
- 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üğmelerde dalgalanma animasyonları.
- Kullanıcının bir resmin üzerine çizebileceği çıkartmalar.
Jetpack Compose, Android uygulamasında durumu nerede ve nasıl depolayıp kullanacağınız konusunda net olmanıza yardımcı olur. Bu kılavuzda, durum ile composable'lar arasındaki bağlantı ve Jetpack Compose'un durumla çalışmak için sunduğu API'ler üzerinde durulmaktadır.
Durum ve kompozisyon
Compose, bildirimseldir ve bu nedenle yalnızca aynı composable'ı yeni bağımsız değişkenlerle çağırarak güncellenebilir. Bu bağımsız değişkenler, kullanıcı arayüzü durumunun gösterimleridir. Bir durum her güncellendiğinde yeniden oluşturma işlemi gerçekleşir. Bu nedenle, TextField gibi öğeler, zorunlu XML tabanlı görünümlerde olduğu gibi otomatik olarak güncellenmez. Bir composable'ın buna göre güncellenmesi için yeni durumun açıkça belirtilmesi gerekir.
@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 kodu çalıştırıp metin girmeye çalıştığınızda hiçbir şey olmadığını görürsünüz. Bunun nedeni, TextField parametresinin kendisini güncellememesidir. TextField parametresi, value parametresi değiştiğinde güncellenir. Bunun nedeni, Compose'da oluşturma ve yeniden oluşturmanın çalışma şeklidir.
İlk beste: Beste oluşturmak için ilk kez composable'ları çalıştırma.
Yeniden oluşturma: Veriler değiştiğinde Composition'ı güncellemek için composable'ları yeniden çalıştırma.
İlk oluşturma ve yeniden oluşturma hakkında daha fazla bilgi edinmek için Compose'da Düşünme başlıklı makaleyi inceleyin.
Composable'larda durum
Composable işlevler, bellekte bir nesne depolamak için remember API'sini kullanabilir. remember tarafından hesaplanan bir değer, ilk oluşturma sırasında Composition'da saklanır ve saklanan değer, yeniden oluşturma sırasında döndürülür. remember, hem değiştirilebilir hem de değiştirilemez nesneleri depolamak için kullanılabilir.
mutableStateOf, Compose çalışma zamanına entegre edilmiş bir observable türü olan gözlemlenebilir MutableState<T> oluşturur.
interface MutableState<T> : State<T> {
override var value: T
}
value'da yapılan tüm değişiklikler, value okuyan tüm composable işlevlerin yeniden oluşturulmasını planlar.
Bir composable'da MutableState nesnesi bildirmenin üç yolu vardır:
val mutableState = remember { mutableStateOf(default) }var value by remember { mutableStateOf(default) }val (value, setValue) = remember { mutableStateOf(default) }
Bu bildirimler eşdeğerdir ve durumun farklı kullanımları için söz dizimi kolaylığı olarak sağlanır. Yazdığınız composable'da en kolay okunabilen kodu üreteni seçmelisiniz.
by temsilci söz dizimi için aşağıdaki içe aktarmalar gerekir:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
Hatırlanan değeri diğer composable'lar için parametre olarak veya hangi composable'ların görüntüleneceğini değiştirmek üzere ifadelerde mantık olarak bile kullanabilirsiniz. Örneğin, ad boşsa selamlama mesajının gösterilmesini istemiyorsanız durumu bir if ifadesinde 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şturma işlemleri arasında durumu korumanıza yardımcı olsa da yapılandırma değişiklikleri arasında durum korunmaz. Bunun için rememberSaveable uygulamasını kullanmanız gerekir. rememberSaveable, Bundle içine kaydedilebilecek tüm değerleri otomatik olarak kaydeder. Diğer değerler için özel bir kaydedici nesne iletebilirsiniz.
Desteklenen diğer eyalet türleri
Compose, durumu tutmak 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, durumu değiştiğinde composable'ların otomatik olarak yeniden oluşturulabilmesi için bu türü State<T> türüne dönüştürmeniz gerekir.
Compose, Android uygulamalarında kullanılan yaygın gözlemlenebilir türlerden State<T> oluşturma işlevleriyle birlikte gelir. Bu entegrasyonları kullanmadan önce uygun yapıları aşağıdaki şekilde ekleyin:
Flow:collectAsStateWithLifecycle()collectAsStateWithLifecycle(), yaşam döngüsüne duyarlı bir şekildeFlowdeğerlerini toplar ve uygulamanızın uygulama kaynaklarını korumasını sağlar. ComposeState'dan yayılan en son değeri temsil eder. Bu API'yi, Android uygulamalarında akış toplamak için önerilen yöntem olarak kullanın.build.gradledosyasında şu bağımlılık gereklidir (2.6.0-beta01 veya daha yeni bir sürüm olmalıdır):
Kotlin
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.10.0")
}
Modern
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.10.0"
}
-
collectAsState,collectAsStateWithLifecycleile benzerdir. ÇünküFlowdeğerlerini toplar ve bunları Compose State'a dönüştürür.Platformdan bağımsız kod için yalnızca Android'e özel olan
collectAsStateWithLifecycleyerinecollectAsStatekullanın.collectAsState,compose-runtime'te kullanılabildiği için ek bağımlılıklar gerekmez. -
observeAsState()buLiveDatadeğerini gözlemlemeye başlar veStatekullanarak değerlerini gösterir.build.gradledosyasında aşağıdaki bağımlılık gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.11.4")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.11.4"
}
-
subscribeAsState(), RxJava2'nin reaktif akışlarını (ör.Single,Observable,Completable) ComposeState'e dönüştüren uzantı işlevleridir.build.gradledosyasında aşağıdaki bağımlılık gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.11.4")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.11.4"
}
-
subscribeAsState(), RxJava3'ün reaktif akışlarını (ör.Single,Observable,Completable) ComposeState'e dönüştüren uzantı işlevleridir.build.gradledosyasında aşağıdaki bağımlılık gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.11.4")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.11.4"
}
Durum bilgili ve durum bilgisiz karşılaştırması
Bir nesneyi depolamak için remember kullanan bir composable, dahili durum oluşturarak composable'ı durumlu hale getirir. HelloContent, name durumunu dahili olarak tuttuğu ve değiştirdiği için durum bilgisi olan bir composable örneğidir. Bu, arayan kişinin durumu kontrol etmesi gerekmediği ve durumu kendisinin yönetmesi gerekmeden kullanabileceği durumlarda yararlı olabilir. Ancak dahili duruma sahip composable'lar daha az yeniden kullanılabilir ve test edilmesi daha zordur.
Durumsuz bir composable, herhangi bir durumu tutmayan composable'dır. Durumsuzluk elde etmenin yaygın bir yolu durum yükseltme kullanmaktır.
Yeniden kullanılabilir composable işlevler geliştirirken genellikle aynı composable işlevin hem durum bilgisi olan hem de durum bilgisi olmayan bir sürümünü kullanıma sunmak istersiniz. Durum bilgili sürüm, durumla ilgilenmeyen çağrılar için uygundur. Durum bilgisiz sürüm ise durumu kontrol etmesi veya yükseltmesi gereken çağrılar için gereklidir.
State hoisting
Compose'da durum yükseltme, composable'ı durum bilgisiz hale getirmek için durumu composable'ın çağrıya taşıma işlemidir. Jetpack Compose'da durum yükseltmeyle ilgili genel kalıp, durum değişkenini iki parametreyle değiştirmektir:
value: T: Gösterilecek mevcut değeronValueChange: (T) -> Unit: Değerin değişmesini isteyen bir etkinlik. BuradaT, önerilen yeni değerdir.
Ancak onValueChange ile sınırlı değilsiniz. Birleştirilebilir için daha spesifik etkinlikler uygunsa bunları lambda'ları kullanarak tanımlamanız gerekir.
Bu şekilde yükseltilen durumun bazı önemli özellikleri vardır:
- Tek doğruluk kaynağı: Durumu kopyalamak yerine taşıyarak tek bir doğruluk kaynağı olmasını sağlıyoruz. Bu sayede hatalar önlenir.
- Kapsüllenmiş: Yalnızca durum bilgisi olan composable'lar durumlarını değiştirebilir. Tamamen dahili bir hizmettir.
- Paylaşılabilir: Yükseltilmiş durum birden fazla composable ile paylaşılabilir.
nameöğesini farklı bir composable'da okumak istiyorsanız hoisting bunu yapmanıza olanak tanır. - Araya girilebilir: Durumsuz composable'ları arayanlar, durumu değiştirmeden önce etkinlikleri yoksayabilir veya değiştirebilir.
- Ayrılmış: Durum bilgisiz composable'ların durumu herhangi bir yerde depolanabilir. Örneğin, artık
nameöğesiniViewModeliçine taşıyabilirsiniz.
Örnekte, name ve onValueChange öğelerini HelloContent öğesinden çıkarıp HelloContent öğesini çağıran bir HelloScreen composable'a taşıyorsunuz.
@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") }) } }
Durumu HelloContent dışına taşıyarak composable hakkında akıl yürütmek, farklı durumlarda yeniden kullanmak ve test etmek daha kolay olur. HelloContent, durumunun nasıl depolandığından bağımsızdır. Ayrıştırma, HelloScreen öğesini değiştirirseniz veya yerine başka bir öğe koyarsanız HelloContent öğesinin uygulanma şeklini değiştirmeniz gerekmediği anlamına gelir.
Durumun azaldığı ve etkinliklerin arttığı bu kalıba tek yönlü veri akışı adı verilir. Bu durumda, durum HelloScreen'dan HelloContent'ye düşer ve etkinlikler HelloContent'den HelloScreen'ya yükselir. Tek yönlü veri akışını izleyerek, kullanıcı arayüzünde durumu gösteren composable'ları, uygulamanızın durumu depolayan ve değiştiren bölümlerinden ayırabilirsiniz.
- Durum, durumu kullanan (okuyan) tüm composable'ların en azından en düşük ortak üst öğesine yükseltilmelidir.
- Durum, en azından değiştirilebileceği en yüksek seviyeye (yazma) yükseltilmelidir.
- Aynı etkinliklere yanıt olarak iki durum değişiyorsa birlikte yükseltilmelidir.
Durumu bu kuralların gerektirdiğinden daha yukarı taşıyabilirsiniz ancak durumu daha aşağı taşırsanız tek yönlü veri akışını takip etmek zor veya imkansız hale gelir.
Daha fazla bilgi edinmek için Durumun nereye yerleştirileceği sayfasına bakın.
Compose'da durumu geri yükleme
rememberSaveable API, yeniden oluşturma işlemlerinde ve ayrıca kaydedilmiş örnek durumu mekanizması kullanılarak etkinlik veya süreç yeniden oluşturma işlemlerinde durumu koruduğu için remember ile benzer şekilde çalışır. Örneğin, ekran döndürüldüğünde bu durum yaşanır.
Durumu depolama yöntemleri
Bundle'ya eklenen tüm veri türleri otomatik olarak kaydedilir. Bundle'ya eklenemeyen bir öğeyi kaydetmek istiyorsanız birkaç seçeneğiniz vardır.
Parcelize
En basit çözüm, nesneye @Parcelize açıklamasını eklemektir. Nesne paketlenebilir hale gelir ve paketlenebilir. Örneğin, bu kod, paketlenebilir City veri türü oluşturur ve durumu kaydeder.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
MapSaver
@Parcelize herhangi bir nedenle uygun değilse bir nesneyi, sistemin Bundle öğesine kaydedebileceği bir değerler kümesine dönüştürmek için kendi kuralınızı tanımlamak üzere mapSaver öğesini 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")) } }
ListSaver
Harita için anahtarları tanımlamanıza gerek kalmaması için listSaver
öğesini de kullanabilir ve dizinlerini anahtar olarak 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 durum bilgisi depolayıcılar
Basit durum yükseltme, composable işlevlerin kendisinde yönetilebilir. Ancak izlenecek durum miktarı artarsa veya composable işlevlerde gerçekleştirilecek mantık ortaya çıkarsa mantık ve durum sorumluluklarını diğer sınıflara (durum tutucular) devretmek iyi bir uygulamadır.
Diğer materyallerde durum tutucuların hoisted state objects olarak da adlandırıldığını unutmayın.
Daha fazla bilgi edinmek için Compose'da durum yükseltme belgelerine veya daha genel olarak mimari kılavuzundaki Durum tutucular ve kullanıcı arayüzü durumu sayfasına göz atın.
Anahtarlar değiştiğinde hatırlatma hesaplamalarını yeniden tetikleme
remember API'si genellikle MutableState ile birlikte kullanılır:
var name by remember { mutableStateOf("") }
Burada, remember işlevinin kullanılması MutableState değerinin yeniden oluşturma işlemlerinde korunmasını sağlar.
Genel olarak remember, calculation lambda parametresi alır. remember ilk kez çalıştırıldığında calculation lambda'sını ç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, başlatılması veya hesaplanması maliyetli olan herhangi bir nesneyi ya da işlem sonucunu Composition'da depolamak için remember öğesini de kullanabilirsiniz. Bu hesaplamayı her yeniden oluşturmada tekrarlamak istemeyebilirsiniz.
Örneğin, pahalı bir işlem olan şu ShaderBrush nesnesini oluşturmak:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
remember, değer Composition'dan ayrılana kadar değeri depolar. Ancak, önbelleğe alınmış değeri geçersiz kılmanın bir yolu vardır. remember API'si key veya keys parametresini de alır. Bu anahtarlardan herhangi biri değişirse işlev bir sonraki remember yeniden oluşturulduğunda önbelleği geçersiz kılar ve hesaplama lambda bloğunu tekrar yürütür. Bu mekanizma, Composition'daki bir nesnenin kullanım ömrünü kontrol etmenizi sağlar. Hesaplama, hatırlanan değer Kompozisyon'dan ayrılana kadar değil, girişler değişene kadar geçerli kalır.
Aşağıdaki örneklerde bu mekanizmanın nasıl çalıştığı gösterilmektedir.
Bu snippet'te, Box composable'ının arka plan boyası olarak kullanılan bir ShaderBrush oluşturuluyor. remember, daha önce açıklandığı gibi yeniden oluşturmak maliyetli olduğundan ShaderBrush örneğini depolar. remember, seçilen arka plan resmi olan key1 parametresini alır.avatarRes avatarRes değişirse fırça yeni resimle yeniden oluşturulur ve avatarRes'ya yeniden uygulanır.Box Bu durum, kullanıcı bir seçiciden arka plan olarak başka bir resim seçtiğinde ortaya çıkabilir.
@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) ) { /* ... */ } }
Aşağıdaki snippet'te durum, düz durum bilgisi depolayıcı sınıfına yükseltilir
MyAppState. rememberMyAppState işlevi, remember kullanılarak sınıfın bir örneğini başlatmak için kullanılır. Bu tür işlevleri, yeniden oluşturma işlemlerinden sonra da varlığını sürdürecek bir örnek oluşturmak için kullanmak, Compose'da yaygın bir yöntemdir. rememberMyAppState işlevi, remember için key parametresi olarak kullanılan windowSizeClass değerini alır. Bu parametre değişirse uygulamanın, düz durum bilgisi depolayıcı sınıfını en son değerle yeniden oluşturması gerekir. Örneğin, kullanıcı cihazı döndürürse bu durum 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 equals uygulamasını kullanır.
Yeniden oluşturmanın ötesinde anahtarlarla durumu depolama
rememberSaveable API, remember etrafındaki bir sarmalayıcıdır ve verileri Bundle içinde saklayabilir. Bu API, durumun yalnızca yeniden oluşturma sırasında değil, etkinlik yeniden oluşturma ve sistem tarafından başlatılan işlem sonlandırma sırasında da korunmasına olanak tanır.
rememberSaveable, remember'nin keys aldığı parametreleri aynı amaçla alır.input Girişlerden herhangi biri değiştiğinde önbellek geçersiz kılınır. İşlev bir sonraki sefer yeniden oluşturulduğunda rememberSaveable hesaplama lambda bloğunu yeniden yürütür.
Aşağıdaki örnekte, rememberSaveable, typedQuery değişene kadar userTypedQuery değerini saklar:
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 göz atın.
Örnekler
Codelab uygulamaları
Videolar
Bloglar
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Compose kullanıcı arayüzünüzü tasarlama
- Compose'da kullanıcı arayüzü durumunu kaydetme
- Compose'da yan etkiler