简单记录Kotlin 下使用MVVM的例子。
实现:模拟数据变化, 更新UI (两个TextView, 一个显示名字,一个显示计数)
一、app 目录build.grade 启动 dataBinding
android {
//...
dataBinding {
enabled = true
}
}
二、创建UserData (M) 和UserViewModel
class UserViewModel : BaseObservable {
private var userData = UserData(this)
constructor()
//e: ����: Bindable must be on a member in an Observable class. UserViewModel is not Observable
@Bindable
fun getUserName(): String? {
return userData.getCurUserName()
}
@Bindable
fun getCount(): String {
return userData.getCurrCount().toString()
}
fun changeUserName() {
notifyPropertyChanged(BR.userName)
notifyPropertyChanged(BR.count)
}
}
1. UserViewModel: 主要提供两个Bindable 函数, 用于布局文件(V- view);
changeUserName() 通知UI 数据已经变化,请更新UI
需要注意: Bindable 修饰函数(实际上为变量的get 方法), 需要所在的ViewModel 继承 BaseObservable,
否则,会报错:
Bindable must be on a member in an Observable class. UserViewModel is not Observable
此外,也可以通过定义Observable 的变量,实现当数据变化时,自动更新UI, 则不需要手动调用notifyPropertyChanged
var userName = ObservableField()
userName.set(null)
userName.set("Xiaoming")
创建UserData:
class UserData {
private var userNameList = mutableListOf("ZhangSan", "LiSi", "WangWu"
, "ZhaoLiu", "YangBa", "SunJiu")
private var curUserNameIndex = 0
private var userViewModel: UserViewModel
private var changeCount = 0
private val maxCount = 20
constructor(userViewModel: UserViewModel) {
this.userViewModel = userViewModel
//模拟数据变化
simulateDataChange()
}
fun getCurUserName() : String ?{
return userNameList[curUserNameIndex]
}
fun getCurrCount () : Int {
return changeCount
}
private fun simulateDataChange() {
Thread{
for ( count in 0 until maxCount) {
Thread.sleep(2000)
changeCount++
curUserNameIndex++
if (curUserNameIndex >= userNameList.size) {
curUserNameIndex = 0
}
this.userViewModel.changeUserName()
}
}.start()
}
}
1. UserData: 提供用户名字(username) 和 当前计数(changeCount), 启动线程,每隔一秒更新数据(即curUserNameIndex),
并且调用 ViewModel 的 changeUserName() 告知数据变化了
其中,UserData / UserViewModel 相互持有引用,实际上,UserData(Model) 不需要持有ViewModel
三、布局文件 (V)
其中,
1. 布局文件为
2. 标签定义引用的 ViewModel 类型和变量, 使用 variable 标签包裹,其中name 为变量名,type为类所在的包
如果想要引用Android 自带VIEW 的API, 例如 View.VISIBLE, 只需import 这个类
3. 使用到viewmodel的地方: android:text="@{userViewModel.userName}"
表示显示的文本,是引用 userViewModel 里面的 getUserName()的返回值 (省略了get)
四、 Activity 中调用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var dataBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
var userViewModel = UserViewModel()
dataBinding.userViewModel = userViewModel
}
}
主要根据 DataBindingUtils 进行setContentView (取代了之前的setContentView(R.layout.activity_main),
并且返回一个布局文件对应的 Binding 类型, 作用是设置布局文件里面的 ViewModel 变量