Eyalet nereye kaldırılır?

Kullanıcı arayüzü durumu eklediğiniz Compose uygulamasında, kullanıcı arayüzünün Mantık veya iş mantığı için bunu gerektirir. Bu belgede şu iki ana konu ele alınmıştır: senaryoları ele alacağız.

En iyi uygulama

Kullanıcı arayüzü durumunu tüm kaynaklar arasındaki en düşük ortak üst öğeye composable'dan oluşuyor. Bulunduğunuz yere en yakın eyaleti korumanız gerekir emin olmanız gerekir. Devlet sahibinden, tüketicilere değiştirilemez durum ve etkinlikler gösterilir tıklayın.

En düşük ortak üst de Beste'nin dışında olabilir. Örneğin, durum ViewModel kaldırıldığında, iş mantığı önemlidir.

Bu sayfada, bu en iyi uygulama ayrıntılı olarak açıklanmakta ve dikkat edilmesi gereken bir uyarı bulunmaktadır.

Kullanıcı arayüzü durumu ve kullanıcı arayüzü mantığı türleri

Aşağıda, kullanılan kullanıcı arayüzü durumu ve mantık türlerinin tanımları verilmiştir bu belgenin tamamından bahsedeceğim.

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ü vardır eyalet:

  • Ekran kullanıcı arayüzü durumu, ekranda görüntülemeniz gereken ifadedir. Örneğin, NewsUiState sınıfı, gerekli haber makalelerini ve diğer bilgileri içerebilir oluşturmak istiyorum. Bu durum genellikle öğenin diğer katmanlarıyla uygulama verileri içerdiğinden hiyerarşik olarak yapılandırılmıştır.
  • Kullanıcı arayüzü öğesi durumu, öğelerin dışında kalan kullanıcı arayüzü öğelerinin nasıl oluşturulduğunu etkiler. Kullanıcı arayüzü öğeleri gösterilebilir veya gizlenebilir. yazı tipi, yazı tipi boyutu veya yazı tipi rengine sahip olduğundan emin olun. Android Görünümlerinde, Görünüm doğası gereği durum bilgili olduğu için bu durumu yönetir. Son olarak da veya sorgulayabilir. get ve metni için TextView sınıfının set yöntemlerini. Jetpack'te Oluşturma, durum composable'ın dışındadır ve hatta kaldırılabilir composable'ın yakın çevresinden çağrıda bulunan composable'a bir işlev veya durum tutucuyu ifade eder. Şu konum için ScaffoldState buna bir örnektir: Scaffold composable.

Mantık

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

  • İş mantığı, uygulama için ürün şartlarının uygulanmasıdır. dışı verilerdir. Örneğin, kullanıcı haber okuyucu uygulamasındaki bir makaleye yer işareti koyduğunda düğmeye dokunur. Yer işaretini bir dosyaya veya veritabanına kaydetmek için bu mantık genellikle alan adına veya veri katmanlarına yerleştirilir. Eyalet sahibi genellikle gösterdikleri 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, Örneğin, kullanıcı bir seçim yaptığında doğru arama çubuğu ipucunu liste içinde belirli bir öğeye gitmek için kaydırma mantığı veya gezinme mantığı Kullanıcı bir düğmeyi tıkladığında belirli bir ekrana yönlendirilir.

Kullanıcı arayüzü mantığı

Kullanıcı arayüzü mantığının durum okuması veya yazması gerektiğinde, durumu kullanıcı arayüzüne takip ederek kapsam belirlemelisiniz. Bunu başarmak için durumu, composable bir işlevde doğru düzeyde kaldırmanız gerekir. Alternatif olarak bunu, kullanıcı arayüzü yaşam döngüsü kapsamındaki bir düz durum tutucu sınıfında da yapın.

Aşağıda hem çözümler hem de ne zaman kullanılacağına dair açıklama yer almaktadır.

Eyalet sahibi olarak bestelenebilirler

composable'larda kullanıcı arayüzü mantığının ve kullanıcı arayüzü öğesi durumunun olması, çok basittir. Durumunuzu dahili olarak bir composable veya kaldırın.

Eyalet kaldırma gerekmez

