首先明确两点
- 数据结果通过LiveData传递,如LoginResult
- 逻辑判断导致的视图变化也通过LiveData传递,如LoginFormState
主要流程代码
class LoginViewModelFactory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(LoginViewModel::class.java)) {
return LoginViewModel(
loginRepository = LoginRepository(
dataSource = LoginDataSource()
)
) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
loginViewModel = ViewModelProviders.of(this, LoginViewModelFactory())
.get(LoginViewModel::class.java)
loginViewModel.loginFormState.observe(this,
Observer { loginFormState ->
if (loginFormState == null) {
return@Observer
}
loginButton.isEnabled = loginFormState.isDataValid
loginFormState.usernameError?.let {
usernameEditText.error = getString(it)
}
loginFormState.passwordError?.let {
passwordEditText.error = getString(it)
}
})
loginViewModel.loginResult.observe(this,
Observer { loginResult ->
loginResult ?: return@Observer
loadingProgressBar.visibility = View.GONE
loginResult.error?.let {
showLoginFailed(it)
}
loginResult.success?.let {
updateUiWithUser(it)
}
})
新建LoginFragment模板后会产生以下类
LoginViewModel包含2个LiveData,即2个状态,登录状态校验和登录结果,loginFormState和loginResult
由于kotlin语法的便利,LoginFormState入参可以有默认值,即自动识别不同参数的构造函数
fun loginDataChanged(username: String, password: String) {
if (!isUserNameValid(username)) {
_loginForm.value = LoginFormState(usernameError = R.string.invalid_username)
} else if (!isPasswordValid(password)) {
_loginForm.value = LoginFormState(passwordError = R.string.invalid_password)
} else {
_loginForm.value = LoginFormState(isDataValid = true)
}
}
这里loginFormState跟_loginForm是同个东西
private val _loginForm = MutableLiveData<LoginFormState>()
val loginFormState: LiveData<LoginFormState> = _loginForm
而登录结果见LoginViewModel的登录方法
fun login(username: String, password: String) {
// can be launched in a separate asynchronous job
val result = loginRepository.login(username, password)
if (result is Result.Success) {
_loginResult.value =
LoginResult(success = LoggedInUserView(displayName = result.data.displayName))
} else {
_loginResult.value = LoginResult(error = R.string.login_failed)
}
}
这里的Result是密封类,sealed密封类跟枚举比较相似,不理解的可以上B站看看抛物线老师的讲解,比较清晰。
sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
override fun toString(): String {
return when (this) {
is Success<*> -> "Success[data=$data]"
is Error -> "Error[exception=$exception]"
}
}
}
PS:
isAssignableFrom与instanceof的区别
isAssignableFrom()方法与instanceof关键字的区别总结为以下两个点:
isAssignableFrom()方法是从类继承的角度去判断,instanceof关键字是从实例继承的角度去判断。
isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。
使用方法:
父类.class.isAssignableFrom(子类.class)
子类实例 instanceof 父类类型