Kullanıcı arayüzü katmanı

Kullanıcı arayüzünün rolü, uygulama verilerini ekranda görüntülemek ve kullanıcı etkileşiminin birincil noktası olarak hizmet etmektir. Kullanıcı etkileşimi (ör. bir düğmeye basma) veya harici giriş (ağ yanıtı gibi) nedeniyle veriler her değiştiğinde, kullanıcı arayüzü bu değişiklikleri yansıtacak şekilde güncellenmelidir. Kullanıcı arayüzü, etkili bir şekilde uygulama durumunun veri katmanından alındığı şekliyle görsel bir temsilidir.

Ancak veri katmanından aldığınız uygulama verileri, genellikle, görüntülemek istediğiniz bilgilerden farklı bir biçimde olur. Örneğin, kullanıcı arayüzü verilerinin yalnızca bir kısmına ihtiyacınız olabilir veya kullanıcıyla alakalı bilgiler sunmak için iki farklı veri kaynağını birleştirmeniz gerekebilir. Uyguladığınız mantık ne olursa olsun, tam olarak oluşturulması için gereken tüm bilgileri kullanıcı arayüzüne iletmeniz gerekir. Kullanıcı arayüzü katmanı, uygulama verisi değişikliklerini kullanıcı arayüzünün sunabileceği ve ardından görüntüleyebileceği bir forma dönüştüren ardışık düzendir.

Tipik bir mimaride, kullanıcı arayüzü katmanının kullanıcı arayüzü öğeleri durum sahiplerine bağlıdır. Durum sahipleri ise veri katmanındaki veya isteğe bağlı alan katmanındaki sınıflara bağlıdır.
Şekil 1. Kullanıcı arayüzü katmanının uygulama mimarisindeki rolü.

Temel örnek olay

Kullanıcının okuması için haber makaleleri getiren bir uygulama düşünün. Uygulamada, okunmaya hazır makalelerin gösterildiği bir makale ekranı bulunur ve ayrıca, oturum açmış kullanıcıların dikkat çeken makalelere yer işareti koymalarına olanak sağlanır. Herhangi bir zamanda çok sayıda makale olabileceği düşünülürse, okuyucu makalelere kategoriye göre göz atabilmelidir. Özetle, uygulama kullanıcıların aşağıdakileri yapmasına olanak tanır:

  • Okunmaya uygun makaleleri görüntüleyin.
  • Makalelere kategoriye göre göz atın.
  • Oturum açın ve belirli makalelere yer işareti koyun.
  • Uygun durumlarda bazı ücretli özelliklere erişebilirsiniz.
Şekil 2. Kullanıcı arayüzünde örnek olay için örnek haber uygulaması.

Aşağıdaki bölümlerde bu örnek, tek yönlü veri akışı ilkelerinin tanıtıldığı ve bu ilkelerin, kullanıcı arayüzü katmanı için uygulama mimarisi bağlamında çözmeye yardımcı olduğu sorunların gösterildiği bir örnek olay olarak kullanılmıştır.

Kullanıcı arayüzü katmanı mimarisi

Kullanıcı arayüzü terimi, bunu yapmak için kullandıkları API'lerden (Views veya Jetpack Compose) bağımsız olarak verileri gösteren etkinlikler ve parçalar gibi kullanıcı arayüzü öğelerini ifade eder. Veri katmanının rolü uygulama verilerini tutmak, yönetmek ve bunlara erişim sağlamak olduğundan kullanıcı arayüzü katmanının aşağıdaki adımları gerçekleştirmesi gerekir:

  1. Uygulama verilerini kullanma ve kullanıcı arayüzünün kolayca oluşturabileceği verilere dönüştürme.
  2. Kullanıcı arayüzünde oluşturulabilecek verileri tüketin ve kullanıcıya sunmak için kullanıcı arayüzü öğelerine dönüştürün.
  3. Bu derlenmiş kullanıcı arayüzü öğelerindeki kullanıcı girişi etkinliklerini tüketin ve bunların etkilerini kullanıcı arayüzü verilerine gerektiği şekilde yansıtın.
  4. 1-3 arasındaki adımları gerektiği kadar tekrarlayın.

