Eyalet sahipleri ve kullanıcı arayüzü durumu

Kullanıcı arayüzü katmanı kılavuzunda, kullanıcı arayüzü katmanı için Kullanıcı Arayüzü Durumunu üretme ve yönetmenin bir yolu olarak tek yönlü veri akışı (UDF) açıklanmaktadır.

Veri, veri katmanından kullanıcı arayüzüne tek yönlü olarak aktarılır.
Şekil 1: Tek yönlü veri akışı

Ayrıca, UDF yönetiminin delege olarak eyalet sahibi adı verilen özel bir sınıfa delege etmenin avantajlarını vurgular. Bir durum tutucuyu ViewModel veya düz bir sınıf aracılığıyla uygulayabilirsiniz. Bu belgede, devlet sahipleri ve kullanıcı arayüzü katmanında oynadıkları role yakından bakalım.

Bu belgenin sonunda, kullanıcı arayüzü katmanında uygulama durumunu nasıl yöneteceğinizi anlamış olmalısınız. Bu, kullanıcı arayüzü durumu üretim ardışık düzenidir. Aşağıdakileri anlayabilmeniz ve bilmeniz gerekir:

  • Kullanıcı arayüzü katmanındaki kullanıcı arayüzü durumu türlerini anlayın.
  • Kullanıcı arayüzü katmanındaki bu kullanıcı arayüzü durumları üzerinde çalışan mantık türlerini anlayın.
  • ViewModel veya basit sınıf gibi bir eyalet sahibinin uygun uygulamasını nasıl seçeceğinizi öğrenin.

Kullanıcı arayüzü durumu üretim ardışık düzeninin öğeleri

Kullanıcı arayüzü durumu ve bunu oluşturan mantık, kullanıcı arayüzü katmanını tanımlar.

Kullanıcı arayüzü durumu

Kullanıcı arayüzü durumu, kullanıcı arayüzünü açıklayan özelliktir. İki tür kullanıcı arayüzü durumu vardır:

  • Ekran kullanıcı arayüzü durumu, ekranda görüntülemeniz gereken ifadedir. Örneğin, NewsUiState sınıfı, kullanıcı arayüzünün oluşturulması için gereken haber makalelerini ve diğer bilgileri içerebilir. Bu durum, uygulama verileri içerdiğinden genellikle hiyerarşinin diğer katmanlarıyla bağlantılıdır.
  • Kullanıcı arayüzü öğesi durumu, oluşturulma şeklini etkileyen kullanıcı arayüzü öğelerinin yapısında olan özellikleri ifade eder. Bir kullanıcı arayüzü öğesi gösterilebilir veya gizlenebilir ve belirli bir yazı tipi, yazı tipi boyutu veya yazı tipi rengine sahip olabilir. Doğası gereği durum bilgili olduğundan, Android Görünümleri'nde durumu değiştirmek veya sorgulamak için yöntem sunan Görünüm ise bu durumu yönetir. Buna örnek olarak, metni için TextView sınıfının get ve set yöntemleri verilebilir. JetpackCompose'da durum, composable'ın dışındadır ve hatta composable'ın yakın çevresinden çağrıda bulunan composable işlevine veya durum sahibine de kaldırılabilir. Scaffold composable için ScaffoldState bu şekilde bir örnektir.

Mantık

Uygulama verileri ve kullanıcı etkinlikleri, kullanıcı arayüzü durumunun zamanla değişmesine neden olduğundan kullanıcı arayüzü durumu statik bir özellik değildir. Mantık, değişiklik ayrıntılarını (kullanıcı arayüzü durumunun hangi bölümlerinin değiştiği, neden değiştiği ve ne zaman değişmesi gerektiği dahil) belirler.

Mantık, kullanıcı arayüzü durumu üretir
Şekil 2: Kullanıcı arayüzü durumu üreticisi olarak mantık

