重生之我在大学自学鸿蒙开发第二天-《MVVM模式》 - 教程

news/2025/11/8 15:00:52/文章来源:https://www.cnblogs.com/yxysuanfa/p/19202328

  • 个人主页:VON
  • 文章所属专栏:从0开始的开源鸿蒙6.0.0
  • 个人抖音:清洒

上一篇:

重生之我在大学自学鸿蒙开发第一天-《基础篇》-CSDN博客

目录

一、前言

二、实践

2.1、简述MVVM

2.2、改造model文件

2.3、改造view文件

2.4、改造rawfile文件夹

1、将获取的buffer内容转换为字符串

2、将字符串转换为页面数据结构

3、整体逻辑如下

4、页面的生命周期执行流程

2.5、改造Banner

数据获取

2.6、改造EnablementView和TutorialView

三、测试

四、源码展示

ArticleClass

BannerClass

Index

BufferUtil

EnablementView

TutorialView


一、前言

在上一篇文章中完成了单独页面的创建,并没有进行分层,导致代码十分冗余,如果想要开发更复杂的APP肯定不能这样写,开发过java的都知道分为三层架构,鸿蒙也是这样也是三层架构的模式,本篇文章主要讲解应用架构设计基础——MVVM模式。废话不多说直接开肝!

官方文档:应用架构设计基础——MVVM模式-HarmonyOS应用开发快速入门-Codelabs-华为开发者联盟

二、实践

本章内容并不复杂,主要三层架构模式见下一章

2.1、简述MVVM

MVVM = Model + View + ViewModel模式,其中状态管理模块起到的就是ViewModel的作用,将数据与视图绑定在一起,更新数据的时候直接更新视图。

2.2、改造model文件

model文件夹用于存储数据模型。它表示组件或其他相关业务逻辑之间传输的数据,是对原始数据的进一步处理。

创建文件的时候看清楚,我刚开始就没看清点击的上面那个导致没有创建成功,这里大家注意下。

这里将Class给提取出来放到model文件中。

导出的时候别忘了使用export进行导出然后再直接import导入即可

2.3、改造view文件

view文件夹主要用于存储UI组件,也就是昨天所写的EnablementView.ets和TutorialView.ets以及Banner.ets组件。

直接将index中的代码拿过来即可,这里由于代码量太多了就将代码折叠起来了。

2.4、改造rawfile文件夹

官方说明:rawfile目录中的资源文件会被直接打包进应用,不经过编译,也不会被赋予资源文件ID。通过指定文件路径和文件名引用。

第一次听说这个文件,好像是用于存储json数据的文件夹。

可以看到这是原本的数据,我们是用数组进行存储的,现在转为json格式存放再rawfile文件夹里面即可。

json格式的数据想必大家也并不陌生。

接下来引用json数据先定义一个方法getBannerDataFromJson(),并通过ResourceManager获取当前工程目录下rawfile中的json文件内容。

转换内容需要两个步骤:

1、将获取的buffer内容转换为字符串

由于ResourceManager获取到的是Uint8Array类型的内容,所以需要将对应的内容转换为字符串,并将字符串解析为对应的数据结构。考虑到其他的文件也会使用这个公共方法,可以新建一个util文件夹,并创建一个BufferUtil文件,实现这个字符串转换方法。

果然这里需要进行转换

2、将字符串转换为页面数据结构

3、整体逻辑如下

这里的函数不用刻意去记,可以当成一个模板来进行套用,其他两个组件也可以使用此方法来进行转换,只要知道思路即可。

4、页面的生命周期执行流程

2.5、改造Banner

通过上面的生命周期不难发现,aboutToAppear要先于build,做过vue项目的同学应该都清楚,加载页面之前要先获取数据,就像vue中的onMountd(),所以我们这里也要先获取数据

数据获取

官方解释:json中数据由于无法使用$r()进行资源访问,所以使用的是字符串"app.media.banner_pic0",而在页面中直接声明时,使用的是$r('app.media.banner_pic0'),而Image组件是无法直接读取字符串"app.media.banner_pic0"的,所以这里需要进行内容调整。

这里要将imageSrc数据类型修改为string(注意下这里的s是小写)

别忘了ArticleClass类中也要进行修改

这里的图片引用的方式也需要改变下

2.6、改造EnablementView和TutorialView

这两个文件的引用一样,只需要改下名字即可。

三、测试

接下来开始进行真机测试,我这里使用模拟机来进行测试

我这里启动虚拟机的时候出了点问题,因为我的电脑没有开启虚拟化,大家可以参考下面文章来进行查看

关于win11如何打开Hyper-V详解_win11开启hyper-v-CSDN博客

