Android 调用组件 w/listener 或让 viewmodel 调用组件与片段通信

Posted

技术标签:

【中文标题】Android 调用组件 w/listener 或让 viewmodel 调用组件与片段通信【英文标题】:Android call component w/listener or have the viewmodel call the component communicate withe the fragment 【发布时间】:2021-12-04 02:54:34 【问题描述】:

我正在使用视图模型从片段调用登录方法。我一直在其他领域使用很多回调,但read 使用 MVVM 我不应该以这种方式在片段和视图模型之间进行通信。 android 文档似乎也使用了LiveData。为什么可以为诸如 recyclerview 的适配器之类的组件而不是从视图模型调用的其他组件设置侦听器。

登录组件位于 Splash 片段中。我应该将它作为视图模型之外的组件调用并利用侦听器吗?

我遇到了一个错误,想向用户提供反馈。我:

    将组件从视图模型中取出,直接从片段中调用 将组件留在视图模型中并通过使用 livedata 向片段/用户提供反馈? 将登录组件留在视图模型中,只使用回调/侦听器

更新

感谢您的反馈。我会提供更多细节。我正在使用Java,仅供参考。我专注于第一次运行过程,这与显示列表或详细数据不同。所以,我需要做很多事情才能让应用程序准备好首次使用,并且我需要大量反馈以防出现问题。我创建了一个启动画面,并有办法记录过程中的步骤,这样我就可以知道什么时候出了问题,哪里出了问题。用户最终看到最后一条消息,但完整的消息被保存。

我一直在为调用添加一个侦听器并为组件添加一个接口。如果你没有猜到,我有点新手,但这似乎确实是一个很好的模式,但我一直在阅读这不是在 Fragment 和 ViewModel 之间进行通信的正确方式。

示例,来自 SplashFragment.java:

viewModel.signIn(getActivity(), getAuthHelperSignInCallback());

在 SplashViewModel.java 中:

public void signIn (Activity activity, AuthHelper.AuthHelperSignInListener listener) 
    
     AuthHelper authHelper = new AuthHelper(activity.getApplication());
     authHelper.signIn(activity,listener);

在 AuthHelper.java 我有一个接口:

public interface AuthHelperSignInListener 
     void onSuccess(IAuthenticationResult iAuthenticationResult);
     void onCancel();
     void onError(MsalException e);

使用这种方法我可以得到我需要的信息,所以如果我不应该在这样的片段中使用回调/侦听器,还有什么替代方法?

【问题讨论】:

我在其他领域使用了很多回调,但读到使用 MVVM 我不应该以这种方式在片段和视图模型之间进行通信。 你是什么意思这边走? 您是否在问,触发 UI 事件的最佳做法是什么,例如从viewmodel 显示错误alerttoastsnackbar 没有。我在视图模型中创建了一个登录方法,该方法调用一个组件进行登录。该组件需要一个侦听器/接口,它具有完整的错误回调。一切都发生在视图模型中,但我认为我需要回调片段,以便向用户显示错误。我读到这不是正确的 MVVM 方法,我应该使用 LiveData w/observers。如果我有几个组件,似乎我最终可能会遇到很多错误观察者。 我假设我不应该从 ViewModel 调用 UI 组件。 【参考方案1】:

您可以使用channel 将这些事件发送到您的活动或片段,并相应地触发 UI 操作。 Channel 属于 kotlinx.coroutines.channels.Channel 包。

首先,使用sealed class 在您的viewModel 类中创建这些事件。

sealed class SignInEvent
    data class ShowError(val message: String) : SignInEvent()
    data class ShowLoginSuccess(val message: String) : SignInEvent()

viewModel 中定义一个channel 变量。

private val signInEventChannel = Channel<SignInEvent>()
   
// below flow will be used to collect these events inside activity/fragment
val signInEvent = signInEventChannel.receiveAsFlow()

现在您可以使用定义的事件通道从viewModel 发送任何errorsuccess 事件

fun onSignIn() 
     try 
          //your sign in logic
          
          // on success
          signInEventChannel.send(SignInEvent.ShowLoginSuccess("Login successful"))
      catch(e: Exception)
          //on getting an error.
          signInEventChannel.send(SignInEvent.ShowError("There is an error logging in"))
              

现在您可以监听这些事件并相应地触发任何 UI 操作,例如显示 toast 或小吃店

活动中

lifecycleScope.launchWhenStarted 
        activityViewModel.signInEvent.collect  event ->
                when (event) 
                    //ActivityViewModel is your viewmodel's class name
                    is ActivityViewModel.SignInEvent.ShowError-> 
                        Snackbar.make(binding.root, event.message, Snackbar.LENGTH_SHORT)
                            .show()
                    
                    is ActivityViewModel.SignInEvent.ShowLoginSuccess-> 
                        Snackbar.make(binding.root, event.message, Snackbar.LENGTH_SHORT)
                            .show()
                    
                

在片段中

viewLifecycleOwner.lifecycleScope.launchWhenStarted 
        fragmentViewModel.signInEvent.collect  event ->
                when (event) 
                    is FragmentViewModel.SignInEvent.ShowError-> 
                        Snackbar.make(requireView(), event.message, Snackbar.LENGTH_SHORT)
                            .show()
                    
                    is FragmentViewModel.SignInEvent.ShowLoginSuccess-> 
                        Snackbar.make(requireView(), event.message, Snackbar.LENGTH_SHORT)
                            .show()
                    
                

【讨论】:

谢谢,普拉文。我碰巧使用的是 Java,我应该在原帖中说明。我也会在 java 中使用 Channel 吗? @lcj Channel 属于kotlin coroutines,它们在java 中不可用。 对于您在 java 中的用例,您可以为您的登录状态创建不同的实时数据并单独观察它们,因为您必须传递您的成功结果或错误。如果没有任何结果,您也可以使用 enum

以上是关于Android 调用组件 w/listener 或让 viewmodel 调用组件与片段通信的主要内容,如果未能解决你的问题,请参考以下文章

android 四大组件值Service 绑定式服务

android 文本输入框里面如何添加别的组件???

Android知识合集

Android检测是不是在设计模式下调用了构造函数?

在Android中销毁布局时覆盖的方法

如何在android的代码中调用某个组件属性