Android (Kotlin)MVVM中片段之间的通信

Android (Kotlin)MVVM中片段之间的通信,android,android-studio,kotlin,mvvm,Android,Android Studio,Kotlin,Mvvm,我试图理解MVVM的概念,但在本例中,我很难理解如何在模型类和UI(片段)之间进行通信 以下是(糟糕的,请注意)代码: LoginFragment.kt class LoginFragment: Fragment(), AuthListener { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View?

我试图理解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()
}}
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类中做出相应的响应


谢谢。

从视图模型返回UI的一种典型通信方式是使用。在LoginView模型中,您可以将livedata设置为
true
false
。在视图
LoginFragment.kt
中,您将有一个观察者。此任务是在livedata的值发生更改时触发。这样,视图中的逻辑可以显示错误消息liveData=false或启动菜单fragment=true


下面是一个很好的例子,使用livedata将数据传递到视图(片段)文档中的这一部分:

您必须使用livedata将数据从viewModel更新到视图。我将发布代码,但请确保您需要理解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
}
}
}
classloginviewmodel:ViewModel(){
变量用户名:字符串?=null
var密码:字符串?=null
var isCredentialsValid:Boolean=false
var authListener:authListener?=null
私有val上下文:上下文?=null
//使用LiveData对UI进行自定义
private val_isValidCredential=MutableLiveData()
val isValidCredential:LiveData=_isValidCredential
乐趣onLoginButtonClicked(视图:视图){
if(username.isNullOrEmpty()| | password.isNullOrEmpty()){
authListener?.onError(“无效的用户名或密码”)
isCredentialsValid=false
返回
}
如果(!username.isNullOrEmpty()&&password!!.length>=8){
isCredentialsValid=true
//随时随地更新实时数据的价值
_isValidCredential.value=true
authListener?.onSuccess()
}否则{
authListener?.onError(“无效”)
//随时随地更新实时数据的价值
_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()
}}
class-LoginFragment:Fragment(),AuthListener{
覆盖创建视图(
充气机,
容器:视图组?,
savedInstanceState:捆绑?
):查看{
val binding=DataBindingUtil.inflate(
充气机,
R.layout.credentials\u login\u片段,
集装箱,
假的
)
val viewModel=ViewModelProviders.of(this.get)(LoginViewModel::class.java)
val view:view=binding.root
val registerButton:Button=view.findViewById(R.id.register_按钮)
binding.viewModel=viewModel
viewModel.authListener=此
//这是您需要观察值的方式
viewModel.isValidCredential.observe(viewLifecycleOwner,Observer{
如果(它){
//在这里做你的导航工作
}否则{
//如果不是有效的凭证,请执行您的操作
}
})
registerButton.setOnClickListener{
val事务:碎片事务?=
碎片管理器?.beginTransaction()
事务?.replace(R.id.fragment_容器,SignupFragment())?.commit()
}
返回视图
}
覆盖已创建的视图(视图:视图,保存状态:捆绑?){
super.onViewCreated(视图,savedInstanceState)
val constraintRoot:MotionLayout=view.findViewById(R.id.sign\u in\u root)
ActivityUtils().SwitchLayoutImationKeyboard(constraintRoot=constraintRoot)
}
覆盖启动时的乐趣(){
Toast.makeText(上下文“开始”,Toast.LENGTH\u SHORT.show())
}
覆盖成功的乐趣(){
Toast.makeText(上下文,“Success”,Toast.LENGTH\u SHORT.show())
}
重写错误(消息:字符串){
Toast.makeText(上下文、消息、Toast.LENGTH\u SHORT).show()
}}

@BenAndJerrys upvoting这个答案可能对其他人有用,我相信我太新了