我这里好像无法使用命令行下载,还是直接去官网下载吧

官网下载的是真的慢,因为这是国外的软件

还是用命令行吧,如果上面文章中的命令无法使用的剋试一下这个指令

  • 使用 PowerShell 下载打开 PowerShell(管理员模式),执行以下命令下载最新版本的 Hyper 安装包:

    # 下载 Hyper 安装文件到当前目录
    Invoke-WebRequest -Uri "https://releases.hyper.is/download/win" -OutFile "hyper-setup.exe"
  • 命令行安装下载完成后,继续在 PowerShell 中执行安装命令:

    # 运行安装程序(会自动执行安装流程)
    .\hyper-setup.exe
  • 静默安装(可选)如果需要无界面静默安装,可以添加参数:

    .\hyper-setup.exe /S

调试了一下午也是终于调试成功,我这里直接将Hyper上传到网盘了,有需要的朋友可以自行提取。

通过网盘分享的文件:Hyper-Setup-3.4.1.exe
链接: https://pan.baidu.com/s/1OM67IrDC3-_Uffew5nXyIg 提取码: 9f9s

整体效果如下

这里出了点问题

注意:这里千万不能马虎,所有用到图片的地方都要这样引用!!!

四、源码展示

整体结构如下:

ArticleClass

export class ArticleClass {id: string = '';imageSrc: string = '';title: string = '';brief: string = '';webUrl: string = '';constructor(id: string, imageSrc: string, title: string, brief: string, webUrl: string) {this.id = id;this.imageSrc = imageSrc;this.title = title;this.brief = brief;this.webUrl = webUrl;}
}

BannerClass

export class BannerClass{id : string='';imageSrc : string='';url : string = 'https://blog.csdn.net/2302_80329073?type=blog';constructor(id : string,imageSrc : string,url : string) {this.id = id;this.imageSrc = imageSrc;this.url = url;}
}

Index

import { TutorialView } from '../view/TutorialView';
import { Banner } from '../view/Banner';
import { EnablementView } from '../view/EnablementView';
@Entry
@Component
struct Index {@State message: string = 'VON';build() {Column(){Text(this.message).fontSize(24).fontWeight(700).width('100%').padding({left:16}).lineHeight(33).textAlign(TextAlign.Start)Scroll(){Column(){Banner()EnablementView()TutorialView()}}.layoutWeight(1).scrollBar(BarState.Off).align(Alignment.TopStart)}.height('100%').width('100%').backgroundColor('#f1f1f1').padding({top:11,left:16,right:16});}
}

BufferUtil

import { util } from '@kit.ArkTS';
//将Uint8Array类型的内容转换为字符串
export function bufferToString(buffer: Uint8Array): string {let textDecoder = util.TextDecoder.create('utf-8', {ignoreBOM: true});let resultPut = textDecoder.decodeToString(buffer);return resultPut;
}
import { BannerClass } from '../model/BannerClass'
import { bufferToString } from '../util/BufferUtil';
// Banner组件
@Component
export struct Banner{@State bannerList:Array = [];aboutToAppear(): void {this.getBannerDataFromJSON();}// 获取json数据并转换为对应类型getBannerDataFromJSON() {this.getUIContext().getHostContext()?.resourceManager.getRawFileContent('BannerData.json').then(value => {this.bannerList = JSON.parse(bufferToString(value)) as BannerClass[];});}build() {Swiper(){ForEach(this.bannerList,(item:BannerClass,index:number)=>{Image($r(item.imageSrc)).objectFit(ImageFit.Contain).width('100%').height(260).borderRadius(16)},(item:BannerClass,index:number)=>item.id)}.autoPlay(true).loop(true).indicator(new DotIndicator().color('#f1f1f1').selectedColor('red'))}
}

EnablementView