Bu kılavuzun geri kalanında, bu adımları gerçekleştiren bir kullanıcı arayüzü katmanının nasıl uygulanacağı gösterilmektedir. Bu kılavuz özellikle aşağıdaki görevleri ve kavramları kapsar:

  • Kullanıcı arayüzü durumunu tanımlama.
  • Kullanıcı arayüzü durumunu oluşturma ve yönetme yöntemi olarak tek yönlü veri akışı (UDF).
  • UDF ilkelerine göre gözlemlenebilir veri türleriyle kullanıcı arayüzü durumunu gösterme.
  • Gözlemlenebilir kullanıcı arayüzü durumunu kullanan kullanıcı arayüzü nasıl uygulanır?

Bunların en temeli, kullanıcı arayüzü durumunun tanımıdır.

Kullanıcı arayüzü durumunu tanımlama

Daha önce özetlenen örnek olaya bakın. Kısacası, kullanıcı arayüzünde makalelerin listesi ve her makaleye ait meta veriler gösterilmektedir. Uygulamanın kullanıcıya sunduğu bu bilgi, kullanıcı arayüzü durumudur.

Başka bir deyişle: Kullanıcı arayüzü, kullanıcı arayüzü kullanıyorsa kullanıcı arayüzü durumu, uygulamanın görmesi gerektiğini söylediği şeydir. Aynı paranın iki yüzü gibi, kullanıcı arayüzü de kullanıcı arayüzü durumunun görsel temsilidir. Kullanıcı arayüzü durumundaki değişiklikler kullanıcı arayüzüne hemen yansıtılır.

Kullanıcı arayüzü, ekranda kullanıcı arayüzü öğelerinin kullanıcı arayüzü durumuyla bağlanmasının bir sonucudur.
Şekil 3. Kullanıcı arayüzü, ekranda kullanıcı arayüzü öğelerinin kullanıcı arayüzü durumuyla bağlanmasının bir sonucudur.

Örnek olayı düşünün. Haberler uygulamasının gereksinimlerini karşılamak üzere, kullanıcı arayüzünü tam olarak oluşturmak için gereken bilgiler, aşağıdaki şekilde tanımlanan bir NewsUiState veri sınıfına dahil edilebilir:

data class NewsUiState(
    val isSignedIn: Boolean = false,
    val isPremium: Boolean = false,
    val newsItems: List<NewsItemUiState> = listOf(),
    val userMessages: List<Message> = listOf()
)

data class NewsItemUiState(
    val title: String,
    val body: String,
    val bookmarked: Boolean = false,
    ...
)

Sabitlik

Yukarıdaki örnekte yer alan kullanıcı arayüzü durum tanımı sabittir. Bunun en önemli avantajı, sabit nesnelerin uygulamanın anında uygulama durumuyla ilgili garantiler vermesidir. Bu, kullanıcı arayüzünün tek bir role odaklanmasını sağlar: durumu okuma ve kullanıcı arayüzü öğelerini uygun şekilde güncelleme. Bu nedenle, verilerinin tek kaynağı kullanıcı arayüzü değilse hiçbir zaman kullanıcı arayüzü durumunu doğrudan kullanıcı arayüzünde değiştirmemeniz gerekir. Bu ilkenin ihlal edilmesi, aynı bilgi için birden çok doğruluk kaynağıyla sonuçlanır. Bu da veri tutarsızlıklarına ve küçük hatalara yol açar.

Örneğin, örnek olaydaki kullanıcı arayüzü durumundaki bir NewsItemUiState nesnesindeki bookmarked işareti Activity sınıfında güncellenmişse bu işaret, bir makalenin yer işareti koyulma durumunun kaynağı olarak veri katmanıyla rekabet eder. Sabit veri sınıfları, bu tür antikalıpları önlemek için oldukça yararlıdır.

Bu kılavuzdaki adlandırma kuralları

Bu kılavuzda, kullanıcı arayüzü durum sınıfları ekranın veya ekranın açıkladıkları bir bölümünün işlevlerine göre adlandırılmıştır. Sistem aşağıdaki gibidir:

