kotlin与MVVM的结合使用总结(二)

news2025/3/14 19:37:01

  在 MVVM(Model - View - ViewModel)架构中,M 层即 Model 层,主要负责数据的管理、存储和获取,它与业务逻辑和数据处理相关。在 Kotlin 中实现 MVVM 的 M 层,通常会涉及数据类的定义、数据的本地存储与远程获取等操作,以下是详细的实现讲解:

1. 定义数据类

数据类是 Kotlin 中非常方便的特性,用于简洁地定义数据模型。它会自动生成 equals()hashCode()toString() 等方法。以下是一个简单的用户数据类示例:

data class User(
    val id: Int,
    val name: String,
    val age: Int,
    val email: String
)

这个数据类 User 表示一个用户的基本信息,包含 idnameage 和 email 四个属性。

2. 本地数据存储

如果需要将数据存储在本地,可以使用 Android 的 Room 数据库。Room 是 Android 官方提供的一个抽象层,用于在 SQLite 数据库上进行对象映射,结合 Kotlin 可以更方便地操作数据库。

2.1 定义实体类

首先,要确保数据类符合 Room 的实体类要求,添加 @Entity 注解:

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    val age: Int,
    val email: String
)

这里的 @Entity 注解指定了该数据类对应数据库中的 users 表,@PrimaryKey 注解指定了 id 作为主键。

2.2 定义 DAO(数据访问对象)

DAO 用于定义数据库操作的方法,使用 @Dao 注解:

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query

@Dao
interface UserDao {
    @Insert
    suspend fun insertUser(user: User)

    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUserById(id: Int): User?
}

在这个 DAO 中,@Insert 注解表示插入操作,@Query 注解用于自定义查询操作。注意方法使用了 suspend 关键字,以便在协程中调用。

2.3 定义数据库

使用 @Database 注解定义数据库:

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

这里指定了数据库包含的实体类为 User,版本号为 1,并提供了获取 UserDao 的抽象方法。

3. 远程数据获取

如果需要从网络获取数据,可以使用 Retrofit 库。Retrofit 是一个类型安全的 HTTP 客户端,结合 Kotlin 协程可以高效地进行网络请求。

3.1 定义 API 接口
import retrofit2.http.GET
import retrofit2.http.Path

interface UserApiService {
    @GET("users/{id}")
    suspend fun getUserById(@Path("id") id: Int): User
}

这个接口定义了一个获取用户信息的方法,使用 @GET 注解指定请求的 URL,@Path 注解用于替换 URL 中的参数。

3.2 创建 Retrofit 实例

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitClient {
    private const val BASE_URL = "https://example.com/api/"

    val instance: Retrofit by lazy {
        Retrofit.Builder()
           .baseUrl(BASE_URL)
           .addConverterFactory(GsonConverterFactory.create())
           .build()
    }

    val userApiService: UserApiService by lazy {
        instance.create(UserApiService::class.java)
    }
}

这里创建了一个 Retrofit 实例,并通过 create() 方法创建了 UserApiService 的实例。

4. 仓库(Repository)模式

为了统一管理本地和远程数据的获取,通常会使用仓库模式。仓库类负责协调数据的来源,根据需要从本地数据库或远程服务器获取数据。

class UserRepository(
    private val userDao: UserDao,
    private val userApiService: UserApiService
) {
    suspend fun getUserById(id: Int): User? {
        // 先从本地数据库获取数据
        var user = userDao.getUserById(id)
        if (user == null) {
            // 如果本地没有数据,从网络获取
            user = userApiService.getUserById(id)
            if (user != null) {
                // 将从网络获取的数据保存到本地数据库
                userDao.insertUser(user)
            }
        }
        return user
    }
}

 在这个仓库类中,getUserById 方法首先尝试从本地数据库获取用户数据,如果本地没有则从网络获取,并将获取到的数据保存到本地数据库。

  总结(M层)

  通过以上步骤,我们在 Kotlin 中实现了 MVVM 架构的 M 层。主要包括数据类的定义、本地数据存储(使用 Room)、远程数据获取(使用 Retrofit)以及仓库模式的应用,这样可以有效地管理数据的来源和流向,提高代码的可维护性和可测试性。

