【鸿蒙学习笔记】MVVM模式
官方文档:MVVM模式
[Q&A] 什么是MVVM
ArkUI采取MVVM = Model + View + ViewModel模式。
Model层:存储数据和相关逻辑的模型。View层:在ArkUI中通常是@Component装饰组件渲染的UI。ViewModel层:在ArkUI中,ViewModel是存储在自定义组件的状态变量、LocalStorage和AppStorage中的数据。

MVVM应用示例
开发一个电话簿应用,实现功能如下:
- 显示联系人和设备(“Me”)电话号码 。
- 选中联系人时,进入可编辑态“Edit”,可以更新该联系人详细信息,包括电话号码,住址。
- 在更新联系人信息时,只有在单击保存“Save Changes”之后,才会保存更改。
- 可以点击删除联系人“Delete Contact”,可以在联系人列表删除该联系人。
Model
Address,Person,AddressBook,MyArray
ViewModel
PersonView,phonesNumber,PersonEditView,AddressBookView
Vidw
PracExample
// ViewModel classes
let nextId = 0;@Observed
export class MyArray<T> extends Array<T> {constructor(args: T[]) {console.log(`MyArray: ${JSON.stringify(args)} `)if (args instanceof Array) {super(...args);} else {super(args)}}
}@Observed
export class Address {street: string;zip: number;city: string;constructor(street: string,zip: number,city: string) {this.street = street;this.zip = zip;this.city = city;}
}@Observed
export class Person {id_: string;name: string;address: Address;phones: MyArray<string>;constructor(name: string,street: string,zip: number,city: string,phones: string[]) {this.id_ = `${nextId}`;nextId++;this.name = name;this.address = new Address(street, zip, city);this.phones = new MyArray<string>(phones);}
}export class AddressBook {me: Person;friends: MyArray<Person>;constructor(me: Person, contacts: Person[]) {this.me = me;this.friends = new MyArray<Person>(contacts);}
}// 渲染出Person对象的名称和Observed数组<string>中的第一个号码
// 为了更新电话号码,这里需要@ObjectLink person和@ObjectLink phones,
// 不能使用this.person.phones,内部数组的更改不会被观察到。
// 在AddressBookView、PersonEditView中的onClick更新selectedPerson
@Component
struct PersonView {@ObjectLink person: Person;@ObjectLink phones: MyArray<string>;@Link selectedPerson: Person;build() {Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {Text(this.person.name)if (this.phones.length) {Text(this.phones[0])}}.height(55).backgroundColor(this.selectedPerson.name == this.person.name ? Color.Pink : "#ffffff").onClick(() => {this.selectedPerson = this.person;})}
}@Component
struct phonesNumber {@ObjectLink phoneNumber: MyArray<string>build() {Column() {ForEach(this.phoneNumber,(phone: ResourceStr, index?: number) => {TextInput({ text: phone }).width(150).onChange((value) => {console.log(`${index}. ${value} value has changed`)this.phoneNumber[index!] = value;})},(phone: ResourceStr, index: number) => `${this.phoneNumber[index] + index}`)}}
}// 渲染Person的详细信息
// @Prop装饰的变量从父组件AddressBookView深拷贝数据,将变化保留在本地, TextInput的变化只会在本地副本上进行修改。
// 点击 "Save Changes" 会将所有数据的复制通过@Prop到@Link, 同步到其他组件
@Component
struct PersonEditView {@Consume addrBook: AddressBook;/* 指向父组件selectedPerson的引用 */@Link selectedPerson: Person;/*在本地副本上编辑,直到点击保存*/@Prop name: string = "";@Prop address: Address = new Address("", 0, "");@Prop phones: MyArray<string> = [];selectedPersonIndex(): number {return this.addrBook.friends.findIndex((person: Person) => person.id_ == this.selectedPerson.id_);}build() {Column() {TextInput({ text: this.name }).onChange((value) => {this.name = value;})TextInput({ text: this.address.street }).onChange((value) => {this.address.street = value;})TextInput({ text: this.address.city }).onChange((value) => {this.address.city = value;})TextInput({ text: this.address.zip.toString() }).onChange((value) => {const result = Number.parseInt(value);this.address.zip = Number.isNaN(result) ? 0 : result;})if (this.phones.length > 0) {phonesNumber({ phoneNumber: this.phones })}Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {Text("Save Changes").onClick(() => {// 将本地副本更新的值赋值给指向父组件selectedPerson的引用// 避免创建新对象,在现有属性上进行修改this.selectedPerson.name = this.name;this.selectedPerson.address = new Address(this.address.street, this.address.zip, this.address.city)this.phones.forEach((phone: string, index: number) => {this.selectedPerson.phones[index] = phone});})if (this.selectedPersonIndex() != -1) {Text("Delete Contact").onClick(() => {let index = this.selectedPersonIndex();console.log(`delete contact at index ${index}`);// 删除当前联系人this.addrBook.friends.splice(index, 1);// 删除当前selectedPerson,选中态前移一位index = (index < this.addrBook.friends.length) ? index : index - 1;// 如果contract被删除完,则设置me为选中态this.selectedPerson = (index >= 0) ? this.addrBook.friends[index] : this.addrBook.me;})}}}}
}@Component
struct AddressBookView {@ObjectLink me: Person;@ObjectLink contacts: MyArray<Person>;@State selectedPerson: Person = new Person("", "", 0, "", []);aboutToAppear() {this.selectedPerson = this.me;}build() {Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) {Text("Me:")PersonView({person: this.me,phones: this.me.phones,selectedPerson: this.selectedPerson})Divider().height(8)ForEach(this.contacts, (contact: Person) => {PersonView({person: contact,phones: contact.phones as MyArray<string>,selectedPerson: this.selectedPerson})}, (contact: Person): string => {return contact.id_;})Divider().height(8)Text("Edit:")PersonEditView({selectedPerson: this.selectedPerson,name: this.selectedPerson.name,address: this.selectedPerson.address,phones: this.selectedPerson.phones})}.borderStyle(BorderStyle.Solid).borderWidth(5).borderColor(0xAFEEEE).borderRadius(5)}
}@Entry
@Component
struct PracExample {@Provide addrBook: AddressBook = new AddressBook(new Person("卧龙", "南路 9", 180, "大连", ["18*********", "18*********", "18*********"]),[new Person("小华", "东路 9", 180, "大连", ["11*********", "12*********"]),new Person("小刚", "西边路 9", 180, "大连", ["13*********", "14*********"]),new Person("小红", "南方街 9", 180, "大连", ["15*********", "168*********"]),]);build() {Column() {AddressBookView({me: this.addrBook.me,contacts: this.addrBook.friends,selectedPerson: this.addrBook.me})}}
}

相关文章:
【鸿蒙学习笔记】MVVM模式
官方文档:MVVM模式 [Q&A] 什么是MVVM ArkUI采取MVVM Model View ViewModel模式。 Model层:存储数据和相关逻辑的模型。View层:在ArkUI中通常是Component装饰组件渲染的UI。ViewModel层:在ArkUI中,ViewModel是…...
端、边、云三级算力网络
目录 端、边、云三级算力网络 NPU Arm架构 OpenStack kubernetes k3s轻量级Kubernetes kubernetes和docker区别 DCI(Data Center Interconnect) SD/WAN TF 端、边、云三级算力网络 算力网络从传统云网融合的角度出发,结合 边缘计算、网络云化以及智能控制的优势,通…...
java —— JSP 技术
一、JSP (一)前言 1、.jsp 与 .html 一样属于前端内容,创建在 WebContent 之下; 2、嵌套的 java 语句放置在<% %>里面; 3、嵌套 java 语句的三种语法: ① 脚本:<% java 代码 %>…...
【Python学习笔记】菜鸟教程Scrapy案例 + B站amazon案例视频
背景前摇(省流可以跳过这部分) 实习的时候厚脸皮请教了一位办公室负责做爬虫这块的老师,给我推荐了Scrapy框架。 我之前学过一些爬虫基础,但是用的是比较常见的BeautifulSoup和Request,于是得到Scrapy这个关键词后&am…...
Pycharm的终端(Terminal)中切换到当前项目所在的虚拟环境
1.在Pycharm最下端点击终端/Terminal, 2.点击终端窗口最上端最右边的∨, 3.点击Command Prompt,切换环境, 可以看到现在环境已经由默认的PS(Window PowerShell)切换为项目所使用的虚拟环境。 4.更近一步,如果想让Pycharm默认显示…...
Nginx 高效加速策略:动静分离与缓存详解
在现代Web开发中,网站性能是衡量用户体验的关键指标之一。Nginx,以其出色的性能和灵活性,成为众多网站架构中不可或缺的一部分。本文将深度解析如何利用Nginx实现动静分离与缓存,从而大幅提升网站加载速度和响应效率。 理解动静分…...
Unity3D 游戏摇杆的制作与实现详解
在Unity3D游戏开发中,摇杆是一种非常常见的输入方式,特别适用于移动设备的游戏控制。本文将详细介绍如何在Unity3D中制作和实现一个虚拟摇杆,包括技术详解和代码实现。 对惹,这里有一个游戏开发交流小组,大家可以点击…...
从nginx返回404来看http1.0和http1.1的区别
序言 什么样的人可以称之为有智慧的人呢?如果下一个定义,你会如何来定义? 所谓智慧,就是能区分自己能改变的部分,自己无法改变的部分,努力去做自己能改变的,而不要天天想着那些无法改变的东西&a…...
MySQL 代理层:ProxySQL
文章目录 说明安装部署1.1 yum 安装1.2 启停管理1.3 查询版本1.4 Admin 管理接口 入门体验功能介绍3.1 多层次配置系统 读写分离将实例接入到代理服务定义主机组之间的复制关系配置路由规则事务读的配置延迟阈值和请求转发 ProxySQL 核心表mysql_usersmysql_serversmysql_repli…...
异步主从复制
主从复制的概念 主从复制是一种在数据库系统中常用的数据备份和读取扩展技术,通过将一个数据库服务器(主服务器)上的数据变更自动同步到一个或多个数据库服务器(从服务器)上,以此来实现数据的冗余备份、读…...
论文解析——Full Stack Optimization of Transformer Inference: a Survey
作者及发刊详情 摘要 正文 主要工作贡献 这篇文章的贡献主要有两部分: 分析Transformer的特征,调查高效transformer推理的方法通过应用方法学展现一个DNN加速器生成器Gemmini的case研究 1)分析和解析Transformer架构的运行时特性和瓶颈…...
selenium处理cookie问题实战
1. cookie获取不完整 需要进入的资损平台(web)首页,才会出现有效的ctoken等信息 1.1. 原因说明 未进入指定页面而获取的 cookie 与进入页面后获取的 cookie 可能会有一些差异,这取决于网站的具体实现和 cookie 的设置方式。 通常情况下,一些…...
(十五)GLM库对矩阵操作
GLM简单使用 glm是一个开源的对矩阵运算的库,下载地址: https://github.com/g-truc/glm/releases 直接包含其头文件即可使用: #include <glad/glad.h>//glad必须在glfw头文件之前包含 #include <GLFW/glfw3.h> #include <io…...
android中activity与fragment之间的各种跳转
我们以音乐播放、视频播放、用户注册与登录为例【Musicfragment(音乐列表页)、Videofragment(视频列表页)、MusicAvtivity(音乐详情页)、VideoFragment(视频详情页)、LoginActivity&…...
动态规划算法-以中学排课管理系统为例
1.动态规划算法介绍 1.算法思路 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若…...
本安防爆手机:危险环境下的安全通信解决方案
在石油化工、煤矿、天然气等危险环境中,通信安全是保障工作人员生命安全和生产顺利进行的关键。防爆智能手机作为专为这些环境设计的通信工具,提供了全方位的安全通信解决方案。 防爆设计与材料: 防爆智能手机采用特殊的防爆结构和材料&…...
算法学习笔记(8)-动态规划基础篇
目录 基础内容: 动态规划: 动态规划理解的问题引入: 解析:(暴力回溯) 代码示例: 暴力搜索: Dfs代码示例:(搜索) 暴力递归产生的递归树&…...
数据库常见问题(持续更新)
数据库常见问题(持续更新) 1、数据库范式? 1NF:不可分割2NF:没有非主属性对候选码存在部分依赖3NF:没有非主属性传递依赖候选码BCNF:消除了主属性对对候选码的传递依赖或部分依赖 2、InnoDB事务的实现? …...
定个小目标之刷LeetCode热题(40)
94. 二叉树的中序遍历 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 直接上代码吧,中序遍历左根右 class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res new ArrayList<Integer>(…...
Linux--线程(概念篇)
目录 1.背景知识 再谈地址空间: 关于页表(32bit机器上) 2.线程的概念和Linux中线程的实现 概念部分: 代码部分: 问题: 3.关于线程的有点与缺点 4.进程VS线程 1.背景知识 再谈地址空间:…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
