HarmonyOS 5.0应用开发——MVVM模式的应用
【高心星出品】
文章目录
- MVVM模式的应用
- ArkUI开发模式图
- 架构设计原则
- 案例
- 运行效果
- 项目结构
- 功能特性
- 开发环境
- model层
- viewmodel层
- view层
MVVM模式的应用
MVVM(Model-View-ViewModel)模式是一种广泛用于应用开发的架构模式,它有助于分离应用程序的业务逻辑、数据和用户界面。在鸿蒙(HarmonyOS)开发中,MVVM模式被广泛应用,特别是在使用ArkUI框架时。
ArkUI采用了 Model-View-ViewModel(MVVM)架构模式。MVVM 将应用分为Model、View和ViewModel三个核心部分,实现数据、视图与逻辑的分离。通过这种模式,UI可以随着状态的变化自动更新,无需手动处理,从而更加高效地管理数据和视图的绑定与更新。
- Model:负责存储和管理应用的数据以及业务逻辑,不直接与用户界面交互。通常从后端接口获取数据,是应用程序的数据基础,确保数据的一致性和完整性。
- View:负责用户界面展示数据并与用户交互,不包含任何业务逻辑。它通过绑定ViewModel层提供的数据来动态更新UI。
- ViewModel:负责管理UI状态和交互逻辑。作为连接Model和View的桥梁,通常一个View对应一个ViewModel,ViewModel监控Model数据的变化,通知View更新UI,同时处理用户交互事件并转换为数据操作。
ArkUI开发模式图
ArkUI的UI开发开发模式即是MVVM模式,而状态变量在MVVM模式中扮演着ViewModel的角色,向上刷新UI,向下更新数据,整体框架如下图:
架构设计原则
不可跨层访问
- View层不可以直接调用Model层的数据,只能通过ViewModel提供的方法进行调用。
- Model层数据,不可以直接操作UI,Model层只能通知ViewModel层数据有更新,由ViewModel层更新对应的数据。
下层不可访问上层数据
下层的数据通过通知模式更新上层数据。在业务逻辑中,下层不可直接写代码去获取上层数据。如ViewModel层的逻辑处理,不能去依赖View层界面上的某个值。
非父子组件间不可直接访问
这是针对View层设计的核心原则,一个组件应该具备这样的逻辑:
- 禁止直接访问父组件(必须使用事件或是订阅能力)。
- 禁止直接访问兄弟组件能力。这是因为组件应该仅能访问自己看的见的子节点(通过传参)和父节点(通过事件或通知),以此完成组件之间的解耦。
对于一个组件,这样设计的原因是:
- 组件自己使用了哪些子组件是明确的,因此可以访问。
- 组件被放置于哪个父节点下是未知的,因此组件想访问父节点,就只能通过通知或者事件能力完成。
- 组件不可能知道自己的兄弟节点是谁,因此组件不可以操纵兄弟节点。
案例
使用MVVM模式开发一个备忘录应用。
运行效果
项目结构
mvvmdemo/
├── src/
│ └── main/
│ ├── ets/
│ │ ├── common/ # 公共组件和常量
│ │ ├── entryability/ # 入口能力
│ │ ├── model/ # 数据模型
│ │ ├── utils/ # 工具类
│ │ ├── view/ # 视图组件
│ │ └── viewmodel/ # 视图模型
│ └── resources/ # 资源文件
功能特性
- 显示待办事项列表
- 添加新的待办事项
- 标记待办事项为已完成/未完成
- 删除待办事项
- 错误处理和加载状态显示
开发环境
- DevEco Studio 5.0或更高版本
- HarmonyOS SDK API 14
- ArkTS
model层
负责描述每一条item数据的结构以及数据的业务逻辑。
TodoItem:
export class TodoItem {id: number;title: string;completed: boolean;createTime: string;constructor(id:number,title: string) {this.id=idthis.title = title;this.completed = false;this.createTime = new Date().toLocaleString();}toggleComplete(): void {this.completed = !this.completed;}
}
TodoService:
import { TodoItem } from './TodoItem';/*** 待办事项服务类* 负责数据的增删改查操作*/
export class TodoService {private static instance: TodoService;private todoList: TodoItem[] = [];private constructor() {// 初始化一些示例数据this.todoList = [new TodoItem(1,'学习鸿蒙开发'),new TodoItem(2,'完成MVVM示例项目'),new TodoItem(3,'阅读鸿蒙文档')];}/*** 获取单例实例*/public static getInstance(): TodoService {if (!TodoService.instance) {TodoService.instance = new TodoService();}return TodoService.instance;}/*** 获取所有待办事项*/public getAllTodos(): TodoItem[] {return [...this.todoList]}/*** 添加待办事项*/public addTodo(title: string) {const todo = new TodoItem(this.todoList[this.todoList.length-1].id+1,title);this.todoList.push(todo);}/*** 删除待办事项*/public deleteTodo(id: number): boolean {const index = this.todoList.findIndex(item => item.id === id);if (index !== -1) {this.todoList.splice(index, 1);return true;}return false;}/*** 切换待办事项状态*/public toggleTodo(id: number): boolean {const todo = this.todoList.find(item => item.id === id);if (todo) {todo.toggleComplete();return true;}return false;}
}
viewmodel层
TodoViewModel:
import { TodoItem } from '../model/TodoItem';
import { TodoService } from '../model/TodoService';/*** 待办事项ViewModel类* 负责连接Model和View,处理业务逻辑*/
@Observedexport class TodoViewModel {private todoService: TodoService;@TracktodoList: TodoItem[] = [];@TracknewTodoTitle: string = '';@TrackisLoading: boolean = false;@TrackerrorMessage: string = '';constructor() {this.todoService = TodoService.getInstance();this.loadTodos();}/*** 加载所有待办事项*/public loadTodos(): void {this.isLoading = true;this.errorMessage = '';try {this.todoList = this.todoService.getAllTodos();} catch (error) {this.errorMessage = '加载待办事项失败';console.error('加载待办事项失败:', error);} finally {this.isLoading = false;}}/*** 添加待办事项*/public async addTodo(){if (!this.newTodoTitle.trim()) {this.errorMessage = '待办事项不能为空';return;}try {this.todoService.addTodo(this.newTodoTitle);this.todoList=this.todoService.getAllTodos()console.log('gxxt add ',JSON.stringify(this.todoList))this.newTodoTitle = '';this.errorMessage = '';} catch (error) {this.errorMessage = '添加待办事项失败';console.error('添加待办事项失败:', error);}}/*** 删除待办事项*/public deleteTodo(id: number): void {try {if (this.todoService.deleteTodo(id)) {this.todoList=this.todoService.getAllTodos()console.log('gxxt del ',JSON.stringify(this.todoList))}} catch (error) {this.errorMessage = '删除待办事项失败';console.error('删除待办事项失败:', error);}}/*** 切换待办事项状态*/public toggleTodo(id: number): void {try {if (this.todoService.toggleTodo(id)) {// 更新列表中的项目状态console.log('gxxt todolist ',JSON.stringify(this.todoList))this.todoList=this.todoService.getAllTodos()}} catch (error) {this.errorMessage = '更新待办事项状态失败';console.error('更新待办事项状态失败:', error);}}/*** 设置新待办事项标题*/public setNewTodoTitle(title: string): void {this.newTodoTitle = title;}/*** 清除错误信息*/public clearError(): void {this.errorMessage = '';}
}
view层
TodoPage:
import { TodoItem } from '../model/TodoItem';
import { TodoItemView } from '../view/TodoItemView';
import { TodoViewModel } from '../viewmodel/TodoViewModel';/*** 待办事项主页面*/
@Entry
@Component
struct TodoPage {@State private viewModel: TodoViewModel = new TodoViewModel();build() {Column() {// 标题栏Row() {Text('待办事项').fontSize(24).fontWeight(FontWeight.Bold)}.width('100%').height(60).padding({ left: 20, right: 20 }).backgroundColor('#F5F5F5')// 添加待办事项区域Row() {TextInput({ placeholder: '输入新的待办事项...' }).width('80%').height(40).backgroundColor('#FFFFFF').onChange((value: string) => {this.viewModel.setNewTodoTitle(value);})Button('添加').width('20%').height(40).backgroundColor('#007DFF').onClick(() => {this.viewModel.addTodo();})}.width('90%').margin({ top: 20, bottom: 10 })// 错误信息if (this.viewModel.errorMessage) {Text(this.viewModel.errorMessage).fontSize(14).fontColor('#FF0000').margin({ bottom: 10 })}// 待办事项列表if (this.viewModel.isLoading) {LoadingProgress().width(50).height(50).margin({ top: 50 })} else {List() {ForEach(this.viewModel.todoList, (item:TodoItem) => {ListItem() {TodoItemView({todo: item,onToggle: (id) => this.viewModel.toggleTodo(id),onDelete: (id) => this.viewModel.deleteTodo(id)})}})}.width('90%').layoutWeight(1)}}.width('100%').height('100%').backgroundColor('#FFFFFF')}
}
TodoItemView:
import { TodoItem } from '../model/TodoItem';/*** 待办事项视图组件*/
@Component
export struct TodoItemView {@State private todo: TodoItem=new TodoItem(0,'')private onToggle: ((id: number) => void)=(id:number)=>{};private onDelete: ((id: number) => void)=(id:number)=>{};build() {Row() {// 复选框Toggle({ type: ToggleType.Checkbox, isOn: this.todo.completed }).onChange(() => {this.onToggle(this.todo.id);}).margin({ right: 10 })// 待办事项标题Text(this.todo.title).fontSize(16).fontColor(this.todo.completed ? '#999999' : '#000000').decoration({ type:this.todo.completed ? TextDecorationType.LineThrough : TextDecorationType.None}).layoutWeight(1)// 删除按钮Button() {Image($r('sys.media.ohos_ic_public_device_watch')).width(20).height(20)}.type(ButtonType.Circle).backgroundColor(Color.Transparent).onClick(() => {this.onDelete(this.todo.id);})}.width('100%').padding(10).backgroundColor('#F5F5F5').borderRadius(8).margin({ bottom: 10 })}
}
相关文章:

HarmonyOS 5.0应用开发——MVVM模式的应用
【高心星出品】 文章目录 MVVM模式的应用ArkUI开发模式图架构设计原则案例运行效果项目结构功能特性开发环境model层viewmodel层view层 MVVM模式的应用 MVVM(Model-View-ViewModel)模式是一种广泛用于应用开发的架构模式,它有助于分离应用程…...

程序员鱼皮最新项目-----AI超级智能体教程(一)
文章目录 1.前言1.什么是AI大模型2.什么是多模态3.阿里云百炼平台介绍3.1文本调试展示3.2阿里云和dashscope的关系3.3平台智能体应用3.4工作流的创建3.5智能体编排应用 1.前言 最近鱼皮大佬出了一套关于这个AI 的教程,关注鱼皮大佬很久了,鱼皮大佬确实在…...

【AI模型学习】双流网络——更强大的网络设计
文章目录 一 背景1.1 背景1.2 研究目标 二 模型2.1 双流架构2.2 光流 三 实验四 思考4.1 多流架构4.2 fusion策略4.3 fusion的early与late 先简单聊了双流网络最初在视频中的起源,之后把重点放在 “多流结构"和"fusion” 上。 一 背景 1.1 背景 Two-Str…...

HarmonyOS:一多能力介绍:一次开发,多端部署
概述 如果一个应用需要在多个设备上提供同样的内容,则需要适配不同的屏幕尺寸和硬件,开发成本较高。HarmonyOS 系统面向多终端提供了“一次开发,多端部署”(后文中简称为“一多”)的能力,可以基于一种设计…...

“在中国,为中国” 英飞凌汽车业务正式发布中国本土化战略
3月28日,以“夯实电动化,推进智能化,实现高质量发展”为主题的2025中国电动汽车百人会论坛在北京举办。众多中外机构与行业上下游嘉宾就全球及中国汽车电动化的发展现状、面临的挑战与机遇,以及在技术创新、市场布局、供应链协同等…...
《Pinia 从入门到精通》Vue 3 官方状态管理 -- 基础入门篇
《Pinia 从入门到精通》Vue 3 官方状态管理 – 基础入门篇 《Pinia 从入门到精通》Vue 3 官方状态管理 – 进阶使用篇 《Pinia 从入门到精通》Vue 3 官方状态管理 – 插件扩展篇 📖 教程目录 为什么选择 Pinia?1.1 背景介绍1.2 Vuex 的痛点(对…...

Java技术体系的主要产品线详解
Java技术体系的主要产品线详解 Java Card:支持Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。 Java ME(Micro Edition):支持Java程序运行在移动终端(手机、P…...

机器学习快速入门--0算力起步实践篇
在学习人工智能的过程中,显卡是必不可少的工具,但它的成本较高且更新换代速度很快。那么,没有GPU的情况下如何学习人工智能呢?以下是针对普通电脑与有算力环境分离的学习规划方案,尤其适合前期无GPU/云计算资源的学习者…...
MySQL 详解之索引:提升查询效率的秘密武器
在数据库的世界里,数据量通常是巨大的。想象一下,一个拥有数百万甚至数十亿条记录的表格,如果你需要从中查找符合特定条件的几条甚至一条记录,数据库是如何快速找到它们的呢?如果没有高效的机制,数据库不得不一条条地遍历整个表格,这无疑会非常缓慢和耗费资源。这时,索…...
中通 Redis 集群从 VM 迁移至 PVE:技术差异、PVE 优劣势及应用场景深度解析
在数字化转型浪潮下,企业对服务器资源的高效利用与成本控制愈发重视。近期,中通快递将服务器上的 Redis 集群服务从 VM(VMware 虚拟化技术)迁移至 PVE(Proxmox VE),这一技术举措引发了行业广泛关…...

源码篇 剖析 Vue2 双向绑定原理
前置操作 源码代码仓地址:https://github.com/vuejs/vue/tree/main 1.查看源码当前版本 当前版本为 v2.7.16 2.Clone 代码 在【Code】位置点击,复制 URL 用于 Clone 代码 3.执行 npm install 4.执行 npm run dev 前言 在 Vue 中最经典的问题就是双…...
Restful接口学习
一、为什么RESTful接口是数据开发的核心枢纽? 在数据驱动的时代,RESTful接口如同数据高速公路上的收费站,承担着数据交换的核心职责。数据工程师每天需要面对: 异构系统间的数据交互(Hadoop集群 ↔ 业务系统…...
C++ round 函数笔记 (适用于算法竞赛)
在算法竞赛中,处理浮点数并将其转换为整数是常见的需求,round 函数是标准库提供的用于执行“四舍五入”到最近整数的工具。理解其工作方式和潜在问题对于避免错误至关重要。 1. 基本用法 头文件 要使用 round 函数,需要包含 <cmath>…...
1.5软考系统架构设计师:架构师的角色与能力要求 - 超简记忆要点、知识体系全解、考点深度解析、真题训练附答案及解析
超简记忆要点 角色职责 需求规划→架构设计→质量保障 能力要求 技术(架构模式/性能优化) 业务(模型抽象→技术方案) 管理(团队协作/风险控制) 知识体系 基础:CAP/设计模式/网络协议案例&am…...

单例模式与消费者生产者模型,以及线程池的基本认识与模拟实现
前言 今天我们就来讲讲什么是单例模式与线程池的相关知识,这两个内容也是我们多线程中比较重要的内容。其次单例模式也是我们常见设计模式。 单例模式 那么什么是单例模式呢?上面说到的设计模式又是什么? 其实单例模式就是设计模式的一种。…...
JAVA程序获取SVN提交记录
1.获取文件提交记录 private String userName "userName "; //svn账号 private String password "password "; //svn密码 private String urlString "urlString "; //svnurl 换成自己对应的svn信息 package com.tengzhi.common.dao;import…...

STM32配置系统时钟
1、STM32配置系统时钟的步骤 1、系统时钟配置步骤 先配置系统时钟,后面的总线才能使用时钟频率 2、外设时钟使能和失能 STM32为了低功耗,一开始是关闭了所有的外设的时钟,所以外设想要工作,首先就要打开时钟,所以后面…...

React 与 Vue:两大前端框架的深度对比
在前端开发领域,React 和 Vue 无疑是当下最受欢迎的两大框架。它们各自拥有独特的优势和特点,吸引了大量开发者。无论是初学者还是经验丰富的工程师,选择 React 还是 Vue 都是一个常见的问题。本文将从多个角度对 React 和 Vue 进行对比&…...
Node.js 学习入门指南
Node.js 学习入门指南 Node.js 是一种流行的开源、跨平台的 JavaScript 运行时环境,它使开发者能够在服务器端运行JavaScript代码。本篇文章旨在帮助初学者快速入门并掌握Node.js的基础知识和常用技巧。 一、什么是Node.js? 定义 Node.js 是一个基于…...

Java24新增特性
Java 24(Oracle JDK 24)作为Java生态的重要更新,聚焦AI开发支持、后量子安全、性能优化及开发者效率提升,带来20余项新特性和数千项改进。以下是核心特性的分类解析: 一、语言特性增强:简化代码与模式匹配 …...

Sentinel源码—6.熔断降级和数据统计的实现一
大纲 1.DegradeSlot实现熔断降级的原理与源码 2.Sentinel数据指标统计的滑动窗口算法 1.DegradeSlot实现熔断降级的原理与源码 (1)熔断降级规则DegradeRule的配置Demo (2)注册熔断降级监听器和加载熔断降级规则 (3)DegradeSlot根据熔断降级规则对请求进行验证 (1)熔断降级…...

Volcano 实战快速入门 (一)
一、技术背景 随着大型语言模型(LLM)的蓬勃发展,其在 Kubernetes (K8s) 环境下的训练和推理对资源调度与管理提出了前所未有的挑战。这些挑战主要源于 LLM 对计算资源(尤其是 GPU)的巨大需求、分布式任务固有的复杂依…...
快速体验tftp文件传输(嵌入式设备)
一、参考资料 Linux tftp 命令 | 菜鸟教程 Ubuntu最新版本(Ubuntu22.04LTS)安装Tftp服务及其使用教程-CSDN博客 Windows下的Tftpd32(Tftpd64)软件下载和使用教程-集成了Tftp服务器、客户端-CSDN博客 tftpd32 tftpd64文件传输安装和使用教程【图文并茂】-CSDN博客 二、快速…...

用交换机连接两台电脑,电脑A读取/写电脑B的数据
1、第一步,打开控制面板中的网络和共享中心,如下图配置,电脑A和电脑B均要配置; 注意:要保证电脑A和电脑B在同一子网掩码下,不同的IP地址; 2、在电脑上同时按‘CommandR’,在弹出的输…...

问道数码兽 怀旧剧情回合手游源码搭建教程(反查重优化版)
本文将对"问道数码兽"这一经典卡通风格回合制手游的服务端部署与客户端调整流程进行详细拆解,适用于具备基础 Windows 运维和手游源码调试经验的开发者参考使用。教程以实战为导向,基于原始说明内容重构优化,具备较高的内容查重避重…...

WLAN共享给以太网后以太网IP为169.254.xx.xx以及uboot无法使用nfs下载命令的的解决方案
WLAN共享网络给以太网,实际上是把以太网口当作一个路由器,这个路由器的IP是由WLAN给他分配的,169.254.xx.xx是windows设定的ip,当网络接口无法从上一级网络接口获得ip时,该网络接口的ip被设置为169.254 ,所…...
Gazebo 仿真环境系列教程(一):环境安装与基础使用
文章目录 一、版本说明与技术背景1.1 Gazebo 版本分支1.2 版本选择建议 二、系统环境准备2.1 硬件要求2.2 软件依赖 三、Gazebo Garden 安装流程3.1 添加官方软件源3.2 执行安装命令3.3 环境验证 四、Gazebo Classic 安装方法4.1 添加软件仓库4.2 安装核心组件4.3 验证安装 五、…...

ROS 快速入门教程03
8.编写Subscriber订阅者节点 8.1 创建订阅者节点 cd catkin_ws/src/ catkin_create_pkg atr_pkg rospy roscpp std_msgs ros::Subscriber sub nh.subscribe(话题名, 缓存队列长度, 回调函数) 回调函数通常在你创建订阅者时定义。一个订阅者会监听一个话题,并在有…...

在 macOS 上合并 IntelliJ IDEA 的项目窗口
在使用 IntelliJ IDEA 开发时,可能会打开多个项目窗口,这可能会导致界面变得混乱。为了提高工作效率,可以通过合并项目窗口来简化界面。本文将介绍如何在 macOS 上合并 IntelliJ IDEA 的项目窗口。 操作步骤 打开 IntelliJ IDEA: 启动你的 I…...
SEO(Search Engine Optimization,搜索引擎优化)相关知识点
SEO(Search Engine Optimization)是指搜索引擎优化,是计算机领域中通过技术手段和内容策略,提升网站在搜索引擎(如Google、Bing、百度)中自然(非付费)排名的系统性方法。是一种通过优…...