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
显示错误alert
、toast
或snackbar
?
没有。我在视图模型中创建了一个登录方法,该方法调用一个组件进行登录。该组件需要一个侦听器/接口,它具有完整的错误回调。一切都发生在视图模型中,但我认为我需要回调片段,以便向用户显示错误。我读到这不是正确的 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
发送任何error
或success
事件
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 吗? @lcjChannel
属于kotlin
coroutines
,它们在java
中不可用。
对于您在 java 中的用例,您可以为您的登录状态创建不同的实时数据并单独观察它们,因为您必须传递您的成功结果或错误。如果没有任何结果,您也可以使用 enum
。以上是关于Android 调用组件 w/listener 或让 viewmodel 调用组件与片段通信的主要内容,如果未能解决你的问题,请参考以下文章