官网 ViewModel 概览 | Android 开发者 | Android Developers https://developer.android.google.cn/topic/libraries/architecture/viewmodel?hl=zh_cn#loaders
LiveData 可以包含任何形式的数据,在数据发生变化时通知观察者。
LiveData 一般都配合 ViewModel 使用。
本文例子,一个 TextView 用于显示数据,一个 Button 点击数据加1 ,一个 Button 用于数据归零。
1.添加依赖
dependencies {
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
}
2.使用 LiveData 实现自定义 ViewModel
自定义了 LDViewModel ,继承 ViewModel 。
要观察的数据就是 num 。
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class LDViewModel(data: Int): ViewModel() {
val num = MutableLiveData<Int>()
init {
num.value = data
}
fun add(){
val temp = num.value ?: 0
num.value = temp + 1
}
fun clear(){
num.value = 0
}
}
3.观察数据变化
先实例化 ViewModel , 再调用 observe 观察数据的变化。
package com.cosmos.kotlinjetpackapplication.livedata
import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.cosmos.kotlinjetpackapplication.R
import kotlinx.android.synthetic.main.activity_livedata.*
import kotlinx.android.synthetic.main.activity_livedata.textview
import kotlinx.android.synthetic.main.activity_view_model.*
class LiveDataActivity: AppCompatActivity() {
private lateinit var ldViewModel: LDViewModel
lateinit var sp:SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_livedata)
sp = getPreferences(MODE_PRIVATE)
val lastNum = sp.getInt("key_last_num", 0)
ldViewModel = ViewModelProvider(this, LDViewModelFactory(lastNum)).get(LDViewModel::class.java)
button_add.setOnClickListener{
ldViewModel.add()
}
button_clear.setOnClickListener{
ldViewModel.clear()
}
ldViewModel.num.observe(this, Observer {
textview.text = it.toString()
})
}
override fun onPause() {
super.onPause()
sp.edit {
putInt("key_last_num", ldViewModel.num.value ?: 0)
}
}
}
常规做法是点击修改数据后之后,再去 get 拿到数据。
本来是没问题的,但如果修改数据是耗时操作,这就不好控制 get 的时机了。
使用 LiveData 可以观察到数据变化,数据变化后再实行对应的逻辑就可以了,不用关心到底什么时候修改数据完成。
补上布局,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textview"
android:gravity="center"
android:textSize="30sp"
android:textColor="@color/design_default_color_secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/button_add"
android:text="add"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/button_clear"
android:text="clear"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
这样,基本功能就 OK 了。
4.隐藏被观察的数据
我们希望 LDViewModel 中 num 数据是隐藏的,外部只能通过给定的 add() 、clear() 方法来修改。
但在 Activity 中,是可以直接这样修改的 ldViewModel.num.value = 1000 ,这不太合理。
所以要隐藏被观察的数据。
修改 LDViewModel ,
package com.cosmos.kotlinjetpackapplication.livedata
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class LDViewModel(data: Int): ViewModel(){
private val _num = MutableLiveData<Int>()
val num: LiveData<Int>
get() = _num
init {
_num.value = data
}
fun add(){
val temp = _num.value ?: 0
_num.value = temp + 1
}
fun clear(){
_num.value = 0
}
}
_num 是实际被观察的数据,用 private 修饰后,外部不能访问了。
重新定义了一个变量 num ,它是不可变的 LiveData ,在它的 get() 方法中返回 _num 。
外部访问 num 时,实际获取到的是 _num 的实例。但 num 是不可变的,这样修改 ldViewModel.num.value = 1000 就不成功了,也就实现了隐藏被观察的数据。
至此,就是完整的例子。
5.验证
直接验证肯定没问题,
但如何模拟耗时操作呢? 用协程。
不了解协程也没事,照抄模拟延时就行。
修改 LDViewModel 的 add() 函数,加上 suspend 关键字,声明成挂起函数,
然后调用 delay() 函数延时。
suspend fun add(){
delay(3000)
val temp = _num.value ?: 0
_num.value = temp + 1
}
修改 LiveDataActivity 的点击事件,使用 runBlocking 开启协程,
button_add.setOnClickListener{
runBlocking {
ldViewModel2.add()
}
}
运行之后,点击 button ,3秒后数据更新。
|