Kotlin + MVVM + Jetpack 学习系列
一、本文主要内容
Kotlin + MVVM 简单搭建示例
二、MVVM结构
首先介绍下什么是MVVM,相信做Android开发的对MVC/MVP都不陌生,MVVM也一样,也是一种设计架构。
层级 | 描述 |
---|---|
View | 对应Activity/Fragment以及XML,负责View的绘制、用户交互 |
ViewModel | 业务逻辑,作为桥梁处理View和Model直接的交互 |
Model | 数据的处理,如网络请求、查询SQLite等 |
这么一看,是不是感觉ViewModel和Presenter蛮像的,但是他们的实现方式是不一样的,在MVVM中,这些是通过数据驱动来自动完成的,数据变化后会自动更新UI,UI的改变(用户输入内容等)也能更新到数据层。
三、简单Demo示例
以帐号登录为例,下面介绍下如何快速搭建各层代码。
3.1 Model层
这里省略了具体的网络请求类,只定义了一个LoginInfo的Bean类。
很简单,三个属性,继承BaseObservable,并在数据被修改后进行notifyChange()。
class LoginInfo: BaseObservable() {
var name: String = ""
set(value) {
if (name != value) {
field = value
notifyChange()
}
}
var pwd: String = ""
set(value) {
if (pwd != value) {
field = value
notifyChange()
}
}
var state: String = ""
set(value) {
field = value
notifyChange()
}
}
3.2 ViewModel层
创建MutableLiveData对象,这里泛型<>就是我们的LoginInfo,还有就是对LoginInfo做一个对象初始化,以及定义了一个login()的方法(下面讲到View层的时候会用到)。
class CusViewModel : ViewModel() {
/**
* 一个可被观察的数据类,并且能感知View(Activity等)生命周期
* databinding后view的输入,或者数据的改变均会通知到双方
*/
val loginInfoData = MutableLiveData<LoginInfo>()
init {
// 初始化loginInfo对象
loginInfoData.value = LoginInfo()
}
fun login() {
// 模拟登录,正常场景应通过model层进行网络请求等
val loginInfo = loginInfoData.value
if (loginInfo != null) {
if ("123" == loginInfo.name && "123" == loginInfo.pwd) {
loginInfo.state = "成功"
} else {
loginInfo.state = "失败"
}
}
}
}
3.3 View层
包含Acitivity和XML布局文件的编写,主要实现View与ViewModel形成绑定关系。
3.3.1 XML
先对XML布局进行转换,通过Alt+Enter会有提示“Convert to data bindling layout”,转换后会多出一组data标签,其中声明我们的ViewModel对象
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="cusViewModel"
type="com.ycl.kotlinapplication.mvvm.viewmodel.CusViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/ed_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="姓名"
android:text="@={cusViewModel.loginInfoData.name}" />
<EditText
android:id="@+id/ed_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密码"
android:text="@={cusViewModel.loginInfoData.pwd}" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{cusViewModel.loginInfoData.name}" />
<TextView
android:id="@+id/tv_age"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{cusViewModel.loginInfoData.pwd}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{()->cusViewModel.login()}"
android:text="登录" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{cusViewModel.loginInfoData.state}" />
</LinearLayout>
</layout>
可以看见在Edittext/TextView中直接使用了cusViewModel.loginInfoData.xxx的属性值对view的text属性赋值,同时Button的点击事件调用到cusViewModel.login()方法。
补充:
@={} 代表双向绑定(view的输入影响数据本身,数据本身变化影响view的展示)
假设上诉Edittext这里改成单向绑定,你觉得体验是什么样?
@{} 代表单向绑定
3.3.2 Activity
1.创建ViewModel
2.DataBinding绑定布局
3.DataBinding与ViewModel关联
这样就可以把View与ViewModel关联起来了
class CusViewActivity : AppCompatActivity() {
private lateinit var mBinding: ActivityCusviewBinding
private lateinit var mCusViewModel: CusViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 创建ViewModel
mCusViewModel = ViewModelProvider(this).get(CusViewModel::class.java);
// DataBinding绑定布局
mBinding =
DataBindingUtil.setContentView<ActivityCusviewBinding>(this, R.layout.activity_cusview)
// 代码初始化LoginInfo信息(会直接刷新到View中)
/*val loginInfo = mCusViewModel.loginInfoData.value
if (loginInfo != null) {
loginInfo.name = "123"
loginInfo.pwd = "345"
}*/
// DataBinding与ViewModel关联
mBinding.cusViewModel = mCusViewModel
}
}
3.4 预览
五、总结
MVVM简化了对view的繁琐操作,可以看到上诉代码不需要再对view设置监听来获取输入内容等。通过数据绑定机制,实现了Model和View的自动更新,降低了代码的复杂度。同时减少手动对生命周期的管理,降低内存泄漏风险。