import { ArticleClass } from '../model/ArticleClass';
import { bufferToString } from '../util/BufferUtil';
@Component
struct EnablementItem{@Prop enablementItem:ArticleClass;build() {Column(){Image($r(this.enablementItem.imageSrc)).width('100%').objectFit(ImageFit.Cover).height(96).borderRadius({topLeft:16,topRight:16})Text(this.enablementItem.title).height(19).width('100%').fontSize(14).textAlign(TextAlign.Start).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1).fontWeight(400).padding({ left: 12, right: 12 }).margin({ top: 8 })Text(this.enablementItem.brief).height(32).width('100%').fontSize(12).textAlign(TextAlign.Start).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(2).fontWeight(400).fontColor('rgba(0, 0, 0, 0.6)').padding({ left: 12, right: 12 }).margin({ top: 2 })}.width(169).height(169).borderRadius(16).backgroundColor(Color.White)}
}
@Component
export struct EnablementView {@State enablementList: Array = [];aboutToAppear(): void {this.getEnablementDataFromJSON();}// 获取json数据并转换为对应类型getEnablementDataFromJSON() {this.getUIContext().getHostContext()?.resourceManager.getRawFileContent('EnablementData.json').then(value => {this.enablementList = JSON.parse(bufferToString(value)) as ArticleClass[];});}build() {Column(){Text('赋能套件').fontColor('#182431').fontSize(16).fontWeight(500).fontFamily('HarmonyHeiTi-medium').textAlign(TextAlign.Start).padding({ left: 16 }).margin({ bottom: 8.5 })Grid() {ForEach(this.enablementList, (item: ArticleClass) => {GridItem() {EnablementItem({ enablementItem: item })}}, (item: ArticleClass) => item.id)}.rowsTemplate('1fr').columnsGap(8).scrollBar(BarState.Off).height(169).padding({ top: 2, left: 16, right: 16 })}.margin({top:18}).alignItems(HorizontalAlign.Start).width('100%')}
}

TutorialView

import { ArticleClass } from '../model/ArticleClass';
import { bufferToString } from '../util/BufferUtil';
@Component
struct TutorialItem {@Prop tutorialItem :ArticleClass;build() {Row(){Column() {Text(this.tutorialItem.title).height(19).width('100%').fontSize(14).textAlign(TextAlign.Start).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(1).fontWeight(400).margin({ top: 4 })Text(this.tutorialItem.brief).height(32).width('100%').fontSize(12).textAlign(TextAlign.Start).textOverflow({ overflow: TextOverflow.Ellipsis }).maxLines(2).fontWeight(400).fontColor('rgba(0, 0, 0, 0.6)').margin({ top: 5 })}.height('100%').layoutWeight(1).alignItems(HorizontalAlign.Start).margin({ right: 12 })Image($r(this.tutorialItem.imageSrc)).objectFit(ImageFit.Cover).height(64).width(108).borderRadius(16)}.width('100%').height(88).borderRadius(16).backgroundColor(Color.White).padding(12).alignItems(VerticalAlign.Top).margin({top: 20})}
}
@Component
export struct TutorialView {@State tutorialList: Array = [];aboutToAppear(): void {this.getTutorialDataFromJSON();}// 获取json数据并转换为对应类型getTutorialDataFromJSON() {this.getUIContext().getHostContext()?.resourceManager.getRawFileContent('TutorialData.json').then(value => {this.tutorialList = JSON.parse(bufferToString(value)) as ArticleClass[];});}build() {Column() {Text('入门教程').fontColor('#182431').fontSize(16).fontWeight(500).fontFamily('HarmonyHeiTi-medium').textAlign(TextAlign.Start).padding({ left: 16 }).margin({ bottom: 8.5 })List({ space: 12 }) {ForEach(this.tutorialList, (item: ArticleClass) => {ListItem() {TutorialItem({ tutorialItem: item })}}, (item: ArticleClass) => item.id)}.scrollBar(BarState.Off).padding({ left: 16, right: 16 })}.margin({ top: 18 }).alignItems(HorizontalAlign.Start)}
}

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

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

相关文章

禁止输入法联网_批量禁止指定目录下的程序联网

禁止输入法联网_批量禁止指定目录下的程序联网禁止EX的嗖√输入法联网本来在电脑一直用的就是搜狗输入法,但是搜狗输入法吃相越来越难看,比如会弹窗广告,游戏中心,智写,智能输入助手,搜索,这些全都不是我想要的…

虚幻引擎5 GAS制作俯视角RPG游戏 P05-05 游戏效果委托

虚幻引擎5 GAS制作俯视角RPG游戏 P05-05 游戏效果委托pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…

高性能计算-CUDA-mma-PTX

1. 简介用 mma PTX 指令实现 M16N16K16 矩阵乘法2. 代码调用1:wmma + sharedM 调用2:wmma + sharedM + padding 避免 bankcoflict 调用3:mma + sharedM + swizzle 避免 bankcoflict//A 16*16; B 16*16 //wmma 处理 …

2025年口碑好的GEO(AI搜索优)服务商解析与推荐

文章摘要 本文深入解析2025年GEO(AI搜索优)服务商的市场现状,重点推荐口碑优秀的服务商如摘星AI。内容涵盖服务商选择标准、行业趋势分析,并提供数据支持的比较,帮助用户做出明智决策。文章基于权威行业报告,旨在为…

2025年手机壳厂家革新包装技术:离心式包装机深度解析

