Kullanıcı arayüzü etkinlikleri

Kullanıcı arayüzü etkinlikleri, kullanıcı arayüzü tarafından veya kullanıcı arayüzü katmanında işlenmesi gereken işlemlerdir veya ViewModel'e göre belirlenir. En yaygın etkinlik türü kullanıcı etkinlikleridir. Kullanıcı uygulamayla etkileşim kurarak kullanıcı etkinlikleri oluşturur (örneğin, hareketler oluşturarak ekranda görebilirsiniz. Kullanıcı arayüzü, daha sonra bu etkinlikleri onClick() dinleyicisi gibi geri çağırmalar.

ViewModel normalde bir modelin iş mantığını ele almaktan belirli bir kullanıcı etkinliği (örneğin, kullanıcı bir öğeyi yenileyerek dışı verilerdir. ViewModel genellikle bunu kullanıcı arayüzünün çağrısına bir tıklama URL'si eklemeniz gerekir. Kullanıcı etkinlikleri, kullanıcı arayüzünün işleyebileceği kullanıcı arayüzü davranış mantığına da sahip olabilir. doğrudan (örneğin farklı bir ekrana giderek veya bir Snackbar.

Aynı uygulamanın farklı mobil cihazlarda iş mantığı aynı kalırken platformlar veya form faktörleriyse, kullanıcı arayüzü davranış mantığı bir uygulama ayrıntısıdır. bu durumlarda farklı olabilir. Kullanıcı arayüzü katmanı page, bu mantığı şöyle olur:

  • İş mantığı, durum değişiklikleriyle ne yapılacağını ifade eder. Örneğin, ödeme yapma veya kullanıcı tercihlerini depolama. Alan ve veri katmanları bu mantığın üstesinden gelebilir. Bu kılavuzda, Mimari Bileşenleri ViewModel sınıfı bir iş mantığını ele alan sınıflar için düşünceli bir çözüm sunar.
  • Kullanıcı arayüzü davranış mantığı veya kullanıcı arayüzü mantığı, nasıl gösterilir? durumunu ifade eder. (ör. gezinme mantığı veya mesajların kullanıcıya nasıl gösterileceği) hakkında daha fazla bilgi edinmenizi sağlar. İlgili içeriği oluşturmak için kullanılan Kullanıcı arayüzü bu mantığı işler.
ziyaret edin.

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

Aşağıdaki şemada en iyi yaklaşımı bulmak için bir karar ağacı örneğin ele alınır. Bu kılavuzun geri kalanında, ayrıntılı olarak inceleyeceğiz.

Etkinlik ViewModel'de gerçekleştiyse kullanıcı arayüzü durumunu güncelleyin. Eğer
    Etkinlik, kullanıcı arayüzünde oluşturulduysa ve iş mantığı gerektirir. Daha sonra
    iş mantığını ViewModel'e yansıtır. Etkinlik kullanıcı arayüzünde ve
    kullanıcı arayüzü davranış mantığı gerektirir, ardından kullanıcı arayüzü öğesi durumunu doğrudan
    Kullanıcı arayüzü.
Şekil 1. Etkinlikleri ele almak için karar ağacı.

Kullanıcı etkinliklerini yönetme

Bu etkinlikler, durumu (örneğin, genişletilebilir bir öğenin durumu). Etkinlik ekrandaki verileri yenilemek gibi bir iş mantığı yürütmeyi gerektiriyorsa ViewModel tarafından işlenmesi gerekir.

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

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")
        }
    }
}

RecyclerViews'taki kullanıcı etkinlikleri

İşlem, RecyclerView gibi kullanıcı arayüzü ağacında daha aşağılarda gerçekleşiyorsa öğe veya özel bir View ise, taşıma işlemini yapan kullanıcı ViewModel olmalıdır. etkinlikler.

Örneğin, NewsActivity kaynağından gelen tüm haber öğelerinin yer işareti içerdiğini varsayalım düğmesini tıklayın. ViewModel uygulamasının, yer işareti koyulan haber öğesinin kimliğini bilmesi gerekir. Zaman kullanıcı bir haber öğesine yer işareti koyduğunda RecyclerView bağdaştırıcısı ViewModel değerindeki açıkta kalan addBookmark(newsId) işlevi ViewModel bağımlılığı var. Bunun yerine, ViewModel bir durum nesnesi gösterir NewsItemUiState; etkinlik:

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 şekilde, RecyclerView bağdaştırıcısı yalnızca ihtiyaç duyduğu verilerle çalışır: NewsItemUiState nesne içeren liste. Adaptör, tüm cihazlara ViewModel'in gösterdiği performansı kötüye kullanma olasılığını ViewModel. Yalnızca etkinlik sınıfının ViewModel ile çalışmasına izin verdiğinizde size zaman kazandırabilir. Bu, görünümler gibi kullanıcı arayüzüne özgü nesnelerin veya RecyclerView bağdaştırıcıları doğrudan ViewModel ile etkileşime girmez.

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