Bir uygulamadaki mantık, iş mantığı veya kullanıcı arayüzü mantığı olabilir:

  • İş mantığı, uygulama verilerine yönelik ürün şartlarının uygulanmasıdır. Örneğin, kullanıcı düğmeye dokunduğunda haber okuyucu uygulamasında makaleye yer işareti koyma. Bir yer işaretini bir dosyaya veya veritabanına kaydetmeye yönelik bu mantık genellikle alana ya da veri katmanlarına yerleştirilir. Eyalet sahibi genellikle ortaya koyduğu yöntemleri çağırarak bu mantığı bu katmanlara delege eder.
  • Kullanıcı arayüzü mantığı, ekranda kullanıcı arayüzü durumunun nasıl gösterileceğiyle ilgilidir. Örneğin, kullanıcı bir kategori seçtiğinde doğru arama çubuğu ipucunu almak, listedeki belirli bir öğeye gitmek veya kullanıcı bir düğmeyi tıkladığında belirli bir ekrana gitmek için gezinme mantığını kullanmak.

Android yaşam döngüsü ile kullanıcı arayüzü durumu ve mantığı türleri

Kullanıcı arayüzü katmanı, biri bağımlı, diğeri kullanıcı arayüzü yaşam döngüsünden bağımsız olmak üzere iki bölümden oluşur. Bu ayırma, her bir bölümde kullanılabilecek veri kaynaklarını belirler ve bu nedenle farklı kullanıcı arayüzü durumu ve mantığı türleri gerektirir.

  • Kullanıcı arayüzü yaşam döngüsünden bağımsız: Kullanıcı arayüzü katmanının bu bölümü, uygulamanın veri üreten katmanlarıyla (veri veya alan katmanları) ele alınır ve iş mantığıyla tanımlanır. Yaşam döngüsü, yapılandırma değişiklikleri ve kullanıcı arayüzünde Activity yeniden oluşturma işlemi, kullanıcı arayüzü durumu üretim ardışık düzeninin etkin olup olmadığını etkileyebilir ancak üretilen verilerin geçerliliğini etkilemez.
  • Kullanıcı arayüzü yaşam döngüsüne bağlıdır: Kullanıcı arayüzü katmanının bu bölümü, kullanıcı arayüzü mantığıyla ilgilidir ve yaşam döngüsünden veya yapılandırma değişikliklerinden doğrudan etkilenir. Bu değişiklikler, içinde okunan veri kaynaklarının geçerliliğini doğrudan etkiler. Sonuç olarak, durum yalnızca yaşam döngüsü etkin olduğunda değişebilir. Çalışma zamanı izinleri ve yerelleştirilmiş dizeler gibi yapılandırmaya bağlı kaynakları alma, bu tür içeriklere örnek olarak verilebilir.

Yukarıdakiler, aşağıdaki tabloyla özetlenebilir:

Kullanıcı arayüzü yaşam döngüsünden bağımsız Kullanıcı Arayüzü Yaşam Döngüsü'ne bağlı
İş mantığı Kullanıcı Arayüzü Mantığı
Ekran kullanıcı arayüzü durumu

Kullanıcı arayüzü durumu üretim ardışık düzeni

Kullanıcı arayüzü durumu üretim ardışık düzeni, kullanıcı arayüzü durumu üretmek için atılan adımları ifade eder. Bu adımlar, daha önce tanımlanan mantık türlerinin uygulanmasını kapsar ve tamamen kullanıcı arayüzünüzün ihtiyaçlarına bağlıdır. Bazı kullanıcı arayüzleri, ardışık düzenin hem UI Yaşam Döngüsü'nden bağımsız hem de Kullanıcı Arayüzü Yaşam Döngüsü'ne bağlı bölümlerin her ikisinden de yararlanabilir veya hiçbirini kullanmayabilir.