文章摘要 本文探讨2025年手机壳行业包装技术趋势,重点解析离心式包装机在提升效率、降低成本方面的优势。基于合肥摘星人工智能应用软件有限公司的经验,分享如何通过智能包装解决方案优化手机壳生产流程,覆盖湖南省…

2025年广州工商注册公司权威推荐榜单:税务股权架构方案/工商变更/工商注销源头公司精选

在广州这座创业活力之都,每天都有大量市场主体诞生。据《2025年广州市中小微企业服务市场发展白皮书》显示,广州作为大湾区核心引擎,2024年新增市场主体超过30万户。然而,“创业第一步”——公司注册正变得日益复杂…

51单片机使用TM1638驱动的数码管键盘模块

51单片机使用TM1638驱动的数码管键盘模块带k的都是可以按键扫描的,SEG和GR是数码管段和位,STB,CLK,DIO是与数据相关的引脚数据手册有说,不管芯片连接的是共阳极数码管还是共阴极数码管,SEG都必须接阳极,GR接阴极,…

2025年专业办公空间装修公司排行

摘要 随着企业对于办公环境需求的不断提升,办公空间装修行业在2025年呈现出智能化、环保化、个性化的发展趋势。本文基于市场调研和用户口碑,整理了目前行业内前十的办公空间装修公司推荐榜单,旨在为企业主提供参考…

记一次 float64 排序失效的灵异事件

某一天的下午,我手头没什么事情,双眼迷离,正左手托着下巴空洞地盯着屏幕发呆。恍惚间,BUG反馈群冷不丁冒了消息,我定下神来看,测试同学反馈了一个排行榜的排序问题,排行榜中相同分数的玩家,后达到分数的反而排…

完整教程:TypeScript 面试题及详细答案 100题 (21-30)-- 接口(Interface)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

详细介绍:SkyDiffusion:用 BEV 视角打开街景→航拍图像合成新范式

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Blender中如何让导出的FBX模型文件同时携带多个动画片段

Blender版本:V4.5; Unity版本:团结引擎 V1.7.3; 问题描述: 起因是博主本人最近在学习Unity,使用到了Blender对3D模型进行动画片段制作,但是博主在学习过程中发现,我使用Blender导出的FBX文件一次只能携带一个动…

精美的vue流程设计器

一、vue-dawn-flow介绍 vue-dawn-flow是一款功能强大的开源流程设计器,专为 Vue.js 生态打造,完美兼容 Vue 2 和 Vue 3 框架。并且能很好的兼容vue前端所有框架。 1.1插件功能提供了一个可视化的流程设计器,你可以在…

2025年刀轮船订制厂家权威推荐榜单:斗轮清淤船/刀轮式挖泥船/小型斗轮船源头厂家精选

在内河航道维护与水利工程建设领域,刀轮船作为高效清淤装备,其作业效率直接影响工程进度与成本。据水利行业统计数据显示,2025年我国内河清淤市场规模预计达到287亿元,年增长率稳定在8%-12%。 刀轮船凭借其独特的斗…

高效地使用std::map

#include <iostream> #include <string> #include <map> using namespace std;typedef map<string, int> M; M m; const char K[] = "key";void fn1 () {auto p = m.insert({K, 0…

flask:得到get/post参数

一,得到get参数 代码: from flask import Blueprint,jsonify,render_template,requestuser = Blueprint(user, __name__)# 用蓝图注册路由 @user.route("/add/") def user_add():# 得到get参数name = requ…

YACS2025年10月甲组

YACS2025年10月甲组T1. 数据结构 注意到可以离线,考虑整体二分。每次执行前一半操作,如果发现超过了 \(y\),那么答案就在前一半操作,否则就在后一半操作(如果补一个操作编号为 \(0\),整体加极大值的操作)。 所以…

2025年peek什么材料定制厂家权威推荐榜单:peek原料/材料peek/peek塑料原料源头厂家精选

在机器人轻量化与新能源汽车爆发式增长的浪潮下,PEEK(聚醚醚酮)材料凭借其卓越性能正成为高端制造领域的“新宠”。据行业数据显示,特斯拉Optimus单机使用PEEK量超过2kg,预计2025年全球出货量达50万台时,将激发出…

一对一视频聊天源码,高效查找方法之二分查找 - 云豹科技

一对一视频聊天源码,高效查找方法之二分查找介绍二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。过程首先,假…

2025年高解析喷码机生产厂家权威推荐榜单:打标机/打码机/工业喷码机源头厂家精选

在“一物一码”成为食品、医药、线缆、日化等行业出厂标配的2025年,高解析喷码机已成为产品追溯、品牌防伪及生产管理不可或缺的一环。 高解析喷码技术正随着工业4.0深化与"中国智造"转型而持续进步。据行业…