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 dair birkaç örnek:
- Ağ bağlantısı kurulamadığında gösterilen bir bilgi çubuğu.
- Bir blog yayını ve ilişkili yorumlar.
- Kullanıcı düğmeleri tıkladığında oynatılan dalga 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 bileşenler arasındaki bağlantıya ve Jetpack Compose'un durumla daha kolay çalışmak için sunduğu API'lere odaklanılmaktadır.
Durum ve kompozisyon
Oluşturma beyandır. Bu nedenle, tek güncelleme yöntemi, aynı derlenebilir öğeyi yeni bağımsız değişkenlerle çağırmaktır. Bu bağımsız değişkenler, kullanıcı arayüzü
durumunu temsil eder. Bir durum her güncellendiğinde yeniden düzenleme gerçekleştirilir. Sonuç olarak, TextField
gibi öğeler, zorunlu XML tabanlı görünümlerde olduğu gibi otomatik olarak güncellenmez. Bir bileşenin uygun şekilde güncellenebilmesi 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ışırsanız hiçbir şey olmadığını görürsünüz. Bunun nedeni, TextField
öğesinin kendisini güncellememesidir, value
parametresi değiştiğinde güncellenir. Bunun nedeni, Oluştur'da derleme ve yeniden derlemenin işleyiş şeklidir.
İlk kompozisyon ve yeniden kompozisyon hakkında daha fazla bilgi edinmek için Oluşturma modunda düşünme başlıklı makaleyi inceleyin.
Birleştirilebilir öğelerdeki durum
Birleştirilebilir işlevler, bir nesneyi bellekte depolamak için remember
API'sini kullanabilir. remember
tarafından hesaplanan bir değer, ilk kompozisyon sırasında kompozisyonda depolanır ve depolanan değer yeniden kompozisyon sırasında döndürülür.
remember
, hem değişken hem de sabit nesneleri depolamak için kullanılabilir.
mutableStateOf
, MutableState<T>
adlı gözlemlenebilir bir tür oluşturur. Bu tür, derleme çalışma zamanında entegre edilmiş bir gözlemlenebilir türdür.
interface MutableState<T> : State<T> {
override var value: T
}
value
'te yapılan değişiklikler, value
değerini okuyan tüm derlenebilir işlevlerin yeniden derlenmesini planlar.
Bir bileşende 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 bildirimler eşdeğerdir ve durumun farklı kullanımları için söz dizimi şekeri olarak sağlanır. Yazdığınız bileşende en kolay okunur kodu üreten seçeneği tercih etmeniz gerekir.
by
yetki verilmiş kullanıcı 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 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ı 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şlemlerinde durumu korumanıza yardımcı olsa da yapılandırma değişikliklerinde 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 bir tasarruf nesnesi iletebilirsiniz.
Desteklenen diğer durum türleri
Compose, durum bilgisini korumak 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, durum değiştiğinde birleştirilebilirlerin otomatik olarak yeniden derlenebilmesi için türü State<T>
olarak dönüştürmeniz gerekir.
Android uygulamalarında kullanılan yaygın gözlemlenebilir türlerden State<T>
oluşturmak için işlevlerle birlikte gönderilir. Bu entegrasyonları kullanmadan önce aşağıda açıklandığı şekilde uygun öğeleri ekleyin:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
,Flow
kaynağından yaşam döngüsüne duyarlı bir şekilde değer toplayarak uygulamanızın uygulama kaynaklarını korumasını sağlar. Oluştur'danState
gönderilen en son değeri temsil eder. Android uygulamalarında akışları toplamak için önerilen yol olarak bu API'yi kullanın.build.gradle
dosyasında aşağıdaki bağımlılığın bulunması gerekir (2.6.0-beta01 veya daha yeni bir sürüm olmalıdır):
Kotlin
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
}
Groovy
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.7"
}
-
collectAsState
,Flow
'den değerler toplayıp OluşturState
işlevine dönüştürdüğü içincollectAsStateWithLifecycle
'e benzer.Yalnızca Android için olan
collectAsStateWithLifecycle
yerine platformdan bağımsız kod içincollectAsState
kullanın.collectAsState
,compose-runtime
'te kullanılabildiği için ek bağımlılıklara ihtiyaç duymaz. -
observeAsState()
, buLiveData
öğesini gözlemlemeye başlar ve değerleriniState
aracılığıyla temsil eder.build.gradle
dosyasında aşağıdaki bağımlılık zorunludur:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.7.5")
}
Eski
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.7.5"
}
-
subscribeAsState()
, RxJava2'nin reaktif akışlarını (ör.Single
,Observable
,Completable
) ComposeState
'e 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.7.5")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.7.5"
}
-
subscribeAsState()
, RxJava3'ün reaktif akışlarını (ör.Single
,Observable
,Completable
) ComposeState
'e dönüştüren uzantı işlevleridir.build.gradle
dosyasında aşağıdaki bağımlılık zorunludur:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.7.5")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.7.5"
}
Durum bilgili ve durum bilgisiz
Bir nesneyi depolamak için remember
kullanan bir bileşen, dahili durum oluşturur ve bileşeni durumlu hale getirir. HelloContent
, name
durumunu dahili olarak saklayıp değiştirdiği için durum bilgisine sahip bir bileşen örneğidir. Bu, arayan kişinin durumu kontrol etmesine gerek olmadığı ve durumu yönetmek zorunda kalmadan kullanabildiği durumlarda yararlı olabilir. Ancak dahili durumu olan bileşenler daha az yeniden kullanılabilir ve test edilmesi daha zordur.
Durumsuz bir bileşen, herhangi bir durum içermeyen bir bileşendir. Durum bilgisizliği sağlamanın kolay bir yolu durum bilgisini kaldırma özelliğini kullanmaktır.
Yeniden kullanılabilir bileşenler geliştirirken genellikle aynı bileşenin hem durum bilgisine sahip hem de durum bilgisi olmayan bir sürümünü göstermek istersiniz. Durum bilgisine sahip sürüm, durumu önemsemeyen arayanlar için kullanışlıdır. Durum bilgisiz sürüm ise durumu kontrol etmesi veya kaldırması gereken arayanlar için gereklidir.
Durum kaldırma
Compose'da durum kaldırma, bir composable'ın durum bilgisiz hale getirmek için composable'ın çağrısına durumu taşıma kalıbıdır. Jetpack Compose'da durum kaldırmayla ilgili genel kalıp, durum değişkenini iki parametreyle değiştirmektir:
value: T
: Gösterilecek geçerli değeronValueChange: (T) -> Unit
: Değerin değiştirilmesini isteyen bir etkinlik. BuradaT
, önerilen yeni değerdir
Ancak onValueChange
ile sınırlı değilsiniz. Kompozit için daha spesifik etkinlikler uygunsa bunları lambda kullanarak tanımlamanız gerekir.
Bu şekilde kaldırılan durum bazı önemli özelliklere sahiptir:
- Tek doğru kaynak: Durumu kopyalamak yerine taşıyarak yalnızca tek bir doğru kaynak olmasını sağlıyoruz. Bu, hataları önlemeye yardımcı olur.
- Kapsülleme: Yalnızca durum bilgisine sahip bileşimler durumlarını değiştirebilir. Tamamen dahilidir.
- Paylaşılabilir: Kaldırılmış durum, birden fazla composable ile paylaşılabilir.
name
değerini farklı bir bileşende okumak isterseniz kaldırma işlemi bunu yapmanıza olanak tanır. - Engellenebilir: Durum bilgisi olmayan birleştirilebilir öğeleri çağıranlar, 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
'üViewModel
'e taşımak mümkün.
Bu örnekte, name
ve onValueChange
öğelerini HelloContent
öğesinin dışına çıkarıp bunları ağaçta HelloContent
adlı bir HelloScreen
composable'a 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") }) } }
Durumu HelloContent
'ten kaldırarak, birleştirilebilir öğe hakkında mantık yürütmek, farklı durumlarda yeniden kullanmak ve test etmek daha kolaydır. HelloContent
, durumunun depolanma şeklinden ayrılır. Ayrıştırma, HelloScreen
öğesini değiştirmeniz veya değiştirmeniz durumunda HelloContent
öğesinin uygulanma şeklini değiştirmeniz gerekmeyeceği anlamına gelir.
Durumun azaldığı ve etkinliklerin arttığı kalıba tek yönlü veri akışı denir. Bu durumda durum HelloScreen
değerinden HelloContent
değerine düşer ve etkinlikler HelloContent
değerinden HelloScreen
değerine yükselir. Tek yönlü veri akışını izleyerek, kullanıcı arayüzünde durumu gösteren bileşenleri, durumunu depolayan ve değiştiren uygulamanızın bölümlerinden ayırabilirsiniz.
Daha fazla bilgi için Eyalet bayrağını nereye çekmelisiniz? sayfasına bakın.
Oluşturma bölümünde durumu geri yükleme
rememberSaveable
API'si, kayıtlı örnek durumu mekanizmasını kullanarak yeniden oluşturma işlemlerinde ve ayrıca etkinlik veya süreç yeniden oluşturma işlemlerinde durumu koruduğu için remember
'e benzer şekilde çalışır. Örneğin, ekran döndürüldüğünde bu durumla karşılaşabilirsiniz.
Eyalet bilgisini depolamanın yolları
Bundle
öğesine eklenen tüm veri türleri otomatik olarak kaydedilir. Bundle
'e eklenemeyen bir öğeyi kaydetmek istiyorsanız birkaç seçeneğiniz vardır.
Paketleme
En basit çözüm, nesneye @Parcelize
ek açıklama eklemektir. Nesne paketlenebilir ve gruplandırılabilir hale gelir. Örneğin, bu kodda paketlenebilir bir City
veri türü oluşturulur ve duruma kaydedilir.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
Harita Koruyucu
@Parcelize
herhangi bir nedenle uygun değilse bir nesneyi sistemin Bundle
'ye kaydedebileceği bir değer kümesine dönüştürmeyle ilgili kendi kuralınızı tanımlamak için 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
Haritanın anahtarlarını tanımlama ihtiyacını ortadan kaldırmak için listSaver
'ü 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")) } }
Oluşturma bölümünde durum bilgisi depolayıcılar
Basit durum kaldırma işlemi, derlenebilir işlevlerin kendisinde yönetilebilir. Ancak, takip edilecek durum miktarı artarsa veya birleştirilebilir işlevlerde uygulanacak 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 kaldırma dokümanlarına 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ırlama 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 derlemelerden etkilenmemesini sağlar.
Genel olarak remember
, calculation
lambda parametresi alır. remember
ilk ç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.
Durumu önbelleğe almanın yanı sıra, ilk başlatılması veya hesaplanması pahalı olan herhangi bir nesneyi ya da kompozisyondaki bir işlemin sonucunu depolamak için remember
'ü de kullanabilirsiniz. Bu hesaplamayı her yeniden derlemede tekrarlamak istemeyebilirsiniz.
Örneğin, pahalı bir işlem olan bu ShaderBrush
nesnesini oluşturabilirsiniz:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
remember
, değeri kompozisyondan çıkana kadar depolar. Ancak önbelleğe alınmış değeri geçersiz kılma yöntemi vardır. remember
API ayrıca bir key
veya keys
parametresi alır. Bu anahtarlardan herhangi biri değişirse işlev bir sonraki kez yeniden derlendiğinde remember
önbelleği geçersiz kılar ve hesaplama lambda bloğunu tekrar yürütür. Bu mekanizma, kompozisyondaki bir nesnenin kullanım ömrü üzerinde kontrol sahibi olmanızı sağlar. Hesaplama, hatırlanan değer kompozisyondan çıkana 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, bir ShaderBrush
oluşturulur ve Box
kompozit öğesinin arka plan boyası olarak kullanılır. remember
, daha önce açıklandığı gibi yeniden oluşturmanın pahalı olması nedeniyle ShaderBrush
örneğini saklar. remember
, seçilen arka plan resmi olan key1
parametresi olarak avatarRes
değerini alır. avatarRes
değişirse fırça yeni resimle yeniden oluşturulur ve Box
'a yeniden uygulanır. Bu durum, kullanıcı seçiciden arka plan olacak 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 state, basit bir durum tutucu sınıfına
MyAppState
çekilir. remember
kullanarak sınıfın bir örneğini başlatmak için bir rememberMyAppState
işlevi sunar. Yeniden bestelemelerden etkilenmeyen bir örnek oluşturmak için bu tür işlevleri sunmak, Compose'da yaygın olarak kullanılan 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 gerçekleşebilir.
@Composable private fun rememberMyAppState( windowSizeClass: WindowSizeClass ): MyAppState { return remember(windowSizeClass) { MyAppState(windowSizeClass) } } @Stable class MyAppState( private val windowSizeClass: WindowSizeClass ) { /* ... */ }
Oluşturma, 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şit uygulamasını kullanır.
Yeniden oluşturmanın ötesinde anahtarlar içeren mağaza durumu
rememberSaveable
API'si, verileri Bundle
içinde saklayabilen remember
sarmalayıcısıdır. Bu API, durumun yalnızca yeniden derlemeden değil, etkinlik yeniden oluşturma ve sistem tarafından başlatılan işlem sonlandırma işlemlerinden de sağ çıkmasına olanak tanır.
rememberSaveable
, remember
'nin keys
almasıyla aynı amaçla input
parametrelerini alır. Girişlerden herhangi biri değiştiğinde önbellek geçersiz kılınır. İşlev yeniden oluşturulduktan sonra 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
Durum 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
- Oluşturma kullanıcı arayüzünüzün mimarisini oluşturma
- Oluştur'da kullanıcı arayüzü durumunu kaydetme
- Oluşturma bölümündeki yan etkiler