Yani kullanıcı arayüzü katmanı ardışık düzeninin aşağıdaki permütasyonları geçerlidir:

  • Kullanıcı arayüzü durumu, kullanıcı arayüzü tarafından oluşturulur ve yönetilir. Örneğin basit ve yeniden kullanılabilir bir temel sayaç:

    @Composable
    fun Counter() {
        // The UI state is managed by the UI itself
        var count by remember { mutableStateOf(0) }
        Row {
            Button(onClick = { ++count }) {
                Text(text = "Increment")
            }
            Button(onClick = { --count }) {
                Text(text = "Decrement")
            }
        }
    }
    
  • Kullanıcı arayüzü mantığı → Kullanıcı arayüzü. Örneğin, kullanıcının listenin başına atlamasına olanak tanıyan bir düğmeyi gösterme veya gizleme.

    @Composable
    fun ContactsList(contacts: List<Contact>) {
        val listState = rememberLazyListState()
        val isAtTopOfList by remember {
            derivedStateOf {
                listState.firstVisibleItemIndex < 3
            }
        }
    
        // Create the LazyColumn with the lazyListState
        ...
    
        // Show or hide the button (UI logic) based on the list scroll position
        AnimatedVisibility(visible = !isAtTopOfList) {
            ScrollToTopButton()
        }
    }
    
  • İş mantığı → Kullanıcı arayüzü. Ekranda geçerli kullanıcının fotoğrafını gösteren bir kullanıcı arayüzü öğesi.

    @Composable
    fun UserProfileScreen(viewModel: UserProfileViewModel = hiltViewModel()) {
        // Read screen UI state from the business logic state holder
        val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
        // Call on the UserAvatar Composable to display the photo
        UserAvatar(picture = uiState.profilePicture)
    }
    
  • İş mantığı → Kullanıcı arayüzü mantığı → Kullanıcı arayüzü. Belirli bir kullanıcı arayüzü durumu için ekranda doğru bilgileri görüntülemek üzere kaydırılan kullanıcı arayüzü öğesi.

    @Composable
    fun ContactsList(viewModel: ContactsViewModel = hiltViewModel()) {
        // Read screen UI state from the business logic state holder
        val uiState by viewModel.uiState.collectAsStateWithLifecycle()
        val contacts = uiState.contacts
        val deepLinkedContact = uiState.deepLinkedContact
    
        val listState = rememberLazyListState()
    
        // Create the LazyColumn with the lazyListState
        ...
    
        // Perform UI logic that depends on information from business logic
        if (deepLinkedContact != null && contacts.isNotEmpty()) {
            LaunchedEffect(listState, deepLinkedContact, contacts) {
                val deepLinkedContactIndex = contacts.indexOf(deepLinkedContact)
                if (deepLinkedContactIndex >= 0) {
                  // Scroll to deep linked item
                  listState.animateScrollToItem(deepLinkedContactIndex)
                }
            }
        }
    }
    

Kullanıcı arayüzü durumu üretim ardışık düzenine her iki mantığın da uygulandığı durumlarda, kullanıcı arayüzü mantığından önce her zaman iş mantığı uygulanmalıdır. Kullanıcı arayüzü mantığından sonra iş mantığını uygulamaya çalışmak, iş mantığının kullanıcı arayüzü mantığına bağlı olduğu anlamına gelir. Aşağıdaki bölümlerde, farklı mantık türlerine ve bunların durum sahiplerine ayrıntılı bir bakış üzerinden bunun neden bir sorun olduğu ele alınmaktadır.

Veri, veri üretme katmanından kullanıcı arayüzüne aktarılır
Şekil 3: Kullanıcı arayüzü katmanında mantığın uygulanması

Devlet sahipleri ve sorumlulukları

Eyalet sahiplerinin sorumluluğu, uygulamanın okuyabilmesi için durumu depolamaktır. Mantığın gerekli olduğu durumlarda, bir aracı işlevi görür ve gerekli mantığı barındıran veri kaynaklarına erişim sağlar. Bu şekilde eyalet sahibi, mantığı uygun veri kaynağına delege eder.

Bunun sonucunda da şu faydalar elde edilir:

  • Basit kullanıcı arayüzleri: Kullanıcı arayüzü yalnızca durumunu bağlar.
  • Sürdürülebilirlik: Durum tutucuda tanımlanan mantık, kullanıcı arayüzünün kendisi değiştirilmeden tekrarlanabilir.
  • Test edilebilirlik: Kullanıcı arayüzü ve durum üretim mantığı bağımsız olarak test edilebilir.
  • Okunabilirlik: Kodun okuyucuları, kullanıcı arayüzü sunu kodu ile kullanıcı arayüzü durumu üretim kodu arasındaki farklılıkları net bir şekilde görebilir.

Boyutu veya kapsamı ne olursa olsun her kullanıcı arayüzü öğesinin, karşılık gelen durum sahibiyle bire bir ilişkisi vardır. Ayrıca, bir durum sahibi, kullanıcı arayüzü durumu değişikliğiyle sonuçlanabilecek herhangi bir kullanıcı işlemini kabul edip işleyebilmeli ve sonrasında gelen durum değişikliğini gerçekleştirebilmelidir.

Eyalet sahiplerinin türleri

Kullanıcı arayüzü durumu ve mantığı türlerine benzer şekilde, kullanıcı arayüzü katmanında kullanıcı arayüzü yaşam döngüsüyle ilişkilerine göre tanımlanan iki tür durum sahibi vardır:

  • İş mantığı durum tutucusu.
  • Kullanıcı arayüzü mantığı durum tutucusu.

Aşağıdaki bölümlerde, iş mantığı durumu sahibinden başlayarak devlet sahiplerinin türlerine daha yakından bakılmaktadır.

İş mantığı ve durum sahibi

İş mantığı durumu sahipleri, kullanıcı etkinliklerini işler ve verileri veri veya alan katmanlarından ekran kullanıcı arayüzü durumuna dönüştürür. Android yaşam döngüsü ve uygulama yapılandırması değişikliklerini değerlendirirken en iyi kullanıcı deneyimini sunmak için iş mantığından yararlanan devlet sahiplerinin aşağıdaki özelliklere sahip olması gerekir:

Özellik Ayrıntı
Kullanıcı Arayüzü Durumu oluşturur İş mantığı durumu sahipleri, kullanıcı arayüzleri için kullanıcı arayüzü durumunu oluşturmaktan sorumludur. Bu kullanıcı arayüzü durumu, genellikle kullanıcı etkinliklerinin işlenmesinin ve alan ile veri katmanlarından gelen verilerin okunmasının bir sonucudur.
Rekreasyon aktivitesiyle elde tutulan içerik İş mantığı durumu sahipleri, Activity yeniden oluşturma işleminde durum ve durum işleme ardışık düzenlerini koruyarak sorunsuz bir kullanıcı deneyimi sunmaya yardımcı olur. Devlet sahibinin tutulamadığı ve yeniden oluşturulduğu (genellikle sürecin ölümünden sonra) durumlarda, devlet sahibinin tutarlı bir kullanıcı deneyimi sağlamak için son durumunu kolayca yeniden oluşturabilmesi gerekir.
Uzun ömürlü olma İş mantığı durum sahipleri genellikle navigasyon hedeflerinin durumunu yönetmek için kullanılır. Bunun sonucunda, genellikle gezinme grafiğinden kaldırılana kadar gezinme değişikliklerindeki durumlarını korurlar.
Kullanıcı arayüzüne özeldir ve yeniden kullanılamaz İş mantığı durumu sahipleri, genellikle TaskEditViewModel veya TaskListViewModel gibi belirli bir uygulama işlevi için durum oluşturur ve dolayısıyla yalnızca bu uygulama işlevinde geçerlidir. Aynı eyalet sahibi, farklı form faktörlerinde bu uygulama işlevlerini destekleyebilir. Örneğin, uygulamanın mobil, TV ve tablet sürümleri aynı iş mantığı durumu tutucusunu yeniden kullanabilir.