下一层:V层  

  以下将详细介绍在 Kotlin 中实现 MVVM 架构里 V 层(View 层)的完整步骤,并给出相应代码示例。V 层主要负责展示 UI 以及处理用户交互,通常由 Activity 或 Fragment 实现。

项目准备

确保在 build.gradle 文件中添加必要依赖:

dependencies {
    // ViewModel 和 LiveData
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
    // ViewBinding
    android {
        buildFeatures {
            viewBinding = true
        }
    }
    // Kotlin 协程
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
}

定义布局文件

在 res/layout 目录下创建 activity_main.xml 文件,用于定义界面布局

<?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"
    android:padding="16dp">

    <EditText
        android:id="@+id/et_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入内容" />

    <Button
        android:id="@+id/btn_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:paddingTop="16dp" />
</LinearLayout>

创建 ViewModel

ViewModel 负责处理业务逻辑和数据管理,以下是 MainViewModel 的实现:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    private val _result = MutableLiveData<String>()
    val result: LiveData<String> = _result

    fun processInput(input: String) {
        // 简单处理输入,这里只是将输入内容反转
        val processed = input.reversed()
        _result.value = processed
    }
}

创建 Activity 作为 V 层

使用 Kotlin 实现 MainActivity 作为 View 层,通过 ViewBinding 绑定视图,观察 ViewModel 中的数据变化并处理用户交互:

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.mvvmexample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 使用 ViewBinding 绑定布局
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 获取 ViewModel 实例
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        // 设置按钮点击事件监听器
        binding.btnSend.setOnClickListener {
            // 获取输入框中的内容
            val input = binding.etInput.text.toString()
            // 调用 ViewModel 的方法处理输入
            viewModel.processInput(input)
        }

        // 观察 ViewModel 中的数据变化
        viewModel.result.observe(this, Observer { result ->
            // 更新 UI 显示处理结果
            binding.tvResult.text = "处理结果: $result"
        })
    }
}

代码解释

  • ViewBinding 的使用
    • ActivityMainBinding.inflate(layoutInflater) 用于实例化绑定类,通过 setContentView(binding.root) 设置布局,之后可直接使用 binding 对象访问布局中的视图组件,如 binding.etInputbinding.btnSend 等。
  • ViewModel 的获取
    • ViewModelProvider(this).get(MainViewModel::class.java) 用于获取 MainViewModel 的实例,确保在 Activity 的生命周期内使用同一个 ViewModel 实例。
  • 用户交互处理
    • 通过 binding.btnSend.setOnClickListener 为按钮设置点击事件监听器,当按钮被点击时,获取输入框中的内容并调用 ViewModel 的 processInput 方法进行处理。
  • 数据观察
    • viewModel.result.observe(this, Observer { ... }) 用于观察 LiveData 的数据变化,当 LiveData 的值发生改变时,会触发 Observer 中的代码块,从而更新 UI。

使用 Fragment 作为 V 层(可选)

如果使用 Fragment 作为 V 层,实现方式类似:

创建布局文件 fragment_main.xml

<?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"
    android:padding="16dp">

    <EditText
        android:id="@+id/et_input_fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入内容" />

    <Button
        android:id="@+id/btn_send_fragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送" />

    <TextView
        android:id="@+id/tv_result_fragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:paddingTop="16dp" />
</LinearLayout>

 创建 Fragment

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.mvvmexample.databinding.FragmentMainBinding

class MainFragment : Fragment() {
    private var _binding: FragmentMainBinding? = null
    private val binding get() = _binding!!
    private lateinit var viewModel: MainViewModel

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // 使用 ViewBinding 绑定布局
        _binding = FragmentMainBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // 获取 ViewModel 实例
        viewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)

        // 设置按钮点击事件监听器
        binding.btnSendFragment.setOnClickListener {
            val input = binding.etInputFragment.text.toString()
            viewModel.processInput(input)
        }

        // 观察 ViewModel 中的数据变化
        viewModel.result.observe(viewLifecycleOwner, Observer { result ->
            binding.tvResultFragment.text = "处理结果: $result"
        })
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

总结(V层)

通过以上步骤,我们可以在 Kotlin 中使用 Activity 或 Fragment 作为 MVVM 架构的 V 层,借助 ViewBinding 绑定视图,使用 ViewModelProvider 获取 ViewModel 实例,处理用户交互并观察 ViewModel 中的数据变化以更新 UI,实现视图和业务逻辑的分离,提高代码的可维护性和可测试性。

