当前位置: 首页 > article >正文

第13天:高级主题 - ViewModel 和 LiveData

在第13天,我们将深入学习Android的两个重要组件:ViewModelLiveData,并通过一个实践实例来学习如何应用它们。这些组件是 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 会自动感知 ActivityFragment 的生命周期,并只在生命周期处于活跃状态时更新数据。当 Activity 被销毁或停止时,LiveData 会停止向UI发送更新,以避免内存泄漏。
  • 数据同步更新:当 LiveData 中的数据发生变化时,UI会自动更新,无需手动调用 notifyDataSetChangedinvalidate 等方法。
  • 无需手动管理生命周期LiveData 会自动处理与生命周期的绑定,使得我们不需要在代码中显式地管理数据和生命周期之间的关系。
MVVM 架构模式

在Android开发中,MVVM (Model-View-ViewModel)是一种常见的架构模式,能够使代码结构更清晰、可维护性更高。

  • Model(模型):负责数据的获取、存储以及业务逻辑的处理。它可以是来自网络、数据库或本地存储的数据。
  • View(视图):表示UI层,直接与用户进行交互。Activity 或 Fragment 通常扮演 View 的角色。
  • ViewModel:持有和管理与UI相关的数据逻辑。它不会直接与View打交道,而是通过 LiveData 来通知 View 更新。

这个模式能够很好地分离业务逻辑与UI逻辑,使代码更加模块化和易于维护。


2. 实践实例 - 构建一个待办事项应用

在这一部分,我们将通过Kotlin编写一个待办事项应用,演示如何使用 ViewModelLiveData 进行数据管理。这个应用的功能包括:展示任务列表,添加任务,并在任务列表中实时更新UI。


工程创建过程
  1. 创建新项目

    • 打开Android Studio,点击“New Project”。
    • 选择“Empty Activity”模板,并点击“Next”。
    • 设置项目名称为 TodoApp
    • 语言选择 Kotlin,点击“Finish”完成项目创建。
  2. 配置依赖
    在项目的 build.gradle 文件中,添加以下依赖,以确保我们可以使用 ViewModelLiveData

    dependencies {
        // ViewModel 和 LiveData
        implementation "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、任务名称和任务是否已完成的状态。

// 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 来保存任务列表,并提供方法来添加任务。

// TaskViewModel.kt
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class 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 来处理数据绑定。

// TaskAdapter.kt
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.todoapp.databinding.ItemTaskBinding

class 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.name
            binding.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 = newTasks
        notifyDataSetChanged()
    }
}

解释:

  • TaskViewHolderRecyclerView.ViewHolder 的子类,用于保存和绑定任务数据到UI。
  • onCreateViewHolder 创建并初始化视图。
  • onBindViewHolder 绑定每个任务的数据。
  • updateTasks 方法用来更新任务列表并通知 RecyclerView 重新渲染。
Step 4: 创建 Activity

MainActivity 中,我们需要初始化 ViewModelRecyclerView,并且通过观察 LiveData 来实时更新UI。

  1. activity_main.xml

在布局文件中,我们需要一个 RecyclerView 用于展示任务列表,还有一个 EditTextButton 用于添加任务。

<!-- activity_main.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <EditText
        android:id="@+id/etTaskName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Enter task name" />

    <Button
       

android:id="@+id/btnAddTask"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Task" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp" />
</LinearLayout>
  1. MainActivity.kt

MainActivity 中,我们需要初始化 ViewModel,设置 RecyclerView,并在 LiveData 发生变化时更新UI。

// 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.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var taskAdapter: TaskAdapter
    private val taskViewModel: TaskViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 设置 RecyclerView
        taskAdapter = 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. 总结

在这个实例中,我们通过构建一个简单的待办事项应用,学习了 ViewModelLiveData 的基础使用。你已经掌握了如何使用 LiveData 来观察数据变化,并通过 ViewModel 来持久化UI数据。这样做不仅能够让应用更具响应性,还能让数据在配置变化时保持不变。


http://www.kler.cn/a/401453.html

相关文章:

  • 编译OpenCV的速度,家里和公司的电脑相差很大
  • Cesium 相机系统
  • 《通往人工智能深度学习专家之路:全面解析学习路线图》
  • css uniapp背景图宽度固定高度自适应可以重复
  • 【时间之外】IT人求职和创业应知【36】-肖申克的救赎
  • Qt 之 qwt和QCustomplot对比
  • 三维测量与建模笔记 - 点特征提取 - 4.4 SIFT
  • 如何在 Python 中判断 ADB 设备是否连接
  • 鼎峰自愈路由系统-完全实现自动化切换最优网络
  • KNN算法介绍及代码实例
  • 大学作业:城市PM2.5预测分析数据挖掘大作业资源源码免费下载
  • 集合的概念及练习
  • 炼码LintCode--数据库题库(级别:中等;数量:更新中~)--刷题笔记_03
  • 【Three.js基础学习】26. Animated galaxy
  • Dubbo源码解析-服务导出(四)
  • chatGPT是如何使用tensrFlow训练模型的?
  • 【数据分享】1981-2024年我国逐日最低气温栅格数据(免费获取)
  • CSS3中的弹性布局之侧轴的对齐方式
  • 梧桐数据库深入探索递归查询的强大功能
  • Excel——宏教程(精简版)
  • EMall实践DDD模拟电商系统总结
  • 学习整理在php中一个二维数组按另一个一维数组顺序排序
  • 【OpenCV】Could NOT find TIFF (missing: TIFF_LIBRARY TIFF_INCLUDE_DIR)
  • C++ 对函数的详细记录 【雨露均沾】
  • 【UGUI】Unity 游戏开发:背包系统初始化克隆道具
  • CFD 应用于分离过程:旋风分离器(第 2 部分)