Örneğin, "Now in Android" uygulamasındaki yazar gezinme hedefini düşünün:

Now in Android uygulaması, önemli bir uygulama işlevini temsil eden gezinme hedefinin kendi benzersiz iş mantığı durum sahibine nasıl sahip olması gerektiğini gösterir.
Şekil 4: Now in Android uygulaması

İş mantığı durum tutucusu olarak AuthorViewModel, bu durumda kullanıcı arayüzü durumunu oluşturur:

@HiltViewModel
class AuthorViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val authorsRepository: AuthorsRepository,
    newsRepository: NewsRepository
) : ViewModel() {

    val uiState: StateFlow<AuthorScreenUiState> = …

    // Business logic
    fun followAuthor(followed: Boolean) {
      …
    }
}

AuthorViewModel öğesinin, daha önce belirtilen özelliklere sahip olduğuna dikkat edin:

Özellik Ayrıntı
AuthorScreenUiState üretir AuthorViewModel, AuthorsRepository ve NewsRepository kaynaklı verileri okur ve bu verileri kullanarak AuthorScreenUiState üretir. Kullanıcı, AuthorsRepository adlı kullanıcıya yetki vererek Author sayfasını takip etmek veya takibi bırakmak istediğinde de iş mantığına uyar.
Veri katmanına erişimi var AuthorsRepository ve NewsRepository örnekleri, oluşturucusunda buna iletilir. Bu sayede, Author aşağıdaki iş mantığını uygulayabilir.
Activity yeniden etkinlikten sonra hayatta kalır ViewModel ile uygulandığından hızlı Activity yeniden oluşturma işleminde tutulur. İşlemin ölümü durumunda, veri katmanından kullanıcı arayüzü durumunu geri yüklemek için gereken minimum miktarda bilginin sağlanması amacıyla SavedStateHandle nesnesi okunabilir.
Uzun ömürlü bir yaşam sürdürüyor ViewModel, gezinme grafiğine dahil edilir. Bu nedenle, yazar hedefi gezinme grafiğinden kaldırılmadığı sürece uiState StateFlow içindeki kullanıcı arayüzü durumu bellekte kalır. Durum yalnızca kullanıcı arayüzü durumunun toplayıcısı varsa üretildiğinden, StateFlow kullanılması, durum tembelliğine neden olan iş mantığının uygulanmasını da sağlar.
Kullanıcı arayüzüne özgü olmalıdır AuthorViewModel yalnızca yazar gezinme hedefi için geçerlidir ve başka bir yerde tekrar kullanılamaz. Gezinme hedeflerinde yeniden kullanılan herhangi bir iş mantığı varsa bu iş mantığı, veri veya alan katmanı kapsamlı bir bileşene dahil edilmelidir.

İş mantığı durum sahibi olarak ViewModel

Android geliştirmede ViewModels'in avantajları, bunları iş mantığına erişim sağlamak ve uygulama verilerini ekranda sunum için hazırlamak üzere uygun hale getirir. Bu avantajlar arasında şunlar yer alır:

  • ViewModelleri tarafından tetiklenen işlemler, yapılandırma değişikliklerinden sonra da yürürlükte kalır.
  • Gezinme ile entegrasyon:
    • Gezinme, ekran arka yığındayken ViewModel'leri önbelleğe alır. Önceden yüklediğiniz verilerin, hedefinize döndüğünüzde anında kullanılabilir olması açısından bu önemlidir. Bu işlemi, composable ekranın yaşam döngüsünü takip eden bir durum sahibiyle yapmak daha zordur.
    • ViewModel, hedef arka yığından çıkarıldığında da temizlenir. Böylece, durumunuz otomatik olarak temizlenir. Bu, yapılandırma değişikliğinden veya başka sebeplerden dolayı yeni ekrana gitme gibi birden fazla sebeple gerçekleşebilecek composable'ın atılmasını dinlemekten farklıdır.
  • Hilt gibi diğer Jetpack kitaplıklarıyla entegrasyon.

