Kullanıcı arayüzü etkinlikleri

Kullanıcı arayüzü etkinlikleri, kullanıcı arayüzü katmanında kullanıcı arayüzü veya ViewModel tarafından işlenmesi gereken işlemlerdir. En yaygın etkinlik türü kullanıcı etkinlikleridir. Kullanıcı, uygulamayla etkileşim kurarak (ör. ekrana dokunarak veya hareketler oluşturarak) kullanıcı etkinlikleri oluşturur. Kullanıcı arayüzü daha sonra onClick() işleyicileri gibi geri çağırma işlevlerini kullanarak bu etkinlikleri tüketir.

ViewModel, normalde belirli bir kullanıcı etkinliğinin (ör. kullanıcının bazı verileri yenilemek için bir düğmeyi tıklaması) iş mantığını işlemeyle sorumludur. Genellikle ViewModel, kullanıcı arayüzünün çağırabileceği işlevleri kullanıma sunarak bu durumu yönetir. Kullanıcı etkinlikleri, kullanıcı arayüzünün doğrudan işleyebileceği kullanıcı arayüzü davranış mantığına da sahip olabilir. Örneğin, farklı bir ekrana gitme veya Snackbar gösterme.

İş mantığı, farklı mobil platformlardaki veya form faktörlerindeki aynı uygulama için aynı kalsa da kullanıcı arayüzü davranış mantığı, bu durumlar arasında farklılık gösterebilecek bir uygulama ayrıntısıdır. Kullanıcı arayüzü katmanı sayfası, bu mantık türlerini aşağıdaki gibi tanımlar:

  • İş mantığı, durum değişiklikleriyle ne yapılacağını ifade eder. Örneğin, ödeme yapma veya kullanıcı tercihlerini saklama. Bu mantık genellikle alan ve veri katmanları tarafından işlenir. Bu kılavuzda, iş mantığını işleyen sınıflar için Architecture Components ViewModel sınıfı, belirli bir görüşe dayalı çözüm olarak kullanılır.
  • Kullanıcı arayüzü davranış mantığı veya kullanıcı arayüzü mantığı, durum değişikliklerinin nasıl gösterileceğini ifade eder. Örneğin, gezinme mantığı veya kullanıcılara mesajların nasıl gösterileceği. Bu mantıkla kullanıcı arayüzü ilgilenir.

Kullanıcı arayüzü etkinliği karar ağacı

Aşağıdaki diyagramda, belirli bir etkinlik kullanım alanını ele almak için en iyi yaklaşımı bulmaya yönelik bir karar ağacı gösterilmektedir. Bu kılavuzun geri kalanında bu yaklaşımlar ayrıntılı olarak açıklanmaktadır.

Etkinlik ViewModel'de başladıysa kullanıcı arayüzü durumunu güncelleyin. Etkinlik kullanıcı arayüzünde başladıysa ve işletme mantığı gerektiriyorsa işletme mantığını ViewModel'e devredin. Etkinlik kullanıcı arayüzünde oluşturulduysa ve kullanıcı arayüzü davranış mantığı gerektiriyorsa kullanıcı arayüzü öğesi durumunu doğrudan kullanıcı arayüzünde değiştirin.
Şekil 1. Etkinlikleri işleme için karar ağacı.

Kullanıcı etkinliklerini işleme

Kullanıcı arayüzü, kullanıcı etkinlikleri bir kullanıcı arayüzü öğesinin durumunu (ör. genişletilebilir bir öğenin durumu) değiştirmekle ilgiliyse bu etkinlikleri doğrudan işleyebilir. Etkinlik, ekrandaki verileri yenileme gibi iş mantığı işlemlerinin yapılmasını gerektiriyorsa ViewModel tarafından işlenmelidir.

Aşağıdaki örnekte, bir kullanıcı arayüzü öğesini (kullanıcı arayüzü mantığı) genişletmek ve ekrandaki verileri yenilemek (iş mantığı) için farklı düğmelerin nasıl kullanıldığı gösterilmektedir:

Görüntüleme sayısı

class LatestNewsActivity : AppCompatActivity() {

    private lateinit var binding: ActivityLatestNewsBinding
    private val viewModel: LatestNewsViewModel by viewModels()

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

        // The expand details event is processed by the UI that
        // modifies a View's internal state.
        binding.expandButton.setOnClickListener {
            binding.expandedSection.visibility = View.VISIBLE
        }

        // The refresh event is processed by the ViewModel that is in charge
        // of the business logic.
        binding.refreshButton.setOnClickListener {
            viewModel.refreshNews()
        }
    }
}

Oluştur

@Composable
fun LatestNewsScreen(viewModel: LatestNewsViewModel = viewModel()) {

    // State of whether more details should be shown
    var expanded by remember { mutableStateOf(false) }

    Column {
        Text("Some text")
        if (expanded) {
            Text("More details")
        }

        Button(
          // The expand details event is processed by the UI that
          // modifies this composable's internal state.
          onClick = { expanded = !expanded }
        ) {
          val expandText = if (expanded) "Collapse" else "Expand"
          Text("$expandText details")
        }

        // The refresh event is processed by the ViewModel that is in charge
        // of the UI's business logic.
        Button(onClick = { viewModel.refreshNews() }) {
            Text("Refresh data")
        }
    }
}

RecyclerView'lerdeki kullanıcı etkinlikleri

Eylem, kullanıcı arayüzü ağacında daha aşağıda (ör. RecyclerView item veya özel View içinde) oluşturulursa ViewModel yine de kullanıcı etkinliklerini işleyen olmalıdır.

Örneğin, NewsActivity sitesindeki tüm haber öğelerinin yer işareti düğmesi içerdiğini varsayalım. ViewModel, yer işareti eklenen haber öğesinin kimliğini bilmelidir. Kullanıcı bir haber öğesine yer işareti koyduğunda RecyclerView bağdaştırıcısı, ViewModel üzerinde bağımlılık gerektirecek olan ViewModel'deki addBookmark(newsId) işlevini çağırmaz. Bunun yerine, ViewModel, NewsItemUiState adlı bir durum nesnesi gösterir. Bu nesne, etkinliği işleme mantığını içerir:

data class NewsItemUiState(
    val title: String,
    val body: String,
    val bookmarked: Boolean = false,
    val publicationDate: String,
    val onBookmark: () -> Unit
)

class LatestNewsViewModel(
    private val formatDateUseCase: FormatDateUseCase,
    private val repository: NewsRepository
)
    val newsListUiItems = repository.latestNews.map { news ->
        NewsItemUiState(
            title = news.title,
            body = news.body,
            bookmarked = news.bookmarked,
            publicationDate = formatDateUseCase(news.publicationDate),
            // Business logic is passed as a lambda function that the
            // UI calls on click events.
            onBookmark = {
                repository.addBookmark(news.id)
            }
        )
    }
}

Bu sayede RecyclerView bağdaştırıcısı yalnızca ihtiyacı olan verilerle çalışır: NewsItemUiState nesnelerinin listesi. Bağdaştırıcı, ViewModel'in tamamına erişemediği için ViewModel'in sunduğu işlevselliği kötüye kullanma olasılığı daha düşüktür. Yalnızca etkinlik sınıfının ViewModel ile çalışmasına izin verdiğinizde sorumlulukları ayırırsınız. Bu, görünümler veya RecyclerView bağdaştırıcılar gibi kullanıcı arayüzüne özgü nesnelerin ViewModel ile doğrudan etkileşime girmemesini sağlar.

Kullanıcı etkinliği işlevleri için adlandırma kuralları

Bu kılavuzda, kullanıcı etkinliklerini işleyen ViewModel işlevleri, işledikleri işleme göre bir fiille adlandırılır. Örneğin: addBookmark(id) veya logIn(username, password).

ViewModel etkinliklerini işleme

ViewModel'dan kaynaklanan kullanıcı arayüzü işlemleri (ViewModel etkinlikleri) her zaman kullanıcı arayüzü durumu güncellemesiyle sonuçlanmalıdır. Bu, Tek Yönlü Veri Akışı ilkelerine uygundur. Yapılandırma değişikliklerinden sonra etkinliklerin yeniden üretilmesini sağlar ve kullanıcı arayüzü işlemlerinin kaybolmamasını garanti eder. İsteğe bağlı olarak, saved state modülünü kullanıyorsanız işlem sonlandırıldıktan sonra da etkinlikleri yeniden oluşturabilirsiniz.

Kullanıcı arayüzü işlemlerini kullanıcı arayüzü durumuna eşlemek her zaman basit bir işlem olmasa da daha basit bir mantıkla sonuçlanır. Düşünce süreciniz, örneğin kullanıcı arayüzünün belirli bir ekrana nasıl gideceğini belirlemekle sona ermemelidir. Daha fazla düşünmeniz ve bu kullanıcı akışını kullanıcı arayüzü durumunuzda nasıl temsil edeceğinizi göz önünde bulundurmanız gerekir. Diğer bir deyişle: Kullanıcı arayüzünün hangi işlemleri yapması gerektiğini düşünmeyin; bu işlemlerin kullanıcı arayüzü durumunu nasıl etkilediğini düşünün.

Örneğin, kullanıcının giriş ekranında oturum açtığı sırada ana ekrana gitme durumunu ele alalım. Bunu kullanıcı arayüzü durumunda aşağıdaki gibi modelleyebilirsiniz:

data class LoginUiState(
    val isLoading: Boolean = false,
    val errorMessage: String? = null,
    val isUserLoggedIn: Boolean = false
)

Bu kullanıcı arayüzü, isUserLoggedIn durumundaki değişikliklere tepki verir ve gerektiğinde doğru hedefe gider:

Görüntüleme sayısı

class LoginViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(LoginUiState())
    val uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()
    /* ... */
}

class LoginActivity : AppCompatActivity() {
    private val viewModel: LoginViewModel by viewModels()

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

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { uiState ->
                    if (uiState.isUserLoggedIn) {
                        // Navigate to the Home screen.
                    }
                    ...
                }
            }
        }
    }
}

Oluştur

class LoginViewModel : ViewModel() {
    var uiState by mutableStateOf(LoginUiState())
        private set
    /* ... */
}

@Composable
fun LoginScreen(
    viewModel: LoginViewModel = viewModel(),
    onUserLogIn: () -> Unit
) {
    val currentOnUserLogIn by rememberUpdatedState(onUserLogIn)

    // Whenever the uiState changes, check if the user is logged in.
    LaunchedEffect(viewModel.uiState)  {
        if (viewModel.uiState.isUserLoggedIn) {
            currentOnUserLogIn()
        }
    }

    // Rest of the UI for the login screen.
}

Etkinlik tüketimi durum güncellemelerini tetikleyebilir

Kullanıcı arayüzünde belirli ViewModel etkinliklerinin kullanılması, diğer kullanıcı arayüzü durumu güncellemelerine neden olabilir. Örneğin, ekranda geçici mesajlar göstererek kullanıcıya bir şey olduğunu bildirmek için mesaj ekranda gösterildiğinde kullanıcı arayüzünün ViewModel'i başka bir durum güncellemesini tetikleyecek şekilde bilgilendirmesi gerekir. Kullanıcı mesajı kapattığında veya zaman aşımından sonra gerçekleşen etkinlik, "kullanıcı girişi" olarak değerlendirilebilir. Bu nedenle, ViewModel bu durumun farkında olmalıdır. Bu durumda, kullanıcı arayüzü durumu şu şekilde modellenebilir:

// Models the UI state for the Latest news screen.
data class LatestNewsUiState(
    val news: List<News> = emptyList(),
    val isLoading: Boolean = false,
    val userMessage: String? = null
)

İş mantığı, kullanıcıya yeni bir geçici mesaj gösterilmesini gerektirdiğinde ViewModel, kullanıcı arayüzü durumunu aşağıdaki gibi günceller:

Görüntüleme sayısı

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

    private val _uiState = MutableStateFlow(LatestNewsUiState(isLoading = true))
    val uiState: StateFlow<LatestNewsUiState> = _uiState

    fun refreshNews() {
        viewModelScope.launch {
            // If there isn't internet connection, show a new message on the screen.
            if (!internetConnection()) {
                _uiState.update { currentUiState ->
                    currentUiState.copy(userMessage = "No Internet connection")
                }
                return@launch
            }

            // Do something else.
        }
    }

    fun userMessageShown() {
        _uiState.update { currentUiState ->
            currentUiState.copy(userMessage = null)
        }
    }
}

Oluştur

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

    var uiState by mutableStateOf(LatestNewsUiState())
        private set

    fun refreshNews() {
        viewModelScope.launch {
            // If there isn't internet connection, show a new message on the screen.
            if (!internetConnection()) {
                uiState = uiState.copy(userMessage = "No Internet connection")
                return@launch
            }

            // Do something else.
        }
    }

    fun userMessageShown() {
        uiState = uiState.copy(userMessage = null)
    }
}

ViewModel'in, kullanıcı arayüzünün ekranda mesajı nasıl gösterdiğini bilmesi gerekmez. Yalnızca gösterilmesi gereken bir kullanıcı mesajı olduğunu bilir. Geçici mesaj gösterildikten sonra kullanıcı arayüzünün ViewModel'i bu konuda bilgilendirmesi gerekir. Bu da userMessage özelliğini temizlemek için başka bir kullanıcı arayüzü durumu güncellemesine neden olur:

Görüntüleme sayısı

class LatestNewsActivity : AppCompatActivity() {
    private val viewModel: LatestNewsViewModel by viewModels()

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

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { uiState ->
                    uiState.userMessage?.let {
                        // TODO: Show Snackbar with userMessage.

                        // Once the message is displayed and
                        // dismissed, notify the ViewModel.
                        viewModel.userMessageShown()
                    }
                    ...
                }
            }
        }
    }
}

Oluştur

@Composable
fun LatestNewsScreen(
    snackbarHostState: SnackbarHostState,
    viewModel: LatestNewsViewModel = viewModel(),
) {
    // Rest of the UI content.

    // If there are user messages to show on the screen,
    // show it and notify the ViewModel.
    viewModel.uiState.userMessage?.let { userMessage ->
        LaunchedEffect(userMessage) {
            snackbarHostState.showSnackbar(userMessage)
            // Once the message is displayed and dismissed, notify the ViewModel.
            viewModel.userMessageShown()
        }
    }
}

İleti geçici olsa da kullanıcı arayüzü durumu, her an ekranda gösterilenlerin doğru bir temsilidir. Kullanıcı mesajı ya gösterilir ya da gösterilmez.

Tüketilen etkinlikler durum güncellemelerini tetikleyebilir bölümünde, kullanıcı mesajlarını ekranda göstermek için kullanıcı arayüzü durumunu nasıl kullandığınız açıklanmaktadır. Geçiş etkinlikleri de Android uygulamalarındaki yaygın etkinlik türlerinden biridir.

Etkinlik, kullanıcı bir düğmeye dokunduğu için kullanıcı arayüzünde tetiklenirse kullanıcı arayüzü, gezinme denetleyicisini çağırarak veya etkinliği uygun şekilde çağırana composable olarak sunarak bu durumu ele alır.

Görüntüleme sayısı

class LoginActivity : AppCompatActivity() {

    private lateinit var binding: ActivityLoginBinding
    private val viewModel: LoginViewModel by viewModels()

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

        binding.helpButton.setOnClickListener {
            navController.navigate(...) // Open help screen
        }
    }
}

Oluştur

@Composable
fun LoginScreen(
    onHelp: () -> Unit, // Caller navigates to the right screen
    viewModel: LoginViewModel = viewModel()
) {
    // Rest of the UI

    Button(onClick = onHelp) {
        Text("Get help")
    }
}

Veri girişi, gezinmeden önce bazı işletme mantığı doğrulaması gerektiriyorsa ViewModel'ın bu durumu kullanıcı arayüzüne göstermesi gerekir. Kullanıcı arayüzü bu durum değişikliğine tepki verir ve buna göre gezinir. ViewModel etkinliklerini işleme bölümünde bu kullanım alanı ele alınmaktadır. Benzer bir kodu aşağıda bulabilirsiniz:

Görüntüleme sayısı

class LoginActivity : AppCompatActivity() {
    private val viewModel: LoginViewModel by viewModels()

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

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { uiState ->
                    if (uiState.isUserLoggedIn) {
                        // Navigate to the Home screen.
                    }
                    ...
                }
            }
        }
    }
}

Oluştur

@Composable
fun LoginScreen(
    onUserLogIn: () -> Unit, // Caller navigates to the right screen
    viewModel: LoginViewModel = viewModel()
) {
    Button(
        onClick = {
            // ViewModel validation is triggered
            viewModel.login()
        }
    ) {
        Text("Log in")
    }
    // Rest of the UI

    val lifecycle = LocalLifecycleOwner.current.lifecycle
    val currentOnUserLogIn by rememberUpdatedState(onUserLogIn)
    LaunchedEffect(viewModel, lifecycle)  {
        // Whenever the uiState changes, check if the user is logged in and
        // call the `onUserLogin` event when `lifecycle` is at least STARTED
        snapshotFlow { viewModel.uiState }
            .filter { it.isUserLoggedIn }
            .flowWithLifecycle(lifecycle)
            .collect {
                currentOnUserLogIn()
            }
    }
}

Yukarıdaki örnekte, mevcut hedef olan Giriş, geri yığında tutulmayacağından uygulama beklendiği gibi çalışır. Kullanıcılar geri düğmesine basarsa bu sayfaya geri dönemez. Ancak bu durumun yaşanabileceği durumlarda çözüm için ek mantık gerekir.

Bir ViewModel, A ekranından B ekranına gezinme etkinliği oluşturan bir durum ayarladığında ve A ekranı gezinme eski yığınında tutulduğunda B ekranına otomatik olarak ilerlemeyi engellemek için ek mantık kullanmanız gerekebilir. Bunu uygulamak için kullanıcı arayüzünün diğer ekrana gidilip gidilmeyeceğini dikkate alıp almayacağını belirten ek bir durum olması gerekir. Normalde bu durum kullanıcı arayüzünde tutulur. Bunun nedeni, gezinme mantığının ViewModel'in değil, kullanıcı arayüzünün sorumluluğunda olmasıdır. Bunu göstermek için aşağıdaki kullanım alanını ele alalım.

Uygulamanızın kayıt akışında olduğunuzu varsayalım. Doğum tarihi doğrulama ekranında, kullanıcı bir tarih girdiğinde bu tarih, kullanıcı "Devam" düğmesine dokunduğunda ViewModel tarafından doğrulanır. ViewModel doğrulama mantığını veri katmanına devreder. Tarih geçerliyse kullanıcı bir sonraki ekrana yönlendirilir. Ek bir özellik olarak, kullanıcılar bazı verileri değiştirmek isterlerse farklı kayıt ekranları arasında ileri ve geri gidebilirler. Bu nedenle, kayıt akışındaki tüm hedefler aynı geri yığında tutulur. Bu şartlar göz önünde bulundurulduğunda, bu ekranı aşağıdaki gibi uygulayabilirsiniz:

Görüntüleme sayısı

// Key that identifies the `validationInProgress` state in the Bundle
private const val DOB_VALIDATION_KEY = "dobValidationKey"

class DobValidationFragment : Fragment() {

    private var validationInProgress: Boolean = false
    private val viewModel: DobValidationViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val binding = // ...
        validationInProgress = savedInstanceState?.getBoolean(DOB_VALIDATION_KEY) ?: false

        binding.continueButton.setOnClickListener {
            viewModel.validateDob()
            validationInProgress = true
        }

        viewLifecycleOwner.lifecycleScope.launch {
            viewModel.uiState
                .flowWithLifecycle(viewLifecycleOwner.lifecycle)
                .collect { uiState ->
                    // Update other parts of the UI ...

                    // If the input is valid and the user wants
                    // to navigate, navigate to the next screen
                    // and reset `validationInProgress` flag
                    if (uiState.isDobValid && validationInProgress) {
                        validationInProgress = false
                        navController.navigate(...) // Navigate to next screen
                    }
                }
        }

        return binding
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putBoolean(DOB_VALIDATION_KEY, validationInProgress)
    }
}

Oluştur

class DobValidationViewModel(/* ... */) : ViewModel() {
    var uiState by mutableStateOf(DobValidationUiState())
        private set
}

@Composable
fun DobValidationScreen(
    onNavigateToNextScreen: () -> Unit, // Caller navigates to the right screen
    viewModel: DobValidationViewModel = viewModel()
) {
    // TextField that updates the ViewModel when a date of birth is selected

    var validationInProgress by rememberSaveable { mutableStateOf(false) }

    Button(
        onClick = {
            viewModel.validateInput()
            validationInProgress = true
        }
    ) {
        Text("Continue")
    }
    // Rest of the UI

    /*
     * The following code implements the requirement of advancing automatically
     * to the next screen when a valid date of birth has been introduced
     * and the user wanted to continue with the registration process.
     */

    if (validationInProgress) {
        val lifecycle = LocalLifecycleOwner.current.lifecycle
        val currentNavigateToNextScreen by rememberUpdatedState(onNavigateToNextScreen)
        LaunchedEffect(viewModel, lifecycle) {
            // If the date of birth is valid and the validation is in progress,
            // navigate to the next screen when `lifecycle` is at least STARTED,
            // which is the default Lifecycle.State for the `flowWithLifecycle` operator.
            snapshotFlow { viewModel.uiState }
                .filter { it.isDobValid }
                .flowWithLifecycle(lifecycle)
                .collect {
                    validationInProgress = false
                    currentNavigateToNextScreen()
                }
        }
    }
}

Doğum tarihi doğrulama, ViewModel'in sorumlu olduğu bir iş mantığıdır. ViewModel, çoğu zaman bu mantığı veri katmanına devreder. Kullanıcıyı bir sonraki ekrana yönlendirme mantığı, kullanıcı arayüzü mantığıdır. Çünkü bu şartlar, kullanıcı arayüzü yapılandırmasına bağlı olarak değişebilir. Örneğin, aynı anda birden fazla kayıt adımı gösteriyorsanız tablette otomatik olarak başka bir ekrana geçmek istemeyebilirsiniz. Yukarıdaki kodda yer alan validationInProgress değişkeni bu işlevi uygular ve doğum tarihi geçerli olduğunda ve kullanıcı bir sonraki kayıt adımına devam etmek istediğinde kullanıcı arayüzünde otomatik olarak gezinilip gezinilmeyeceğini belirler.

Diğer kullanım alanları

Kullanıcı arayüzü etkinliği kullanım alanınızın, kullanıcı arayüzü durumu güncellemeleriyle çözülemeyeceğini düşünüyorsanız uygulamanızdaki veri akışını yeniden gözden geçirmeniz gerekebilir. Aşağıdaki ilkeleri göz önünde bulundurun:

  • Her sınıf, sorumluluğunda olanı yapmalı, daha fazlasını değil. Kullanıcı arayüzü; gezinme çağrıları, tıklama etkinlikleri ve izin isteklerini alma gibi ekrana özgü davranış mantığından sorumludur. ViewModel, iş mantığını içerir ve hiyerarşinin alt katmanlarındaki sonuçları kullanıcı arayüzü durumuna dönüştürür.
  • Etkinliğin kaynağını düşünün. Bu kılavuzun başında sunulan karar ağacını izleyin ve her sınıfın kendi sorumluluğunda olanı işlemesini sağlayın. Örneğin, etkinlik kullanıcı arayüzünden kaynaklanıyorsa ve bir gezinme etkinliğiyle sonuçlanıyorsa bu etkinlik kullanıcı arayüzünde işlenmelidir. Bazı mantıklar ViewModel'e devredilebilir ancak etkinliğin işlenmesi tamamen ViewModel'e devredilemez.
  • Birden fazla tüketiciniz varsa ve etkinliğin birden fazla kez tüketilmesinden endişeleniyorsanız uygulama mimarinizi yeniden gözden geçirmeniz gerekebilir. Aynı anda birden fazla tüketicinin olması, tam olarak bir kez teslim edildi sözleşmesinin garanti edilmesini son derece zorlaştırır. Bu nedenle, karmaşıklık ve ince davranış miktarı artar. Bu sorunu yaşıyorsanız endişelerinizi kullanıcı arayüzü ağacınızda yukarıya taşıyabilirsiniz. Hiyerarşide daha yukarıda farklı bir kapsamlı öğeye ihtiyacınız olabilir.
  • Durumun ne zaman kullanılması gerektiğini düşünün. Belirli durumlarda, uygulama arka plandayken durumu tüketmeye devam etmek istemeyebilirsiniz. Örneğin, Toast göstermek. Bu gibi durumlarda, kullanıcı arayüzü ön plandayken durumu kullanmayı düşünebilirsiniz.

Örnekler

Aşağıdaki Google örneklerinde, kullanıcı arayüzü katmanındaki kullanıcı arayüzü etkinlikleri gösterilmektedir. Bu kılavuzun nasıl uygulandığını görmek için aşağıdaki kaynakları inceleyin: