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ı, kullanıcıya durumu gösterir. Android uygulamalarındaki duruma dair 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 açık olmanıza yardımcı olur. Bu kılavuzda, durum ile composable'lar arasındaki bağlantı ve Jetpack Compose'un durumla daha kolay ç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 durumu açıkça belirtmeniz 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 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, bir nesneyi bellekte depolamak için remember API'sini kullanabilir. remember tarafından hesaplanan bir değer, ilk oluşturma sırasında Composition'da depolanır ve depolanan 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
gözlemlenebilir bir tür olan
MutableState<T> gözlemlenebilirini oluşturur. Bu tür, Compose çalışma zamanıyla entegre edilmiştir.
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 selamlamayı görüntülemek 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, aşağıda belirtildiği gibi uygun yapıları 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. ComposeStateişlevinden 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 ve değerleriniStatearacılığıyla gösterir.build.gradledosyasında aşağıdaki bağımlılık gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.11.1")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.11.1"
}
-
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.1")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.11.1"
}
-
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.1")
}
Modern
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.11.1"
}
Durum bilgili ve durum bilgisiz karşılaştırması
Bir nesneyi depolamak için remember kullanan composable'lar dahili durum oluşturarak composable'ı durumlu hale getirir. HelloContent, name durumunu dahili olarak tuttuğu ve değiştirdiği için durumlu bir composable örneğidir. Bu, arayanın durumu kontrol etmesi gerekmediği ve durumu kendisi yönetmek zorunda kalmadan kullanabileceği durumlarda faydalı olabilir. Ancak dahili duruma sahip composable'lar genellikle daha az yeniden kullanılabilir ve test edilmesi daha zordur.
Durumsuz composable, herhangi bir durumu tutmayan composable'dır. Durumsuzluk elde etmenin kolay bir yolu state hoisting kullanmaktır.
Yeniden kullanılabilir composable'lar geliştirirken genellikle aynı composable'ın hem durum bilgili hem de durum bilgisiz 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ükseltme için 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ış: Durumsuz composable'ların durumu herhangi bir yerde depolanabilir. Örneğin,
nameartıkViewModeliçine taşınabilir.
Ö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 daha kolay akıl yürütebilir, farklı durumlarda yeniden kullanabilir ve test edebilirsiniz. HelloContent, durumunun nasıl depolandığından bağımsızdır. Bağımsızlık, HelloScreen öğesini değiştirir veya değiştirirseniz HelloContent öğesinin nasıl uygulandığını değiştirmeniz gerekmediği anlamına gelir.
Durumun azaldığı ve etkinliklerin arttığı bu kalıba tek yönlü veri akışı denir. Bu durumda durum, HelloScreen'dan HelloContent'a düşer ve etkinlikler HelloContent'dan HelloScreen'a 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.
Daha fazla bilgi edinmek için Durumun nereye yerleştirileceği sayfasına bakın.
Compose'da durumu geri yükleme
rememberSaveable API, remember'ye benzer şekilde çalışır. Çünkü yeniden oluşturma işlemlerinde ve ayrıca kayıtlı örnek durumu mekanizması kullanılarak etkinlik veya süreç yeniden oluşturma işlemlerinde durumu korur. Örneğin, bu durum ekran döndürüldüğünde gerçekleşir.
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 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
değerini 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.
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 parametresini alır. remember ilk kez çalıştırıldığında calculation lambdasını çağırır ve sonucunu saklar. Yeniden oluşturma sırasında remember, en son saklanan 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şlemin sonucunu Composition'da depolamak için remember öğesini de kullanabilirsiniz. Bu hesaplamayı her yeniden oluşturmada tekrarlamak istemeyebilirsiniz. Bir örnek olarak, maliyetli bir işlem olan şu ShaderBrush nesnesinin oluşturulması verilebilir:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
remember, değeri Composition'dan ayrılana kadar depolar. Ancak, ö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şlev bir sonraki sefer yeniden oluşturulduğunda remember önbelleği geçersiz kılar ve hesaplama lambda bloğunu tekrar yürütür. Bu mekanizma, Composition'daki bir nesnenin yaşam süresi üzerinde kontrol sahibi olmanızı sağlar. Hesaplama, hatırlanan değer Composition'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 Box'ye yeniden uygulanır. 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) ) { /* ... */ } }
Sonraki snippet'te durum, basit bir durum bilgisi depolayıcı sınıfına yükseltilir
MyAppState. remember kullanılarak sınıfın bir örneğini ilk kullanıma hazırlamak için rememberMyAppState işlevini kullanır. Yeniden oluşturma işlemlerinden etkilenmeyen bir örnek oluşturmak için bu tür işlevleri 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, basit 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 depolayabilir. 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 baki kalmasını sağlar. rememberSaveable, remember'ün keys parametrelerini aldığı amaçla aynı amaç için input parametrelerini alır. Girişlerden herhangi biri değiştiğinde önbellek geçersiz kılınır. İşlev bir sonraki yeniden oluşturma işleminde hesaplama lambda bloğunu yeniden yürütür.rememberSaveable
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