Kullanıcı arayüzü mantığı ve durum tutucu

Kullanıcı arayüzü mantığı, kullanıcı arayüzünün sağladığı veriler üzerinde çalışan bir mantıktır. Bu işlem, kullanıcı arayüzü öğelerinin durumunda veya allow API ya da Resources gibi kullanıcı arayüzü veri kaynaklarında olabilir. Kullanıcı arayüzü mantığından yararlanan devlet sahipleri genellikle aşağıdaki özelliklere sahiptir:

  • Kullanıcı arayüzü durumunu oluşturur ve kullanıcı arayüzü öğelerinin durumunu yönetir.
  • Activity yeniden oluşturmasından sonra devam etmez: Kullanıcı arayüzü mantığında barındırılan durum sahipleri, genellikle kullanıcı arayüzünün kendisinden veri kaynaklarına bağlıdır ve yapılandırma değişiklikleri genelinde bu bilgileri saklamaya çalışmak, bellek sızıntısına neden olmamasına kıyasla daha sık karşılaşılan bir durumdur. Devlet sahiplerinin yapılandırma değişiklikleri sırasında verilere sahip olmaları gerekiyorsa, Activityrekreasyon ortamında hayatta kalmaya daha uygun olan başka bir bileşene yetki vermeleri gerekir. Örneğin Jetpack Compose'da remembered işlevleriyle oluşturulan Oluşturulabilir kullanıcı arayüzü öğesi durumları, Activity yeniden oluşturmalarında durumu korumak için genellikle rememberSaveable öğesine yetki verir. Bu tür işlevlere örnek olarak rememberScaffoldState() ve rememberLazyListState() verilebilir.
  • Kullanıcı arayüzü kapsamlı veri kaynaklarına referanslar içeriyor: Kullanıcı arayüzü mantık durumu tutucusu, kullanıcı arayüzüyle aynı yaşam döngüsüne sahip olduğundan, yaşam döngüsü API'leri ve Kaynakları gibi veri kaynaklarına güvenli bir şekilde başvurulabilir ve okunabilir.
  • Birden fazla kullanıcı arayüzünde yeniden kullanılabilir olması: Aynı kullanıcı arayüzü mantık durumu tutucusunun farklı örnekleri, uygulamanın farklı bölümlerinde yeniden kullanılabilir. Örneğin, bir çip grubunun kullanıcı girişi etkinliklerini yönetmek için kullanılan durum tutucu, filtre çipleri için arama sayfasında ve e-postanın alıcıları için "kime" alanında kullanılabilir.

Kullanıcı arayüzü mantık durumu tutucusu genellikle düz bir sınıfla uygulanır. Bunun nedeni, kullanıcı arayüzü mantığı durumu tutucusunun oluşturulmasından kullanıcı arayüzünün sorumlu olması ve kullanıcı arayüzü mantığı durumu tutucusunun, kullanıcı arayüzüyle aynı yaşam döngüsüne sahip olmasıdır. Örneğin Jetpack Compose'da durum sahibi Beste'nin bir parçasıdır ve Beste'nin yaşam döngüsünü takip eder.

Bu, yukarıdaki Now in Android örneğinde bulunan aşağıdaki örnekte açıklanabilir:

Artık Android&#39;de, kullanıcı arayüzü mantığını yönetmek için düz bir sınıf durumu tutucu kullanılır.
Şekil 5: Android'de Now örnek uygulaması

Android'de Now örneği, cihazın ekran boyutuna bağlı olarak, gezinme için bir alt uygulama çubuğunu veya bir gezinme çubuğunu gösterir. Küçük ekranlarda alt uygulama çubuğu, daha büyük ekranlarda ise gezinme çubuğu kullanılır.