因为太多了,我就先讲解前两个层,下一节我将阐述最重要的VM层

谢谢观看!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2315040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

政策助力,3C 数码行业数字化起航

政策引领&#xff0c;数字经济浪潮来袭 在当今时代&#xff0c;数字经济已成为全球经济发展的核心驱动力&#xff0c;引领着新一轮科技革命和产业变革的潮流。我国深刻洞察这一发展趋势&#xff0c;大力推进数字化经济发展战略&#xff0c;为经济的高质量发展注入了强大动力。 …

MySQL数据库复制

文章目录 MySQL数据库复制一、复制的原理二、复制的搭建1.编辑配置文件2.在主库上创建复制的用户3.获取主库的备份4.基于从库的恢复5.建立主从复制6.开启主从复制7.查看主从复制状态 MySQL数据库复制 MySQL作为非常流行的数据库&#xff0c;支撑它如此出彩的因素主要有两个&am…

101.在 Vue 3 + OpenLayers 使用 declutter 避免文字标签重叠

1. 前言 在使用 OpenLayers 进行地图开发时&#xff0c;我们经常需要在地图上添加点、线、区域等图形&#xff0c;并给它们附加文字标签。但当地图上的标注较多时&#xff0c;文字标签可能会发生重叠&#xff0c;导致用户无法清晰地查看地图信息。 幸运的是&#xff0c;OpenL…

uniapp移动端图片比较器组件,仿英伟达官网rtx光追图片比较器功能

组件下载地址&#xff1a;https://ext.dcloud.net.cn/plugin?id22609 已测试h5和微信小程序&#xff0c;理论支持全平台 亮点&#xff1a; 简单易用 使用js计算而不是resize属性&#xff0c;定制化程度更高 组件挂在后可播放指示线动画&#xff0c;提示用户可以拖拽比较图片…

深度学习与大模型-矩阵

矩阵其实在我们的生活中也有很多应用&#xff0c;只是我们没注意罢了。 1. 矩阵是什么&#xff1f; 简单来说&#xff0c;矩阵就是一个长方形的数字表格。比如你有一个2行3列的矩阵&#xff0c;可以写成这样&#xff1a; 这个矩阵有2行3列&#xff0c;每个数字都有一个位置&a…

搭建基于chatgpt的问答系统

一、语言模型&#xff0c;提问范式与 Token 1.语言模型 大语言模型&#xff08;LLM&#xff09;是通过预测下一个词的监督学习方式进行训练的&#xff0c;通过预测下一个词为训练目标的方法使得语言模型获得强大的语言生成能力。 a.基础语言模型 &#xff08;Base LLM&…

LuaJIT 学习(2)—— 使用 FFI 库的几个例子

文章目录 介绍Motivating Example: Calling External C Functions例子&#xff1a;Lua 中调用 C 函数 Motivating Example: Using C Data StructuresAccessing Standard System FunctionsAccessing the zlib Compression LibraryDefining Metamethods for a C Type例子&#xf…

解锁 AI 开发的无限可能:邀请您加入 coze-sharp 开源项目

大家好&#xff01;今天我要向大家介绍一个充满潜力的开源项目——coze-sharp&#xff01;这是一个基于 C# 开发的 Coze 客户端&#xff0c;旨在帮助开发者轻松接入 Coze AI 平台&#xff0c;打造智能应用。项目地址在这里&#xff1a;https://github.com/zhulige/coze-sharp&a…

全面解析与实用指南:如何有效解决ffmpeg.dll丢失问题并恢复软件正常运行

在使用多媒体处理软件或进行视频编辑时&#xff0c;你可能会遇到一个常见的问题——ffmpeg.dll文件丢失。这个错误不仅会中断你的工作流程&#xff0c;还可能导致软件无法正常运行。ffmpeg.dll是FFmpeg库中的一个关键动态链接库文件&#xff0c;负责处理视频和音频的编码、解码…

Python----计算机视觉处理(opencv:像素,RGB颜色,图像的存储,opencv安装,代码展示)

一、计算机眼中的图像 像素 像素是图像的基本单元&#xff0c;每个像素存储着图像的颜色、亮度和其他特征。一系列像素组合到一起就形成 了完整的图像&#xff0c;在计算机中&#xff0c;图像以像素的形式存在并采用二进制格式进行存储。根据图像的颜色不 同&#xff0c;每个像…

