在第13天,我们将深入学习Android的两个重要组件:ViewModel
和 LiveData
,并通过一个实践实例来学习如何应用它们。这些组件是 Android Jetpack 的一部分,它们不仅能让应用更具响应性和可扩展性,还能帮助你在面对配置变化(例如屏幕旋转)时保存数据,防止数据丢失。
##本节对应的代码链接为: TodoApp简单的待办事项(Todo)应用
1. ViewModel 和 LiveData 详细介绍
什么是 ViewModel?
ViewModel
是一种专门用于保存和管理 UI 相关数据的类,它解决了一个常见问题:当Activity或Fragment在配置发生变化(例如屏幕旋转)时会被销毁和重新创建,这可能导致其中的数据丢失。而 ViewModel
能够在这种情况下保持数据持久化,避免不必要的数据重新加载。
ViewModel
的作用主要包括:
- 数据持久化:当Activity/Fragment重新创建时,
ViewModel
中的数据仍然可以被保留,不会因为UI组件的重建而丢失。 - 与生命周期分离:
ViewModel
独立于Activity或Fragment的生命周期。这意味着,即使Activity被销毁,ViewModel
中的数据也不会受到影响。 - 数据管理:
ViewModel
是专门用于管理和处理与UI相关的数据逻辑,而不会影响UI的渲染逻辑。
举例来说,假设我们有一个任务列表应用,任务数据存储在 ViewModel
中。如果用户旋转屏幕,Activity 会重新创建,但 ViewModel
中的数据不会丢失,从而避免重新加载任务列表的操作。
什么是 LiveData?
LiveData
是一种可观察的数据持有类。与普通的数据类不同,LiveData
可以与UI组件建立联系,当数据发生变化时,UI组件会自动更新,无需手动通知。
LiveData
的主要特点包括:
- 感知生命周期:
LiveData
会自动感知Activity
或Fragment
的生命周期,并只在生命周期处于活跃状态时更新数据。当Activity
被销毁或停止时,LiveData
会停止向UI发送更新,以避免内存泄漏。 - 数据同步更新:当
LiveData
中的数据发生变化时,UI会自动更新,无需手动调用notifyDataSetChanged
或invalidate
等方法。 - 无需手动管理生命周期:
LiveData
会自动处理与生命周期的绑定,使得我们不需要在代码中显式地管理数据和生命周期之间的关系。
MVVM 架构模式
在Android开发中,MVVM
(Model-View-ViewModel)是一种常见的架构模式,能够使代码结构更清晰、可维护性更高。
- Model(模型):负责数据的获取、存储以及业务逻辑的处理。它可以是来自网络、数据库或本地存储的数据。
- View(视图):表示UI层,直接与用户进行交互。Activity 或 Fragment 通常扮演 View 的角色。
- ViewModel:持有和管理与UI相关的数据逻辑。它不会直接与View打交道,而是通过
LiveData
来通知 View 更新。
这个模式能够很好地分离业务逻辑与UI逻辑,使代码更加模块化和易于维护。
2. 实践实例 - 构建一个待办事项应用
在这一部分,我们将通过Kotlin编写一个待办事项应用,演示如何使用 ViewModel
和 LiveData
进行数据管理。这个应用的功能包括:展示任务列表,添加任务,并在任务列表中实时更新UI。
工程创建过程
-
创建新项目
- 打开Android Studio,点击“New Project”。
- 选择“Empty Activity”模板,并点击“Next”。
- 设置项目名称为
TodoApp
。 - 语言选择
Kotlin
,点击“Finish”完成项目创建。
-
配置依赖
在项目的build.gradle
文件中,添加以下依赖,以确保我们可以使用ViewModel
和LiveData
:dependencies {// ViewModel 和 LiveDataimplementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.0"implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.0"// RecyclerView 用于展示任务列表implementation "androidx.recyclerview:recyclerview:1.2.1" }
添加完依赖后,点击“Sync Now”同步项目。
3. MVVM 实现步骤
Step 1: 创建数据模型
首先,我们创建一个 Task
数据类,表示每个待办事项的任务。它包含三个属性:任务的唯一ID、任务名称和任务是否已完成的状态。
kotlin">// Task.kt
data class Task(val id: Int,val name: String,val isCompleted: Boolean = false
)
解释:
id
:任务的唯一标识符。name
:任务的名称。isCompleted
:任务是否完成,默认值为false
。
Step 2: 创建 ViewModel
ViewModel
负责持有和管理任务列表数据。我们创建一个 TaskViewModel
,它包含 LiveData
来保存任务列表,并提供方法来添加任务。
kotlin">// TaskViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelclass TaskViewModel : ViewModel() {// 私有的 MutableLiveData 保存任务列表private val _taskList = MutableLiveData<List<Task>>()val taskList: LiveData<List<Task>> get() = _taskList// 初始化任务列表为空init {_taskList.value = listOf()}// 添加新任务的方法fun addTask(taskName: String) {val currentList = _taskList.value ?: listOf()val newTask = Task(id = currentList.size + 1, name = taskName)_taskList.value = currentList + newTask}
}
解释:
MutableLiveData
是一个可变的LiveData
,用于在内部管理数据。LiveData
对外暴露,只读,用于观察任务列表的变化。addTask()
方法会向当前的任务列表中添加新任务,然后更新LiveData
,从而自动通知UI更新。
Step 3: 创建 Adapter
为了在 RecyclerView
中显示任务列表,我们需要创建一个适配器 TaskAdapter
来处理数据绑定。
kotlin">// TaskAdapter.kt
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.todoapp.databinding.ItemTaskBindingclass TaskAdapter(private var taskList: List<Task>) : RecyclerView.Adapter<TaskAdapter.TaskViewHolder>() {// ViewHolder 用于保存每个任务视图的引用class TaskViewHolder(private val binding: ItemTaskBinding) : RecyclerView.ViewHolder(binding.root) {fun bind(task: Task) {binding.taskName.text = task.namebinding.taskCompleted.text = if (task.isCompleted) "Completed" else "Not Completed"}}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {val binding = ItemTaskBinding.inflate(LayoutInflater.from(parent.context), parent, false)return TaskViewHolder(binding)}override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {val task = taskList[position]holder.bind(task)}override fun getItemCount() = taskList.size// 更新任务列表的方法fun updateTasks(newTasks: List<Task>) {taskList = newTasksnotifyDataSetChanged()}
}
解释:
TaskViewHolder
是RecyclerView.ViewHolder
的子类,用于保存和绑定任务数据到UI。onCreateViewHolder
创建并初始化视图。onBindViewHolder
绑定每个任务的数据。updateTasks
方法用来更新任务列表并通知RecyclerView
重新渲染。
Step 4: 创建 Activity
在 MainActivity
中,我们需要初始化 ViewModel
和 RecyclerView
,并且通过观察 LiveData
来实时更新UI。
- activity_main.xml
在布局文件中,我们需要一个 RecyclerView
用于展示任务列表,还有一个 EditText
和 Button
用于添加任务。
<!-- activity_main.xml -->
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><EditTextandroid:id="@+id/etTaskName"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Enter task name" /><Buttonandroid:id="@+id/btnAddTask"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Add Task" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="16dp" />
</LinearLayout>
- MainActivity.kt
在 MainActivity
中,我们需要初始化 ViewModel
,设置 RecyclerView
,并在 LiveData
发生变化时更新UI。
kotlin">// MainActivity.kt
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.todoapp.databinding.ActivityMainBindingclass MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate lateinit var taskAdapter: TaskAdapterprivate val taskViewModel: TaskViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)// 设置 RecyclerViewtaskAdapter = TaskAdapter(listOf())binding.recyclerView.layoutManager = LinearLayoutManager(this)binding.recyclerView.adapter = taskAdapter// 观察任务列表数据变化taskViewModel.taskList.observe(this) { tasks ->taskAdapter.updateTasks(tasks)}// 添加任务按钮点击事件binding.btnAddTask.setOnClickListener {val taskName = binding.etTaskName.text.toString()if (taskName.isNotEmpty()) {taskViewModel.addTask(taskName)binding.etTaskName.text.clear()}}}
}
解释:
by viewModels()
是一个 Kotlin 委托,用于初始化ViewModel
。observe()
方法用来观察LiveData
,当taskList
发生变化时,适配器会更新任务列表。- 在
btnAddTask.setOnClickListener
中,用户输入任务名并点击“Add Task”按钮后,ViewModel
将新任务添加到任务列表中。
4. 总结
在这个实例中,我们通过构建一个简单的待办事项应用,学习了 ViewModel
和 LiveData
的基础使用。你已经掌握了如何使用 LiveData
来观察数据变化,并通过 ViewModel
来持久化UI数据。这样做不仅能够让应用更具响应性,还能让数据在配置变化时保持不变。