NiaApp composable işlevinde kullanılan uygun gezinme kullanıcı arayüzü öğesine karar verme mantığı iş mantığına bağlı olmadığından, NiaAppState adlı bir düz sınıf durumu tutucusu tarafından yönetilebilir:

@Stable
class NiaAppState(
    val navController: NavHostController,
    val windowSizeClass: WindowSizeClass
) {

    // UI logic
    val shouldShowBottomBar: Boolean
        get() = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact ||
            windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact

    // UI logic
    val shouldShowNavRail: Boolean
        get() = !shouldShowBottomBar

   // UI State
    val currentDestination: NavDestination?
        @Composable get() = navController
            .currentBackStackEntryAsState().value?.destination

    // UI logic
    fun navigate(destination: NiaNavigationDestination, route: String? = null) { /* ... */ }

     /* ... */
}

Yukarıdaki örnekte, NiaAppState ile ilgili aşağıdaki ayrıntılar dikkat çekicidir:

  • Activity yeniden oluşturmada hayatta kalamaz: NiaAppState, Oluştur adlandırma kurallarına göre bir Oluşturulabilir işlevi rememberNiaAppState kullanılarak bestede remembered olarak yer alıyor. Activity yeniden oluşturulduktan sonra önceki örnek kaybedilir ve yeniden oluşturulan Activity öğesinin yeni yapılandırmasına uygun, tüm bağımlılıkları iletilmiş yeni bir örnek oluşturulur. Bu bağımlılıklar yeni veya önceki yapılandırmadan geri yüklenebilir. Örneğin rememberNavController(), NiaAppState oluşturucuda kullanılır ve Activity yeniden oluşturma genelinde durumu korumak için rememberSaveable adlı kullanıcıya yetki verir.
  • Kullanıcı arayüzü kapsamlı veri kaynaklarına referanslar içeriyor: Aynı yaşam döngüsü kapsamını paylaştıkları için navigationController, Resources ve diğer benzer yaşam döngüsü kapsamlı türlere yapılan referanslar NiaAppState içinde güvenle tutulabilir.

Durum sahibi için ViewModel ve düz sınıf arasında seçim yapma

Yukarıdaki bölümlerde, ViewModel ile düz sınıf durum tutucu arasında seçim yapmak, kullanıcı arayüzü durumuna uygulanan mantık ve mantığın üzerinde çalıştığı veri kaynakları ile ilgilidir.

Özet olarak aşağıdaki şemada, kullanıcı arayüzü durumu üretim hattındaki durum sahiplerinin konumu gösterilmektedir:

Veri üretme katmanından kullanıcı arayüzü katmanına veri akışı
Şekil 6: Kullanıcı Arayüzü Durumu üretim hattındaki durum sahipleri. Oklar, veri akışını ifade eder.

Sonuç olarak, kullanıcı arayüzü durumunu, tüketildiği yere en yakın durum sahiplerini kullanarak oluşturmanız gerekir. Daha resmî olmayan bir biçimde, durumu mümkün olduğunca düşük tutmalı ve uygun sahipliği korumalısınız. İş mantığına erişmeniz ve Activity yeniden oluşturma sürecinde bile bir ekrana geçilebildiği sürece kullanıcı arayüzü durumunun devam etmesi gerekiyorsa ViewModel, iş mantığı durumu tutucu uygulamanız için ideal bir seçenektir. Daha kısa ömürlü kullanıcı arayüzü durumu ve kullanıcı arayüzü mantığı için yaşam döngüsü yalnızca kullanıcı arayüzüne bağlı olan sade bir sınıf yeterli olacaktır.

Eyalet sahipleri birleştirilebilir

Bağımlılıkların ömürleri eşit veya daha kısa olduğu sürece devlet sahipleri, diğer devlet sahiplerine güvenebilirler. Bu tür içeriğe ilişkin bazı örnekler:

  • bir kullanıcı arayüzü mantık durumu tutucusu başka bir kullanıcı arayüzü mantık durumu sahibine bağlı olabilir.
  • ekran düzeyi durum tutucusu, bir kullanıcı arayüzü mantık durumu sahibine bağlı olabilir.