小米路由器SSH下安装DDNS-GO

文章目录 前言一、下载&#xff06;安装DDNS-GO二、配置ddns-go设置开机启动 前言 什么是DDNS&#xff1f; DDNS&#xff08;Dynamic Domain Name Server&#xff09;是动态域名服务的缩写。 目前路由器拨号上网获得的多半都是动态IP&#xff0c;DDNS可以将路由器变化的外网I…

go语言zero框架拉取内部平台开发的sdk报错的修复与实践

在开发过程中&#xff0c;我们可能会遇到由于认证问题无法拉取私有 SDK 的情况。这种情况常发生在使用 Go 语言以及 Zero 框架时&#xff0c;尤其是在连接到私有平台&#xff0c;如阿里云 Codeup 上托管的 Go SDK。如果你遇到这种错误&#xff0c;通常是因为 Go 没有适当的认证…

手机屏幕摔不显示了,如何用其他屏幕临时显示,用来导出资料或者清理手机

首先准备一个拓展坞 然后 插入一个外接的U盘 插入鼠标 插入有数字小键盘区的键盘 然后准备一根高清线&#xff0c;一端链接电脑显示器,一端插入拓展坞 把拓展坞的连接线&#xff0c;插入手机充电口&#xff08;可能会需要转接头&#xff09; 然后确保手机开机 按下键盘…

工业三防平板AORO-P300 Ultra,开创铁路检修与调度数字化新范式

在现代化铁路系统的庞大网络中&#xff0c;其设备维护与运营调度的精准性直接影响着运输效率和公共安全。在昼夜温差大、电磁环境复杂、震动粉尘交织的铁路作业场景中&#xff0c;AORO-P300 Ultra工业三防平板以高防护标准与智能化功能体系&#xff0c;开创了铁路行业移动端数字…

LInux基础--apache部署网站

httpd的安装 yum -y install httpdhttpd的使用 启动httpd systemctl enable --now httpd使用enable --now 进行系统设置时&#xff0c;会将该服务设置为开机自启并且同时开启服务 访问httpd 创建虚拟主机 基于域名 在一台主机上配置两个服务server1和server2&#xff0c;其…

Linux内核套接字以及分层模型

一、套接字通信 内核开发工程师将网络部分的头文件存储到一个专门的目录include/net中&#xff0c;而不是存储到标准位置include/linux。 计算机之间通信是一个非常复杂的问题&#xff1a; 如何建立物理连接&#xff1f;使用什么样的线缆&#xff1f;通信介质有那些限制和特殊…

Linux《基础开发工具(中)》

在之前的Linux《基础开发工具&#xff08;上&#xff09;》当中已经了解了Linux当中到的两大基础的开发工具yum与vim&#xff1b;了解了在Linux当中如何进行软件的下载以及实现的基本原理、知道了编辑器vim的基本使用方式&#xff0c;那么接下来在本篇当中将接下去继续来了解另…

使用1Panel一键搭建WordPress网站的详细教程(全)

嘿&#xff0c;各位想搭建自己网站的朋友们&#xff01;今天我要跟大家分享我用1Panel搭建WordPress网站的全过程。说实话&#xff0c;我之前对服务器运维一窍不通&#xff0c;但通过这次尝试&#xff0c;我发现原来建站可以这么简单&#xff01;下面是我的亲身经历和一些小技巧…

uni-app学习笔记——自定义模板

一、流程 1.这是一个硬性的流程&#xff0c;只要按照如此程序化就可以实现 二、步骤 1.第一步 2.第二步 3.第三步 4.每一次新建页面&#xff0c;都如第二步一样&#xff1b;可以选择自定义的模版&#xff08;vue3Setup——这是我自己的模版&#xff09;&#xff0c;第二步的…

数据结构——顺序表seqlist

前言&#xff1a;大家好&#x1f60d;&#xff0c;本文主要介绍了数据结构——顺序表部分的内容 目录 一、线性表的定义 二、线性表的基本操作 三.顺序表 1.定义 2. 存储结构 3. 特点 四 顺序表操作 4.1初始化 4.2 插入 4.2.1头插 4.2.2 尾插 4.2.3 按位置插 4.3 …