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ü durumunun temsilidir. Bir durum 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. 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
'ın kendisini güncellememesidir. TextField
, 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 Kompozisyonda 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
temsilci söz dizimi aşağıdaki içe aktarma işlemlerini gerektirir:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
Hatırlanan değeri, diğer derlenebilirler için parametre olarak veya hangi derlenebilirlerin gösterileceğini değiştirmek üzere 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
Oluşturma, 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, 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şturState
işlevinden yayınlanan en son değeri temsil eder. Android uygulamalarında akışları toplamanın önerilen yolu 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 gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.7.5")
}
Groovy
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 gereklidir:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.7.5")
}
Groovy
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.7.5"
}
Durum bilgisiz ve durum bilgili
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 bileşeni durum bilgisiz hale getirmek için durumu bileşenin arayanına 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 mevcut 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: Yükseltilmiş durum, birden fazla kompozisyonla 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.
- Bağımsız: Durum bilgisi olmayan bileşenlerin durumu herhangi bir yerde depolanabilir. Örneğin, artık
name
'üViewModel
'e taşımak mümkün.
Örnek durumda, name
ve onValueChange
öğelerini HelloContent
'den ayıklayıp ağaçta HelloContent
'yi çağıran bir HelloScreen
bileşenine 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
'ü değiştirirseniz veya değiştirirseniz HelloContent
'un uygulanma şeklini değiştirmeniz gerekmediğ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, 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.
Durumu depolama yöntemleri
Bundle
'e eklenen tüm veri türleri otomatik olarak kaydedilir. Bundle
'e eklenemeyen bir öğeyi kaydetmek istiyorsanız birkaç seçeneğiniz vardır.
Parçalara ayırma
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")) } }
MapSaver
@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şturma:
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'si 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 süresi ü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 işleyiş şekli 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çili 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ı 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 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 sağlar. Yeniden derlemelerden etkilenmeyen bir örnek oluşturmak için bu tür işlevleri göstermek, Oluştur'da yaygın bir modeldir. 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. Örneğin, kullanıcı cihazı döndürdüğünde bu durum ortaya çıkabilir.
@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 derlemenin ötesinde anahtarlarla durumu depolama
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 bir sonraki sefer yeniden derlendiğinde 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