Kaldırma durumu her zaman gerekli değildir. Durum, bir composable içinde dahili tutulabilir bu özelliği kullanabilirsiniz. Bu snippet'te, dokunarak genişletilen veya daraltılan bir composable var:

@Composable
fun ChatBubble(
    message: Message
) {
    var showDetails by rememberSaveable { mutableStateOf(false) } // Define the UI element expanded state

    ClickableText(
        text = AnnotatedString(message.content),
        onClick = { showDetails = !showDetails } // Apply simple UI logic
    )

    if (showDetails) {
        Text(message.timestamp)
    }
}

showDetails değişkeni, bu kullanıcı arayüzü öğesinin dahili durumudur. Yalnızca ve ona uygulanan mantık çok basit. Bu durumda devlete hizmet etmek, pek bir fayda sağlamaz. Dolayısıyla dahili olarak bırakabiliriz. Bu sayede, bu composable, sahibi ve tek başına genişletilmiş durumun veri kaynağıdır.

composable'ların içine kaldırma

Kullanıcı arayüzü öğenizin durumunu diğer composable'larla paylaşmanız ve kullanıcı arayüzü uygulamanız gerekiyorsa bunu kullanıcı arayüzü hiyerarşisinde daha yukarılara çıkarabilirsiniz. Bu sayede composable'larınızı daha fazla yeniden kullanabilir ve daha kolay test edebilirsiniz.

Aşağıdaki örnek, iki farklı işlev sunan bir sohbet uygulamasıdır:

  • JumpToBottom düğmesi, ileti listesini en alta kaydırır. Düğme liste durumunda kullanıcı arayüzü mantığı gerçekleştirir.
  • Kullanıcı yeni bir ileti gönderdikten sonra MessagesList listesi en alta gider mesaj. UserInput, liste durumunda kullanıcı arayüzü mantığı gerçekleştirir.
ziyaret edin.
JumpToBottom düğmesi olan sohbet uygulaması ve yeni mesajlarda ekranı en alta kaydır
Şekil 1. JumpToBottom düğmesi bulunan Chat uygulamasında yeni mesajlarda ekranı aşağı kaydırın

Oluşturulabilir hiyerarşi aşağıdaki gibidir:

Sohbet composable ağaç
Şekil 2. Chat'te composable ağaç
'nı inceleyin.

Uygulamanın şunları yapabilmesi için LazyColumn durumu görüşme ekranına taşınır. kullanıcı arayüzü mantığını uygulayın ve bunu gerektiren tüm composable'lardan durumu okuyun:

LazyColumn durumunu LazyColumn'tan ConversationScreen'e kaldırma
Şekil 3. LazyColumn durumu LazyColumn noktasından ConversationScreen noktasına kaldırılıyor

Son olarak composable'lar şöyle:

ConversationScreen'a getirilen LazyListState ve sohbet composable ağacı
Şekil 4. LazyListState ile sohbet composable ağaç ConversationScreen konumuna kaldırıldı

Kod şu şekildedir:

@Composable
private fun ConversationScreen(/*...*/) {
    val scope = rememberCoroutineScope()

    val lazyListState = rememberLazyListState() // State hoisted to the ConversationScreen

    MessagesList(messages, lazyListState) // Reuse same state in MessageList

    UserInput(
        onMessageSent = { // Apply UI logic to lazyListState
            scope.launch {
                lazyListState.scrollToItem(0)
            }
        },
    )
}

@Composable
private fun MessagesList(
    messages: List<Message>,
    lazyListState: LazyListState = rememberLazyListState() // LazyListState has a default value
) {

    LazyColumn(
        state = lazyListState // Pass hoisted state to LazyColumn
    ) {
        items(messages, key = { message -> message.id }) { item ->
            Message(/*...*/)
        }
    }

    val scope = rememberCoroutineScope()

    JumpToBottom(onClicked = {
        scope.launch {
            lazyListState.scrollToItem(0) // UI logic being applied to lazyListState
        }
    })
}

LazyListState, olması gereken kullanıcı arayüzü mantığı için gereken kadar yükseltilir geçerlidir. composable işlevde başlatıldığı için, Bestenin yaşam döngüsü.

lazyListState öğesinin, MessagesList yönteminde varsayılan değere (rememberLazyListState()) sahip. Bu, Compose'da sık kullanılan bir kalıptır. composable'ları yeniden kullanılabilir ve esnek hale getirir. Ardından composable'ı kullanarak uygulamanın farklı bölümlerinde durumu kontrol etmelerine gerek yoktur. Bu durum genellikle bir composable'ı test ederken veya önizlerken geçerlidir. İşte bu şekilde Durumunu LazyColumn tanımlar.

LazyListState için en düşük ortak üst öğe ConversationScreen&#39;dir
Şekil 5. LazyListState için en düşük ortak üst öğe: ConversationScreen

Eyalet sahibi olarak düz eyalet sahibi sınıfı

Bir composable, bir veya daha fazla durum içeren karmaşık kullanıcı arayüzü mantığı içerdiğinde bir kullanıcı arayüzü öğesinin alanına eklenirse bu sorumluluğu sahipler gibi. Bu, composable'ın mantığını ve karmaşıklığını azaltır. Bu yaklaşım, endişelerin ayrılması ilkesi: composable devreye girer ya da kullanıcı arayüzü öğeleri yayınlamanın bir listesini içerir ve durum tutucu, kullanıcı arayüzü mantığını ve kullanıcı arayüzünü içerir öğe durumunu kontrol edin.

Düz durum tutucu sınıfları, sayfanızı arayan kişilere composable fonksiyonunu kullanarak bu mantığı yazma zorunluluğu yoktur.

Bu sade sınıflar Beste'de oluşturulur ve hatırlanır. Çünkü composable'ın yaşam döngüsünü takip ederseniz rememberNavController() veya rememberLazyListState() gibi kitaplık oluşturun.

Bunun bir örneği LazyListState düz durum tutucudur. sınıfı, LazyColumn kullanıcı arayüzü karmaşıklığını kontrol etmek için Compose'da uygulandı veya LazyRow.

// LazyListState.kt

@Stable
class LazyListState constructor(
    firstVisibleItemIndex: Int = 0,
    firstVisibleItemScrollOffset: Int = 0
) : ScrollableState {
    /**
     *   The holder class for the current scroll position.
     */
    private val scrollPosition = LazyListScrollPosition(
        firstVisibleItemIndex, firstVisibleItemScrollOffset
    )

    suspend fun scrollToItem(/*...*/) { /*...*/ }

    override suspend fun scroll() { /*...*/ }

    suspend fun animateScrollToItem() { /*...*/ }
}

LazyListState,LazyColumn Bu kullanıcı arayüzü öğesi için scrollPosition. Ayrıca, belirli bir öğeye gidin.

Gördüğünüz gibi bir composable'ın sorumluluklarının artması, bir gerekliliktir. Sorumluluklar kullanıcı arayüzü mantığı veya yalnızca durum miktarına bakılır.

Yaygın olarak kullanılan bir diğer model, uygulamadaki kök composable işlevlerin karmaşıklığı. Bu tür bir sınıfı şunlar için kullanabilirsiniz: gezinme durumu ve ekran boyutlandırma gibi uygulama düzeyindeki durumu kapsamalıdır. Tam bunun açıklamasını kullanıcı arayüzü mantığı ve durum tutucu sayfasında bulabilirsiniz.

İş mantığı

Kullanıcı arayüzü mantığından ve Kullanıcı arayüzü öğesi durumunda, ekran düzeyinde durum tutucu aşağıdakilerden sorumlu olur: görevler:

  • Aynı işleve sahip uygulamanın iş mantığına erişmesini sağlama genellikle hiyerarşinin diğer katmanlarına (ör. işletme, veri katmanından yararlanın.
  • Uygulama verileri belirli bir ekranda sunum için hazırlanıyor, Bu da ekranın kullanıcı arayüzü durumu olur.

Eyalet sahibi olarak ViewModels'ler

Android geliştirmede AAC ViewModel'lerin avantajları, onları iş mantığına erişim sağlamak ve uygulama verilerini hazırlamak için ekranda gösterilmesi için kullanılır.

Kullanıcı arayüzü durumunu ViewModel içine kaldırdığınızda, öğeyi Beste.

ViewModel&#39;e kaldırılan durum, Beste&#39;nin dışında depolanır.
Şekil 6. ViewModel değerine kaldırılan durum, Beste'nin dışında depolanır.

ViewModel'ler Beste'nin bir parçası olarak depolanmaz. Sağlayan: ve bir ViewModelStoreOwner kapsamında değerlendirilirler. Bu, Bir gezinme grafiğinin etkinliği, bölümü, gezinme grafiği veya hedefi. Örneğin, ViewModel kapsam hakkında daha fazla bilgi için dokümanları inceleyebilirsiniz.

Bu durumda ViewModel, şuna ait bilginin kaynağı ve en alçak ortak atadır: Kullanıcı arayüzü durumu.

Ekran kullanıcı arayüzü durumu

Yukarıdaki tanımlara göre, ekran kullanıcı arayüzü durumu, işletme kurallar. Ekran düzeyi durumu sahibi, bu işlemden sorumlu ekran kullanıcı arayüzü durumunun genellikle ekran düzeyi durumunda kaldırıldığı anlamına gelir öğesi, bu örnekte bir ViewModel.

Bir sohbet uygulamasının ConversationViewModel özelliğini ve ekranın nasıl göründüğünü düşünün Değiştirilecek kullanıcı arayüzü durumu ve etkinlikler:

class ConversationViewModel(
    channelId: String,
    messagesRepository: MessagesRepository
) : ViewModel() {

    val messages = messagesRepository
        .getLatestMessages(channelId)
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = emptyList()
        )

    // Business logic
    fun sendMessage(message: Message) { /* ... */ }
}

Özelleştirilebilir öğeler, ViewModel içine kaldırılan ekran kullanıcı arayüzü durumunu kullanır. Şunları yapmalısınız: enjekte etmek için ViewModel örneğini ekran düzeyinde composable'larınıza ekleyin iş mantığına erişmelerini sağlar.

Aşağıda, ekran düzeyinde bir composable'da kullanılan ViewModel örneği verilmiştir. Burada, composable ConversationScreen(), kaldırılan ekran kullanıcı arayüzü durumunu tüketir ViewModel:

@Composable
private fun ConversationScreen(
    conversationViewModel: ConversationViewModel = viewModel()
) {

    val messages by conversationViewModel.messages.collectAsStateWithLifecycle()

    ConversationScreen(
        messages = messages,
        onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) }
    )
}

@Composable
private fun ConversationScreen(
    messages: List<Message>,
    onSendMessage: (Message) -> Unit
) {

    MessagesList(messages, onSendMessage)
    /* ... */
}

Gayrimenkul sondajı

"Mülkü ayrıntılı inceleme", verilerin iç içe yerleştirilmiş birkaç alt öğeden geçirilmesini ifade eder içerikleri okundukları konuma taşımalıdır.

Compose'da sondajın gösterilebileceği tipik bir örnek, ekran düzeyi durum tutucusunu üst düzeye yerleştirin ve aşağı durumu iletin ve . Bu da aşırı yüklenmeye yol açabilir. composable işlevler imzaları gibidir.

Etkinlikleri ayrı lambda parametreleri olarak göstermek, composable fonksiyonunun görünürlüğünü en üst düzeye çıkararak hakkında daha fazla bilgi edindiniz. Ne işe yaradığını bir bakışta görebilirsiniz.

Mülk işleme, kapsüllemek için sarmalayıcı sınıfları oluşturmak yerine tercih edilir durumu ve olayları tek bir yerde toplaması sayesinde, sorumluluklar var. Sarmalayıcı sınıfların olmaması, composable'lara yalnızca ihtiyaç duydukları parametreleri iletme ihtimali vardır. Bu da alıştırma da yapın.

Bu etkinlikler gezinme etkinlikleriyse de aynı en iyi uygulama geçerlidir. gezinme dokümanlarından bu konu hakkında daha fazla bilgi edinebilirsiniz.

Bir performans sorunu tespit ettiyseniz okumayı ertelemeyi de seçebilirsiniz durumu. Daha fazla bilgi edinmek için performans belgelerini inceleyebilirsiniz.

kullanıcı arayüzü öğesinin durumu

Kullanıcı arayüzü öğesi durumunu, okuması veya yazması gereken bir iş mantığıdır.

Sohbet uygulaması örneğine devam edersek uygulama, kullanıcı önerilerini Kullanıcı @ ve bir ipucu yazdığında grup sohbeti. Bu öneriler kullanıcı önerileri listesini hesaplama mantığı ve veri katmanı iş mantığına dayanır. Bu özellik aşağıdaki gibi görünür:

Kullanıcı &quot;@&quot; ve ipucu yazdığında grup sohbetinde kullanıcı önerilerini gösteren özellik
Şekil 7. Kullanıcı @ ve bir ipucu yazdığında grup sohbetinde kullanıcı önerilerini gösteren özellik

Bu özelliği uygulamak için kullanılan ViewModel aşağıdaki gibi görünür:

class ConversationViewModel(/*...*/) : ViewModel() {

    // Hoisted state
    var inputMessage by mutableStateOf("")
        private set

    val suggestions: StateFlow<List<Suggestion>> =
        snapshotFlow { inputMessage }
            .filter { hasSocialHandleHint(it) }
            .mapLatest { getHandle(it) }
            .mapLatest { repository.getSuggestions(it) }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = emptyList()
            )

    fun updateInput(newInput: String) {
        inputMessage = newInput
    }
}

inputMessage, TextField durumunu depolayan bir değişkendir. Her Kullanıcı yeni bir giriş yazarsa uygulama, suggestions üretmek için iş mantığını çağırır.

suggestions, ekran kullanıcı arayüzü durumudur ve veri toplama işlevi kullanılarak Compose kullanıcı arayüzünden tüketilir StateFlow.

Uyarı

Bazı Compose kullanıcı arayüzü öğesi durumlarında ViewModel öğesine kaldırma işlemi gerekebilir dikkat edilmesi gereken birkaç nokta var. Örneğin, Compose kullanıcı arayüzü öğelerinin bazı durum sahipleri ve durumu değiştirme yöntemleri sunulabilir. Bunlardan bazıları, animasyonları tetikleyebilir. Bu askıya alma işlevleri bunlarıCoroutineScope Beste.

Uygulama çekmecesinin içeriğinin dinamik olduğunu ve alıp yenilemeniz gerektiğini varsayalım. veri katmanından kaldırmanız gerekir. Çekmece durumunu, dikey biçimde Bu öğede hem kullanıcı arayüzünü hem de iş mantığını çağırabilmeniz için ViewModel bunu yapabilirsiniz.

Ancak, DrawerStateclose() Oluştur kullanıcı arayüzündeki viewModelScope, şu tür bir çalışma zamanı istisnasına neden oluyor: IllegalStateException yazan bir mesajda "a" MonotonicFrameClock bu konumda kullanılamıyor CoroutineContext”.

Bunu düzeltmek için Beste'ye ayarlanmış bir CoroutineScope kullanın. Sağladığı CoroutineContext içindeki MonotonicFrameClock askıya alma işlevleri kullanılabilir.

Bu kilitlenmeyi düzeltmek için eş yordanın CoroutineContext ayarını değiştirin Beste'ye ayarlanan bir genel bakış için ViewModel. Bu kod aşağıdaki gibi görünebilir:

class ConversationViewModel(/*...*/) : ViewModel() {

    val drawerState = DrawerState(initialValue = DrawerValue.Closed)

    private val _drawerContent = MutableStateFlow(DrawerContent.Empty)
    val drawerContent: StateFlow<DrawerContent> = _drawerContent.asStateFlow()

    fun closeDrawer(uiScope: CoroutineScope) {
        viewModelScope.launch {
            withContext(uiScope.coroutineContext) { // Use instead of the default context
                drawerState.close()
            }
            // Fetch drawer content and update state
            _drawerContent.update { content }
        }
    }
}

// in Compose
@Composable
private fun ConversationScreen(
    conversationViewModel: ConversationViewModel = viewModel()
) {
    val scope = rememberCoroutineScope()

    ConversationScreen(onCloseDrawer = { conversationViewModel.closeDrawer(uiScope = scope) })
}

Daha fazla bilgi

State ve Jetpack Compose hakkında daha fazla bilgi edinmek için aşağıdakilere bakın ek kaynaklar.

Örnekler

Codelab'ler

Videolar

ziyaret edin. ziyaret edin.