Aşağıdaki kod snippet'i, Compose'un DrawerState öğesinin başka bir dahili durum sahibine (SwipeableState) nasıl bağlı olduğunu ve bir uygulamanın kullanıcı arayüzü mantık durumu sahibinin DrawerState öğesine nasıl bağlı olabileceğini göstermektedir:

@Stable
class DrawerState(/* ... */) {
  internal val swipeableState = SwipeableState(/* ... */)
  // ...
}

@Stable
class MyAppState(
  private val drawerState: DrawerState,
  private val navController: NavHostController
) { /* ... */ }

@Composable
fun rememberMyAppState(
  drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
  navController: NavHostController = rememberNavController()
): MyAppState = remember(drawerState, navController) {
  MyAppState(drawerState, navController)
}

Bir durum tutucunun geçerlilik süresi sona eren bağımlılıklara örnek olarak ekran düzeyi durum sahibine bağlı bir kullanıcı arayüzü mantık durumu tutucusu verilebilir. Bu, daha kısa ömürlü durum sahibinin tekrar kullanılabilirliğini azaltıp gerçekte gerektirdiğinden daha fazla mantık ve duruma erişmesini sağlar.

Daha kısa ömürlü durum sahibi, daha yüksek kapsamlı bir durum sahibinden gelen belirli bilgilere ihtiyaç duyuyorsa durum sahibi örneğini iletmek yerine, yalnızca ihtiyaç duyduğu bilgileri parametre olarak iletin. Örneğin, aşağıdaki kod snippet'inde kullanıcı arayüzü mantık durum tutucu sınıfı, ViewModel örneğinin tamamını bağımlılık olarak iletmek yerine, tam olarak ihtiyacı olan şeyi ViewModel'den parametre olarak alır.

class MyScreenViewModel(/* ... */) {
  val uiState: StateFlow<MyScreenUiState> = /* ... */
  fun doSomething() { /* ... */ }
  fun doAnotherThing() { /* ... */ }
  // ...
}

@Stable
class MyScreenState(
  // DO NOT pass a ViewModel instance to a plain state holder class
  // private val viewModel: MyScreenViewModel,

  // Instead, pass only what it needs as a dependency
  private val someState: StateFlow<SomeState>,
  private val doSomething: () -> Unit,

  // Other UI-scoped types
  private val scaffoldState: ScaffoldState
) {
  /* ... */
}

@Composable
fun rememberMyScreenState(
  someState: StateFlow<SomeState>,
  doSomething: () -> Unit,
  scaffoldState: ScaffoldState = rememberScaffoldState()
): MyScreenState = remember(someState, doSomething, scaffoldState) {
  MyScreenState(someState, doSomething, scaffoldState)
}

@Composable
fun MyScreen(
  modifier: Modifier = Modifier,
  viewModel: MyScreenViewModel = viewModel(),
  state: MyScreenState = rememberMyScreenState(
    someState = viewModel.uiState.map { it.toSomeState() },
    doSomething = viewModel::doSomething
  ),
  // ...
) {
  /* ... */
}

Aşağıdaki şemada, kullanıcı arayüzü ile önceki kod snippet'inin farklı durum sahipleri arasındaki bağımlılıklar gösterilmektedir:

Hem kullanıcı arayüzü mantık durumu sahibine hem de ekran düzeyi durum sahibine bağlı olarak kullanıcı arayüzü
Şekil 7: Farklı durum sahiplerine bağlı olarak kullanıcı arayüzü. Oklar bağımlılıkları ifade eder.

Numuneler

Aşağıdaki Google örnekleri, kullanıcı arayüzü katmanında durum sahiplerinin kullanımını göstermektedir. Uygulamadaki bu rehberliği görmek için bu yöntemleri inceleyin: