【问题标题】:(Kotlin) Communcation between Fragments in MVVM(Kotlin) MVVM 中 Fragments 之间的通信
【发布时间】:2020-07-13 06:01:33
【问题描述】:

我正在尝试理解 MVVM 的概念,但在这种情况下,我很难理解如何在模型类和 UI(片段)之间进行通信。

这是(糟糕,要注意)代码:

LoginFragment.kt

class LoginFragment: Fragment(), AuthListener {

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val binding = DataBindingUtil.inflate<CredentialsLoginFragmentBinding>(
        inflater,
        R.layout.credentials_login_fragment,
        container,
        false
    )
    val viewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java)
    val view: View = binding.root
    val registerButton: Button = view.findViewById(R.id.register_button)
    binding.viewModel = viewModel
    viewModel.authListener = this

    registerButton.setOnClickListener {
        val transaction: FragmentTransaction? = fragmentManager?.beginTransaction()
        transaction?.replace(R.id.fragment_container, SignupFragment())?.commit()
    }
    return view
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val constraintRoot: MotionLayout = view.findViewById(R.id.sign_in_root)
    ActivityUtils().switchLayoutAnimationKeyboard(constraintRoot = constraintRoot)

}

override fun onStarted() {
    Toast.makeText(context, "Started", Toast.LENGTH_SHORT).show()
}

override fun onSuccess() {
    Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
}

override fun onError(message: String) {
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}}

LoginViewModel.kt

class LoginViewModel: ViewModel(){
var username: String? = null
var password: String? = null
var isCredentialsValid: Boolean = false
var authListener: AuthListener? = null
private val context: Context? = null

fun onLoginButtonClicked(view: View){

    if(username.isNullOrEmpty() || password.isNullOrEmpty()){
        authListener?.onError("Invalid username or password")
        isCredentialsValid = false
        return
    }

    if(!username.isNullOrEmpty() && password!!.length >= 8){
        isCredentialsValid = true
        authListener?.onSuccess()
    }else{
        authListener?.onError("Invalid")
    }
}}

现在假设我输入了用户名和密码并且都符合条件。现在我想,当我点击“登录”按钮时,当前片段被一个菜单片段替换,例如。

我怎样才能实现这样的目标?我试图从 ViewModel 类中替换,但这不起作用。 我应该从 VM 类中获取“isCredentialsValid”的结果并在 LoginFragment 类中做出相应的响应吗?

谢谢。

【问题讨论】:

    标签: android android-studio kotlin mvvm


    【解决方案1】:

    您必须使用实时数据将数据从 viewModel 更新到 view。我将发布代码,但请确保您需要了解 LiveData 的概念。

    LoginViewModel.kt

    class LoginViewModel: ViewModel(){
    var username: String? = null
    var password: String? = null
    var isCredentialsValid: Boolean = false
    var authListener: AuthListener? = null
    private val context: Context? = null
    // LiveData to udpate the UI
    private val _isValidCredential = MutableLiveData<Boolean>()
    val isValidCredential: LiveData<Boolean> = _isValidCredential
    
    
    fun onLoginButtonClicked(view: View){
    
    if(username.isNullOrEmpty() || password.isNullOrEmpty()){
        authListener?.onError("Invalid username or password")
        isCredentialsValid = false
        return
    }
    
    if(!username.isNullOrEmpty() && password!!.length >= 8){
        isCredentialsValid = true
    // to update the value of live data wherever you need
        _isValidCredential.value = true
        authListener?.onSuccess()
    }else{
        authListener?.onError("Invalid")
    // to update the value of live data wherever you need
        _isValidCredential.value = false
    }
    }
    }
    

    你的片段应该是

    LoginFragment.kt

    class LoginFragment: Fragment(), AuthListener {
    
    override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View? {
    val binding = DataBindingUtil.inflate<CredentialsLoginFragmentBinding>(
        inflater,
        R.layout.credentials_login_fragment,
        container,
        false
    )
    val viewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java)
    val view: View = binding.root
    val registerButton: Button = view.findViewById(R.id.register_button)
    binding.viewModel = viewModel
    viewModel.authListener = this
    
    // This is the way you need to observe the value
    viewModel.isValidCredential.observe(viewLifecycleOwner, Observer {
    
    if(it){
    // do your navigation stuff here
    }else{
    // do your stuff if not valid credential
    }
    
        })
    
    registerButton.setOnClickListener {
        val transaction: FragmentTransaction? = 
    fragmentManager?.beginTransaction()
        transaction?.replace(R.id.fragment_container, SignupFragment())?.commit()
    }
    return view
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val constraintRoot: MotionLayout = view.findViewById(R.id.sign_in_root)
    ActivityUtils().switchLayoutAnimationKeyboard(constraintRoot = constraintRoot)
    
    }
    
    override fun onStarted() {
    Toast.makeText(context, "Started", Toast.LENGTH_SHORT).show()
    }
    
    override fun onSuccess() {
    Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
    }
    
    override fun onError(message: String) {
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
    }}
    

    【讨论】:

    • @BenAndJerrys 支持答案可能对其他人参考有用
    • 我觉得我太新了
    【解决方案2】:

    从视图模型返回 UI 的典型方式是使用 livedata。在您的 LoginViewModel 中,您可以将 livedata 设置为 truefalse。在您的视图LoginFragment.kt 中,您将有一个观察者。观察者的工作是在 livedata 的值发生变化时触发。这样,您可以在视图中包含可以显示错误消息 liveData = false 或启动菜单片段 = true 的逻辑。

    这是使用 livedata 将数据传递给视图(片段)的一个很好的例子:https://developer.android.com/topic/libraries/architecture/viewmodel#implement

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多