İşlevler + UiState.

Örneğin, haberlerin gösterildiği bir ekranın durumu NewsUiState, haber öğeleri listesindeki bir haber öğesinin durumu ise NewsItemUiState olarak adlandırılabilir.

Tek Yönlü Veri Akışı ile durumu yönetme

Önceki bölümde, kullanıcı arayüzü durumunun, kullanıcı arayüzünün oluşturulması için gereken ayrıntıların sabit bir anlık görüntüsü olduğu belirtiliyordu. Ancak, uygulamalardaki verilerin dinamik doğası, durumun zaman içinde değişebileceği anlamına gelir. Bunun nedeni, kullanıcı etkileşimi veya uygulamayı doldurmak için kullanılan temel verileri değiştiren diğer etkinlikler olabilir.

Bu etkileşimler, her etkinliğe uygulanacak mantığı tanımlayarak ve kullanıcı arayüzü durumu oluşturmak için gerekli dönüşümleri destekleyen veri kaynaklarında gerekli dönüşümleri gerçekleştirerek bunları işlemesi için bir aracıdan yararlanabilir. Bu etkileşimler ve mantığı kullanıcı arayüzünün kendisinde barındırılabilir. Ancak kullanıcı arayüzü, adından da anlaşılacağı gibi veri sahibi, üretici, dönüştürücü ve daha birçok varlık haline gelmeye başladıkça bu durum kısa sürede kontrolden çıkmayabilir. Dahası, ortaya çıkan kod fark edilebilir sınır olmayan, sıkı sıkıya bağlı bir amalgam olduğundan bu durum test edilebilirliği etkileyebilir. Sonuç olarak, kullanıcı arayüzü daha az yükten faydalanıyor. Kullanıcı arayüzü durumu çok basit olmadığı sürece, kullanıcı arayüzünün tek sorumluluğu kullanıcı arayüzü durumunu tüketmek ve görüntülemek olmalıdır.

Bu bölümde, sorumluluğun düzgün şekilde ayrılmasını sağlamaya yardımcı olan bir mimari kalıbı olan Tek Yönlü Veri Akışı (UDF) ele alınmaktadır.

Devlet sahipleri

Kullanıcı arayüzü durumunun oluşturulmasından sorumlu olan ve bu görev için gerekli mantığı içeren sınıflara devlet sahipleri adı verilir. Durum sahipleri, yönettikleri ilgili kullanıcı arayüzü öğelerinin kapsamına bağlı olarak, alt uygulama çubuğu gibi tek bir widget'tan tüm ekrana veya gezinme hedefine kadar çeşitli boyutlarda olabilir.

İkinci durumda tipik uygulama, ViewModel örneğidir. Ancak uygulamanın gereksinimlerine bağlı olarak basit bir sınıf yeterli olabilir. Örneğin, örnek olaydaki News uygulaması, söz konusu bölümde görüntülenen ekranın kullanıcı arayüzü durumunu üretmek için eyalet sahibi olarak bir NewsViewModel sınıfı kullanır.

Kullanıcı arayüzü ve durum üreticisi arasındaki kod bağımlılığını modellemenin birçok yolu vardır. Bununla birlikte, kullanıcı arayüzü ile ViewModel sınıfı arasındaki etkileşim büyük ölçüde etkinlik girişi ve ardından gelen durum çıkış olarak anlaşılabileceğinden, ilişki aşağıdaki şemada gösterildiği gibi temsil edilebilir:

Uygulama verileri, veri katmanından ViewModel&#39;e aktarılır. Kullanıcı arayüzü durumu, ViewModel&#39;den kullanıcı arayüzü öğelerine, etkinlikler ise kullanıcı arayüzü öğelerinden tekrar ViewModel&#39;e aktarılır.
Şekil 4. UDF'nin uygulama mimarisinde nasıl çalıştığını gösteren şema.

Durumun aşağı aktığı ve etkinliklerin yukarı doğru çıktığı kalıp, tek yönlü veri akışı (UDF) olarak adlandırılır. Bu kalıbın uygulama mimarisi için etkileri şu şekildedir:

  • ViewModel, durumu kullanıcı arayüzü tarafından kullanılacak şekilde saklar ve kullanıma sunar. Kullanıcı arayüzü durumu, ViewModel tarafından dönüştürülen uygulama verileridir.
  • Kullanıcı arayüzü, ViewModel'i kullanıcı etkinliklerini bilgilendirir.
  • ViewModel, kullanıcı işlemlerini işler ve durumu günceller.
  • Güncellenen durum, oluşturma işlemi için kullanıcı arayüzüne geri aktarılır.
  • Durum değişikliğine neden olan tüm olaylar için yukarıdaki işlemler tekrarlanır.

ViewModel, navigasyon hedefleri veya ekranları için veri depoları ya da kullanım alanı sınıflarıyla çalışarak verileri alıp kullanıcı arayüzü durumuna dönüştürür ve aynı zamanda durumun değişmesine neden olabilecek etkinliklerin etkilerini entegre eder. Daha önce bahsedilen örnek olayda, her birinin başlığı, açıklaması, kaynağı, yazar adı, yayınlanma tarihi ve yer işareti koyulup eklenmediği gibi makalelerin listesi yer alır. Her makale öğesinin kullanıcı arayüzü aşağıdaki gibi görünür:

Şekil 5. Örnek olay uygulamasındaki bir makale öğesinin kullanıcı arayüzü.

Bir makaleye yer işareti koymak isteyen bir kullanıcı, durum değişimlerine neden olabilecek etkinliklere örnek olarak verilebilir. Durum üreticisi olarak, kullanıcı arayüzü durumundaki tüm alanları doldurmak ve kullanıcı arayüzünün tam olarak oluşturulması için gereken etkinlikleri işlemek için gereken tüm mantığı tanımlamak ViewModel'in sorumluluğundadır.

Kullanıcı bir eklere yer işareti koyduğunda bir kullanıcı arayüzü etkinliği oluşur. ViewModel, veri katmanına durum değişikliğini bildirir. Veri katmanı, veri değişimine devam eder ve uygulama verilerini günceller. Yer işareti eklenmiş makaleyi içeren yeni uygulama verileri ViewModel&#39;e iletilir. ViewModel, yeni kullanıcı arayüzü durumunu üretir ve görüntülenmek üzere kullanıcı arayüzü öğelerine iletir.
Şekil 6. UDF'de etkinlik ve veri döngüsünü gösteren şema.

Aşağıdaki bölümlerde durum değişikliklerine neden olan olaylar ve bunların UDF kullanılarak nasıl işlenebileceği daha ayrıntılı olarak ele alınmaktadır.

Mantık türleri

Bir makaleye yer işareti koymak, uygulamanıza değer kattığı için iş mantığına bir örnektir. Bu konuda daha fazla bilgi edinmek için veri katmanı sayfasına bakın. Ancak, tanımlanması gereken farklı mantık türleri vardır:

  • İş mantığı, uygulama verileri için ürün gereksinimlerinin uygulanmasıdır. Daha önce de bahsedildiği gibi, örnek olay uygulamasındaki bir makaleye yer işareti koymaktır. İş mantığı genellikle alana veya veri katmanlarına yerleştirilir, ancak hiçbir zaman kullanıcı arayüzü katmanına yerleştirilemez.
  • Kullanıcı arayüzü davranış mantığı veya kullanıcı arayüzü mantığı, durum değişikliklerini ekranda nasıl görüntüleyeceğinizi ifade eder. Android Resources kullanarak ekranda gösterilecek doğru metni elde etme, kullanıcı düğmeyi tıkladığında belirli bir ekrana gitme ya da kısa mesaj veya atıştırmalık çubuğu kullanarak ekranda kullanıcı mesajı görüntüleme buna örnek olarak verilebilir.

Özellikle Context gibi kullanıcı arayüzü türleri içerdiğinde kullanıcı arayüzü mantığı, ViewModel'de değil, kullanıcı arayüzünde bulunmalıdır. Kullanıcı arayüzü karmaşık hale gelirse ve test edilebilirlik ve kaygıların birbirinden ayrılması için kullanıcı arayüzü mantığını başka bir sınıfa yetkilendirmek isterseniz devlet yöneticisi olarak basit bir sınıf oluşturabilirsiniz. Kullanıcı arayüzünde oluşturulan basit sınıflar, kullanıcı arayüzünün yaşam döngüsünü takip ettikleri için Android SDK bağımlılıklarını alabilir; ViewModel nesnelerinin ömürleri daha uzundur.

Devlet sahipleri hakkında daha fazla bilgi edinmek ve bunların kullanıcı arayüzü oluşturmaya yardımcı olma bağlamına nasıl uyduklarını öğrenmek için Jetpack Compose State (Durum) kılavuzuna bakın.

UDF'yi neden kullanmalısınız?

UDF, Şekil 4'te gösterildiği gibi durum üretim döngüsünü modeller. Ayrıca durum değişikliklerinin kaynaklandığı, dönüştürüldüğü ve nihayet tüketildiği yeri ayırır. Bu ayırma, kullanıcı arayüzünün tam olarak adının ima ettiği şeyi yapmasını sağlar: durum değişikliklerini gözlemleyerek bilgileri görüntüleme ve bu değişiklikleri ViewModel'e ileterek kullanıcı niyetini aktarma.

Başka bir deyişle, UDF aşağıdakileri sağlar:

  • Veri tutarlılığı. Kullanıcı arayüzü için tek ve doğru bir kaynak vardır.
  • Test edilebilirlik. Durum kaynağı izoledir ve bu nedenle kullanıcı arayüzünden bağımsız olarak test edilebilir.
  • Sürdürülebilirlik. Durum değişikliği, mutasyonların hem kullanıcı etkinliklerinin hem de kullanıcı kaynaklı veri kaynaklarının bir sonucu olduğu iyi tanımlanmış bir kalıbı izler.

Kullanıcı arayüzü durumunu göster

Kullanıcı arayüzü durumunuzu tanımladıktan ve bu durumun üretimini nasıl yöneteceğinizi belirledikten sonra, üretilen durumu kullanıcı arayüzüne sunarsınız. Eyalet üretimini yönetmek için UDF'yi kullandığınız için oluşturulan durumu bir akış olarak düşünebilirsiniz. Diğer bir deyişle, zaman içinde eyaletin birden fazla versiyonu oluşturulacaktır. Bu nedenle, kullanıcı arayüzü durumunu LiveData veya StateFlow gibi gözlemlenebilir bir veri tutucuda göstermeniz gerekir. Bunun nedeni, kullanıcı arayüzünün verileri doğrudan ViewModel'den manuel olarak çekmek zorunda kalmadan durumda yapılan değişikliklere tepki verebilmesidir. Bu türler, her zaman kullanıcı arayüzü durumunun en son sürümünün önbelleğe alınması avantajına da sahiptir. Bu, yapılandırma değişikliklerinden sonra hızlı durum geri yükleme için yararlıdır.

Görüntüleme sayısı

class NewsViewModel(...) : ViewModel() {

    val uiState: StateFlow<NewsUiState> = …
}

Oluştur

class NewsViewModel(...) : ViewModel() {

    val uiState: NewsUiState = …
}

Gözlemlenebilir veri sahibi olarak LiveData hakkında temel bilgiler için bu codelab'e bakın. Kotlin akışlarına benzer bir giriş için Android'de Kotlin akışları konusuna bakın.

Kullanıcı arayüzüne maruz kalan verilerin nispeten basit olduğu durumlarda, durum sahibinin emisyonu ile ilişkili ekran veya kullanıcı arayüzü öğesi arasındaki ilişkiyi aktardığından verileri kullanıcı arayüzü durum türünde sarmalamak genellikle iyi bir fikirdir. Ayrıca, kullanıcı arayüzü öğesi karmaşıklaştıkça, kullanıcı arayüzü öğesini oluşturmak için gereken ek bilgilere yer vermek amacıyla kullanıcı arayüzü durumunun tanımına ekleme yapmak her zaman daha kolay olur.

UiState akışı oluşturmanın yaygın bir yolu, yedek değişken akışı ViewModel'den sabit bir akış olarak göstermektir. Örneğin, bir MutableStateFlow<UiState> öğesini StateFlow<UiState> olarak gösterebilirsiniz.

Görüntüleme sayısı

class NewsViewModel(...) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    ...

}

Oluştur

class NewsViewModel(...) : ViewModel() {

    var uiState by mutableStateOf(NewsUiState())
        private set

    ...
}

Daha sonra ViewModel, durumu dahili olarak değiştiren yöntemleri ortaya çıkarabilir ve kullanıcı arayüzünün tüketmesi için güncellemeler yayınlayabilir. Örneğin, eşzamansız bir işlemin yapılması gerektiğini ele alalım; viewModelScope ile bir eş yordam başlatılabilir ve tamamlandığında değişken durum güncellenebilir.

Görüntüleme sayısı

class NewsViewModel(
    private val repository: NewsRepository,
    ...
) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                _uiState.update {
                    it.copy(newsItems = newsItems)
                }
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                _uiState.update {
                    val messages = getMessagesFromThrowable(ioe)
                    it.copy(userMessages = messages)
                 }
            }
        }
    }
}

Oluştur

class NewsViewModel(
    private val repository: NewsRepository,
    ...
) : ViewModel() {

   var uiState by mutableStateOf(NewsUiState())
        private set

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                uiState = uiState.copy(newsItems = newsItems)
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                val messages = getMessagesFromThrowable(ioe)
                uiState = uiState.copy(userMessages = messages)
            }
        }
    }
}

Yukarıdaki örnekte, NewsViewModel sınıfı belirli bir kategorideki makaleleri getirmeye çalışır ve daha sonra, denemenin sonucunu (başarılı veya başarısız) kullanıcı arayüzünün uygun bir şekilde tepki verebileceği kullanıcı arayüzü durumunda yansıtır. Hata işleme hakkında daha fazla bilgi edinmek için Ekranda hataları göster bölümüne bakın.

Göz önünde bulundurulması gereken diğer noktalar

Kullanıcı arayüzü durumunu gösterirken önceki kılavuza ek olarak aşağıdakileri göz önünde bulundurun:

  • Kullanıcı arayüzü durum nesnesi, birbiriyle ilgili durumları işlemelidir. Bu, daha az tutarsızlık olmasını sağlar ve kodun daha kolay anlaşılmasını sağlar. Haber öğeleri listesini ve iki farklı akıştaki yer işaretlerinin sayısını gösterirseniz, bunlardan birinin güncellenmiş, diğerinin güncellenmediği bir durumla karşılaşabilirsiniz. Tek bir akış kullandığınızda her iki öğe de güncel tutulur. Ayrıca bazı iş mantığı için kaynakların bir kombinasyonu gerekebilir. Örneğin, yalnızca kullanıcı oturum açmış ve premium bir haber hizmetinin abonesiyse yer işareti düğmesi göstermeniz gerekebilir. Bir kullanıcı arayüzü durum sınıfını aşağıdaki gibi tanımlayabilirsiniz:

    data class NewsUiState(
        val isSignedIn: Boolean = false,
        val isPremium: Boolean = false,
        val newsItems: List<NewsItemUiState> = listOf()
    )
    
    val NewsUiState.canBookmarkNews: Boolean get() = isSignedIn && isPremium
    

    Bu bildirimde, yer işareti düğmesinin görünürlüğü diğer iki özelliğin türetilmiş bir özelliğidir. İş mantığı daha karmaşık hale geldikçe, tüm mülklerin anında kullanılabilir olduğu tek bir UiState sınıfına sahip olmak giderek daha önemli hale gelmektedir.

  • Kullanıcı arayüzü durumları: tek yayın mı birden çok yayın mı? Kullanıcı arayüzü durumunu tek bir akışta mı yoksa birden fazla akışta mı göstermeniz gerektiğine karar vermenin temel yolu, bir önceki maddedir: yani yayınlanan öğeler arasındaki ilişki. Tek bir yayında karşılaşmanın en büyük avantajı kolaylık ve veri tutarlılığıdır: Mevcut tüketiciler her zaman, herhangi bir zamanda en yeni bilgilere sahiptir. Bununla birlikte, ViewModel'den ayrı durum akışlarının uygun olabileceği durumlar da vardır:

    • İlişkisiz veri türleri: Kullanıcı arayüzünü oluşturmak için gereken bazı durumlar birbirinden tamamen bağımsız olabilir. Bu gibi durumlarda, özellikle bu durumlardan biri diğerine göre daha sık güncelleniyorsa, bu farklı eyaletleri gruplandırmanın maliyeti faydalardan ağır basabilir.

    • UiState farkı: Bir UiState nesnesinde ne kadar fazla alan olursa akışın alanlarından birinin güncellenmesi sonucunda akışın yayılma olasılığı o kadar yüksek olur. İzlenme sayısı, ardışık emisyonların farklı mı yoksa aynı mı olduğunu anlamak için farklı bir mekanizmaya sahip olmadığından her emisyon, görünümün güncellenmesine neden olur. Bu nedenle, LiveData üzerinde Flow API'leri veya distinctUntilChanged() gibi yöntemler kullanılarak çözüm uygulanması gerekebilir.

Kullanıcı arayüzü durumunu tüket

Kullanıcı arayüzündeki UiState nesnelerinin akışını kullanmak için kullandığınız gözlemlenebilir veri türünün terminal operatörünü kullanırsınız. Örneğin, LiveData için observe() yöntemini, Kotlin akışları için collect() yöntemini veya varyasyonlarını kullanırsınız.

Kullanıcı arayüzünde gözlemlenebilir veri sahiplerini kullanırken kullanıcı arayüzünün yaşam döngüsünü dikkate aldığınızdan emin olun. Bu önemlidir, çünkü görünüm kullanıcıya gösterilmezken kullanıcı arayüzünün, kullanıcı arayüzü durumunu gözlemlememesi gerekir. Bu konu hakkında daha fazla bilgi edinmek için bu blog yayınına bakın. LiveData kullanıldığında LifecycleOwner, yaşam döngüsü endişelerini dolaylı yoldan ele alır. Akışları kullanırken bunu uygun eş yordam kapsamı ve repeatOnLifecycle API ile yönetmek en iyisidir:

Görüntüleme sayısı

class NewsActivity : AppCompatActivity() {

    private val viewModel: NewsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

Oluştur

@Composable
fun LatestNewsScreen(
    viewModel: NewsViewModel = viewModel()
) {
    // Show UI elements based on the viewModel.uiState
}

Devam eden işlemleri göster

UiState sınıfında yükleme durumlarını temsil etmenin basit bir yolu boole alanını kullanmaktır:

data class NewsUiState(
    val isFetchingArticles: Boolean = false,
    ...
)

Bu işaretin değeri, kullanıcı arayüzünde bir ilerleme çubuğunun olup olmadığını gösterir.

Görüntüleme sayısı

class NewsActivity : AppCompatActivity() {

    private val viewModel: NewsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // Bind the visibility of the progressBar to the state
                // of isFetchingArticles.
                viewModel.uiState
                    .map { it.isFetchingArticles }
                    .distinctUntilChanged()
                    .collect { progressBar.isVisible = it }
            }
        }
    }
}

Oluştur

@Composable
fun LatestNewsScreen(
    modifier: Modifier = Modifier,
    viewModel: NewsViewModel = viewModel()
) {
    Box(modifier.fillMaxSize()) {

        if (viewModel.uiState.isFetchingArticles) {
            CircularProgressIndicator(Modifier.align(Alignment.Center))
        }

        // Add other UI elements. For example, the list.
    }
}

Hataları ekranda göster

Kullanıcı arayüzünde hataları göstermek, devam eden işlemleri göstermeye benzer. Çünkü her ikisi de, var olup olmadıklarını belirten boole değerleriyle kolayca temsil edilebilir. Bununla birlikte hatalar, kullanıcıya geri aktarmak için ilgili bir mesajı veya hatayla ilişkilendirilmiş ve başarısız işlemi yeniden deneyen bir işlemi de içerebilir. Bu nedenle, devam eden bir işlem yüklenirken veya yüklenmezken hata durumlarının, hatanın bağlamına uygun meta verileri barındıran veri sınıflarıyla modellenmesi gerekebilir.

Örneğin, önceki bölümde yer alan ve makaleleri getirirken ilerleme çubuğu gösteren örneği ele alalım. Bu işlem hatayla sonuçlanırsa kullanıcıya neyin yanlış gittiğini ayrıntılarıyla açıklayan bir veya daha fazla mesaj görüntülemek isteyebilirsiniz.

data class Message(val id: Long, val message: String)

data class NewsUiState(
    val userMessages: List<Message> = listOf(),
    ...
)

Hata mesajları daha sonra kullanıcıya atıştırmalıklar gibi kullanıcı arayüzü öğeleri biçiminde sunulabilir. Bu, kullanıcı arayüzü etkinliklerinin oluşturulma ve tüketilme şekliyle ilgili olduğundan daha fazla bilgi edinmek için Kullanıcı arayüzü etkinlikleri sayfasına bakın.

İş parçacığı işleme ve eşzamanlılık

ViewModel'de gerçekleştirilen tüm işler main-safe olmalıdır; ana iş parçacığından çağrılması güvenli olmalıdır. Bunun nedeni, işi farklı bir iş parçacığına taşımaktan veri ve alan katmanlarının sorumlu olmasıdır.

ViewModel, uzun süreli işlemler gerçekleştiriyorsa bu mantığı bir arka plan iş parçacığına taşımaktan da sorumludur. Kotlin eş yordamları, eşzamanlı işlemleri yönetmenin harika bir yoludur. Jetpack Mimari Bileşenleri ise bunlar için yerleşik destek sağlar. Android uygulamalarında eş yordamları kullanma hakkında daha fazla bilgi edinmek için Android'de Kotlin eş yordamları konusunu inceleyin.

Uygulamada gezinmedeki değişiklikler genellikle etkinliğe benzer emisyonlardan kaynaklanır. Örneğin, bir SignInViewModel sınıfı oturum açtıktan sonra UiState için isSignedIn alanı true olarak ayarlanmış olabilir. Bu gibi tetikleyiciler, yukarıdaki Kullanıcı arayüzü durumu bölümünde ele alınanlarla aynı şekilde kullanılmalıdır. Tek fark, tüketim uygulamasının Gezinme bileşenine bağlı olması gerekir.

Sayfalama

Çağrı kitaplığı, kullanıcı arayüzünde PagingData adlı bir türle kullanılır. PagingData, zaman içinde değişebilen öğeleri temsil edip içerdiğinden, diğer bir deyişle, sabit bir tür olmadığından sabit bir kullanıcı arayüzü durumunda temsil edilmemelidir. Bunun yerine, bunu ViewModel'den bağımsız olarak kendi akışında göstermeniz gerekir. Buna özel bir örnek için Android Sayfalama codelab'ine bakın.

Animasyonlar

Üst düzey gezinme geçişlerinin akıcı ve sorunsuz olmasını sağlamak için, animasyonu başlatmadan önce ikinci ekranın veri yüklemesini beklemeniz önerilir. Android görünümü çerçevesi, postponeEnterTransition() ve startPostponedEnterTransition() API'leriyle parça hedefleri arasındaki geçişleri geciktirmek için kancalar sağlar. Bu API'ler, kullanıcı arayüzünde söz konusu ekrana geçişi canlandırmadan önce ikinci ekrandaki kullanıcı arayüzü öğelerinin (genellikle ağdan getirilen bir resim) görüntülenmeye hazır olmasını sağlamanın bir yolunu sunar. Daha fazla ayrıntı ve uygulama özellikleri için Android Motion örneğine bakın.

Sana Özel

Aşağıdaki Google örneklerinde, kullanıcı arayüzü katmanının kullanımı gösterilmektedir. Bu kılavuzu inceleyerek örnekleri inceleyin: