定义
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。
ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
简单的说就是,在android中,当Activity重建或销毁时,页面上的数据会丢失。为了保存页面的数据,我们以前通常的做法是在 onSaveInstanceState 中,将数据保存到 bundle 中,再在 onCreate 中将 bundle 中的数据取出来。
而使用 ViewModel,我们就无需再用这种方法保存,因为 ViewModel 会自动感知生命周期,处理数据的保存与恢复。即数据可在发生屏幕旋转等配置(其它例如分辨率调整、权限变更、系统字体样式、语言变更等)更改后继续留存。
使用
ViewModel也属于 androidx.lifecycle 包; 导入方式和Lifecycle类似。
dependencies {
def lifecycle_version = "2.2.0"
def arch_version = "2.1.0"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
}
完整的架构组件导入方式请参看: https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#declaring_dependencies
实现 ViewModel
例如,如果您需要在应用中显示用户列表,请确保将获取和保留该用户列表的责任分配给 ViewModel,而不是 Activity 或 Fragment,如以下示例代码所示:
class MyViewModel : ViewModel() {
//延迟
private val users: MutableLiveData<List<User>> by lazy {
MutableLiveData().also {
loadUsers()
}
}
fun getUsers(): LiveData<List<User>> {
return users
}
// 异步
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
}
}
然后,可以从 Activity 访问该列表,如下所示:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val model: MyViewModel by viewModels()
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
ViewModel 对象存在的时间比视图或 LifecycleOwners 的特定实例存在的时间更长。
ViewModel 对象可以包含 LifecycleObservers,如 LiveData 对象。但是,ViewModel 对象绝不能观察对生命周期感知型可观察对象(如 LiveData 对象)的更改。
通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。
在 Fragment 之间共享数据
假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。
传统的处理方式是这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。
可以使用 ViewModel 对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:
//Activity 不需要执行任何操作,也不需要对此通信有任何了解。
//除了 SharedViewModel 约定之外,Fragment 不需要相互了解。
//每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
}
最简单示例
先看实现效果:点击按钮,数字 ++
布局activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.528"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.313" />
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="@string/plusbtn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.547"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv" />
</androidx.constraintlayout.widget.ConstraintLayout>
创建ViewModel
class NumberPlusViewModel: ViewModel() {
var number = 0
}
activity 使用ViewModel
class MainActivity : AppCompatActivity() {
private val numberPlusViewModel by viewModels<NumberPlusViewModel>();
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv.text = numberPlusViewModel.number.toString()
btn1.setOnClickListener {
numberPlusViewModel.number++
tv.text = numberPlusViewModel.number.toString()
}
}
}
注意上述代码中的 viewModels 扩展函数,来源于 "androidx.activity:activity-ktx:1.1.0" ,记得添加依赖
dependencies {
implementation "androidx.activity:activity-ktx:1.1.0"
}
// 即:
//Lazy<VM> ComponentActivity.viewModels(noinline factoryProducer: () -> ViewModelProvider.Factory = null)
//返回一个延迟委托以访问 ComponentActivity 的 ViewModel。如果已指定 factoryProducer,则它返回的 ViewModelProvider.Factory 将用于首次创建 ViewModel。
如果运行代码,还是报错,记得配置 compileOptions。
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}