Bu kılavuzda, kullanıcı etkinliklerini işleyen ViewModel işlevleri fiil, gerçekleştirdikleri işleme göre değişir. Örneğin: addBookmark(id) veya logIn(username, password).

ViewModel etkinliklerini işleme

ViewModel etkinliklerinden (ViewModel etkinlikleri) kaynaklanan kullanıcı arayüzü işlemleri her zaman kullanıcı arayüzü durumunun güncellenmesine neden olur. Bu Tek Yönlü Veri ilkelerine uygundur. Akış. Etkinliklerin ve kullanıcı arayüzü işlemlerinin kaybolmayacağını garanti eder. İsteğe bağlı olarak, Ayrıca, kaydedilen kayıtlı öğe sayısını kullanıyorsanız işlem ölümünden sonra durum modülü'ne gidin.

Kullanıcı arayüzü işlemlerinin kullanıcı arayüzü durumuyla eşlenmesi her zaman basit bir süreç değildir ancak daha basit bir mantığa dönüştü. Düşünce süreciniz, projenizin başarısını nasıl belirleyeceğinize karar vermekle kullanıcı arayüzünün belirli bir ekrana gitmesini sağlayabilirsiniz. Birlikte çalıştığınız ve kullanıcı arayüzü durumunuzda bu kullanıcı akışını nasıl yansıtacağınızı düşünün. diğer bir deyişle, kullanıcı arayüzünün hangi işlemleri yapması gerektiğini düşünmeyin. CANNOT TRANSLATE kullanıcı arayüzünün durumunu nasıl etkilediğini açıklayacağım.

Örneğin, kullanıcı açıkken ana ekrana gitmeyi kullanıcı giriş yapmıştır. 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 durumunda yapılan değişikliklere tepki verir ve Gerektiğinde doğru hedefi kullanın:

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.
}

Etkinlikleri kullanmak durum güncellemelerini tetikleyebilir

Kullanıcı arayüzünde belirli ViewModel etkinliklerini kullanmak başka kullanıcı arayüzü durumlarıyla sonuçlanabilir güncellemelerine göz atın. Örneğin, geçici mesajları ekranda gösterirken kullanıcı bir şey olduğunu anlarsa, kullanıcı arayüzünün ViewModel'e Mesaj ekranda gösterildiğinde başka bir durum güncellemesini tetikleyebilir. İlgili içeriği oluşturmak için kullanılan Kullanıcı iletiyi tükettiğinde (iletiyi kapatarak veya sonra) "kullanıcı girişi" olarak değerlendirilebilir. ve bu nedenle ViewModel bunu dikkate almalıdır. Bu durumda, kullanıcı arayüzü durumu modellendiği gibi:

// 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
)

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

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 ekran; yalnızca gösterilmesi gereken bir kullanıcı mesajı olduğunu bilir. Bir kez geçici mesaj gösterildiğine göre, kullanıcı arayüzünün ViewModel'e Bu da başka bir kullanıcı arayüzü durumu güncellemesinin userMessage özelliğini temizlemesine 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 ziyarette ekranda görünenlerin güvenilir bir şekilde temsil edilmesi bir nokta olabilir. Kullanıcı mesajı gösteriliyor veya gösterilmiyor.

Tüketme etkinlikleri durum güncellemelerini tetikleyebilir bölümünde, tıklayın. Gezinme etkinlikleri de Android uygulamalarında yaygın olarak kullanılan bir etkinlik türüdür.

Etkinlik, kullanıcı bir düğmeye dokunduğu için kullanıcı arayüzünde tetiklenirse kullanıcı arayüzü bunun için gezinme denetleyicisini çağırır veya etkinliği görüntüler composable'a uygun şekilde iletebilir.

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 navigasyondan önce birtakım iş mantığı doğrulaması gerektiriyorsa, ViewModel'in bu durumu kullanıcı arayüzüne sunması gerekir. Kullanıcı arayüzü o durum değişikliğine göre hareket etmeniz gerekir. Handle ViewModel etkinlikleri bölümündeki öğrenebilirsiniz. Benzer bir kod aşağıda verilmiştir:

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, uygulama beklendiği gibi çalışır. Bunun nedeni, mevcut hedefin Giriş, arka yığında saklanmaz. Kullanıcılar, geri tuşuna basın. Ancak bu gibi durumlarda çözüm, ek mantık gerektirir.

ViewModel, ekrandan bir gezinme etkinliği oluşturan bir durum belirlediğinde B ekranı için A ekranı ve A ekranı gezinme geri yığınında tutulur. B'ye otomatik olarak ilerlemeye devam etmemek için ek bir mantık vardır. Bunu uygulamak için kullanıcı arayüzünün diğer ekrana gitmenizi öneririz. Normalde bu eyalet çünkü Gezinme mantığı ViewModel'le değil, kullanıcı arayüzüyle ilgilidir. Bunu göstermek için aşağıdaki kullanım alanını inceleyelim.

Uygulamanızın kayıt akışında olduğunuzu varsayalım. tarihinde doğum doğrulama ekranında, kullanıcı bir tarih girdiğinde tarih kullanıcı "Devam" düğmesine dokunduğunda ViewModel'i düğmesini tıklayın. ViewModel doğrulama mantığı için veri katmanına yetki verir. Tarih geçerliyse ve kullanıcı bir sonraki ekrana geçer. Ek bir özellik olarak kullanıcılar geri dönüp geçiş yapmak istediklerinde kullanmaları için farklı kayıt ekranları arasında bazı verilerdir. Bu nedenle, kayıt akışındaki tüm hedefler görebilirsiniz. Bu gereksinimler göz önünde bulundurulduğunda, bu ekranı şu şekilde:

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ğrulaması, ViewModel'in şu iş mantığıdır: çok önemli. Çoğu zaman ViewModel bu mantığı veri katmanından yararlanın. Kullanıcıyı sonraki ekrana taşıma mantığı Bu gereksinimler kullanıcı arayüzüne bağlı olarak değişebileceğinden, kullanıcı arayüzü mantığıdır. yapılandırma. Örneğin, her ekip üyesine otomatik olarak aynı anda birden çok kayıt adımını gösteriyorsanız, tablette anlamına gelir. Yukarıdaki kodda bulunan validationInProgress değişkeni olup olmadığını kontrol eder ve kullanıcı arayüzünde gezinme doğum tarihi geçerli olduğunda ve kullanıcı istediği zaman otomatik olarak bağlantısını tıklayın.

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ülemediğini düşünüyorsanız uygulamanızdaki veri akışını yeniden değerlendirmeniz gerekebilir. Aşağıdakileri göz önünde bulundurun ilkeleri:

  • Her sınıf daha fazla değil, sorumlu olduğu işi yapmalıdır. Kullanıcı arayüzü gezinme çağrıları, tıklama izleme gibi ekrana özgü davranış mantığının ve izin isteklerinin alınmasıyla ilgili bilgiler edinebilirsiniz. ViewModel, işletme verilerini içerir. mantığın temelini oluşturur ve hiyerarşinin alt katmanlarındaki sonuçları kullanıcı arayüzüne dönüştürür. durumu.
  • Etkinliğin nereden gerçekleştiğini düşünün. Kararı uygulayın başlangıcında verilen ağaç ve her bir sorumluluklarının bilincinde olmalarına yardımcı olur. Örneğin, kullanıcı arayüzünden oluşturulur ve gezinme etkinliği ile sonuçlanır; ardından bu etkinlik kullanıcı arayüzünde işlenmesi gerekir. Bazı mantık değerleri ViewModel'e devredilebilir, Ancak etkinliğin işlenmesi tamamen ViewModel'e devredilemez.
  • Birden fazla tüketiciniz varsa ve etkinliğin gerçekleşeceği konusunda endişeleriniz varsa veya birden fazla kez tüketiliyorsa uygulama mimarinizi yeniden gözden geçirmeniz gerekebilir. Birden fazla tüketicinin aynı anda olması tam olarak bir kez yayınlanır. çok zor bir iş haline gelecektir. Bu da, sözleşmenin karmaşıklık ve karmaşık davranışlar patlıyor. Bu sorunu yaşıyorsanız bu endişeleri kullanıcı arayüzü ağacınızda yukarıya taşıyabilirsiniz; bir ekip üyesine hiyerarşinin daha üst sıralarında bulunan farklı bir varlık.
  • Durumun ne zaman tüketilmesi gerektiğini düşünün. Bazı durumlarda, uygulama bu moddayken tüketim durumunu arka plan (örneğin, Toast gösteriliyor). Bu tür durumlarda durumu gösterir.

Örnekler

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

ziyaret edin.