【鸿蒙 HarmonyOS 4.0】状态管理

news/发布时间2024/5/15 21:28:36

一、介绍

资料来自官网:文档中心

在声明式UI编程框架中,UI是程序状态的运行结果,用户构建了一个UI模型,其中应用的运行时的状态是参数。当参数改变时,UI作为返回结果,也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染,在ArkUI中统称为状态管理机制。

  • View(UI):UI渲染,指将build方法内的UI描述和@Builder装饰的方法内的UI描述映射到界面。
  • State:状态,指驱动UI更新的数据。用户通过触发组件的事件方法,改变状态数据。状态数据的改变,引起UI的重新渲染。

二、@State装饰器:组件内状态 

@State装饰的变量,或称为状态变量,一旦变量拥有了状态属性,就和自定义组件的渲染绑定起来。当状态改变时,UI会发生对应的渲染改变。

 

说明:

@State装饰器标记的变量必须初始化,不能为空值

@State支持Object、class、string、number、boolean、enum类型以及这些类型的数组

嵌套类型以及数组中的对象属性无法触发视图更新 

组件传值代码示例,为下面不同组件之间传值做准备:👇

// 任务类
class Task{static id: number = 1// 任务名称name: string = `任务${Task.id++}`// 任务状态:是否完成finished: boolean = false
}
// 统一的卡片样式
@Styles function card(){.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4})
}@Entry
@Component
struct PropLinkPages {// 总任务数量@State totalTask: number = 0// 已完成任务数量@State finishTask: number = 0// 任务数组@State tasks: Task[] = []//此函数是更新任务总数量和已完成任务数量的handleTaskChange(){// 1.更新任务总数量this.totalTask = this.tasks.length// 2.更新已完成任务数量this.finishTask = this.tasks.filter(item => item.finished).length}build() {Column({space:10}){//1.任务进度卡片Row(){Text('任务进度:').fontSize(30).fontWeight(FontWeight.Bold)Stack(){Progress({value: this.finishTask,total: this.totalTask,type: ProgressType.Ring}).width(100)Row(){Text(this.finishTask.toString()).fontSize(24).fontColor('#36D')Text(' / ' + this.totalTask.toString()).fontSize(24)}}}.card().margin({top: 5, bottom: 10}).justifyContent(FlexAlign.SpaceEvenly)// 2.新增任务按钮Button('新增任务').width(200).margin({bottom: 10}).onClick(() => {// 1.新增任务数据this.tasks.push(new Task())// 2.更新任务总数量this.handleTaskChange()})//3.任务列表List({space: 10}){ForEach(this.tasks,(item: Task, index) => {ListItem(){Row(){Text(item.name).fontSize(20)Checkbox().select(item.finished).onChange(val => {// 1.更新当前任务状态item.finished = val// 2.更新已完成任务数量this.handleTaskChange()})}.card().justifyContent(FlexAlign.SpaceBetween)}.swipeAction({end: this.DeleteButton(index)})})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}.width('100%').height('100%').backgroundColor('#F1F2F3')}@Builder DeleteButton(index: number){Button(){Image($r('app.media.ic_public_delete_filled')).fillColor(Color.White).width(20)}.width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5).onClick(() => {this.tasks.splice(index, 1)this.handleTaskChange()})}
}

示例代码说明:

这是一个展示任务进度的效果,分为任务进度条和任务列表两部分

对于新增的任务勾选后可在任务进度中查看已勾选和总任务数量,左滑单个任务会出现删除按钮,可进行此任务删除操作

示例代码的效果:

 

三、父子组件数据同步

3.1、@Prop装饰器:父子单向同步

@Prop装饰的变量可以和父组件建立单向的同步关系。@Prop装饰的变量是可变的,但是变化不会同步回其父组件。

需求:将示例代码中的任务进度卡片封装成TaskStatistics组件,在PropLinkPages组件中引入TaskStatistics组件,封装后再完成数据的同步渲染

上面示例中:

父组件PropLinkPages,子组件TaskStatistics

总任务与已完成任务数据是由父组件进行维护,子组件进行渲染,所以需要父组件将数据传递给子组件

✍使用@Prop,父子单向同步


@Prop只支持string、number、boolean、enum类型;父组件对象类型,子组件是对象属性;不可以是数组、any

3.2、@Link装饰器:父子双向同步 

@Link装饰的变量与其父组件中的数据源共享相同的值。

限制条件:@Link装饰器不能在@Entry装饰的自定义组件中使用

需求:将示例代码中对任务数组的操作(新增任务与任务列表)封装成TaskList组件,在PropLinkPages组件中引入TaskList组件

上面示例中:

父组件PropLinkPages,子组件TaskList

父子双方都需要使用总认为与已完成任务数据,并且子组件的数据发生变化后需要通知父组件进行变化,因为上一步@Prop时父组件需要将数据传递给另一个子组件TaskStatistics,所以涉及到父子双向数据绑定渲染

✍使用@Link,父子双向同步


父子类型一致:string、number、boolean、enum、object、class,以及他们的数组;

数组中元素增、删、替换会引起刷新

嵌套类型以及数组中的对象属性无法触发视图更新

 四、后代组件双向同步

4.1、@Provide装饰器和@Consume装饰器:与后代组件双向同步

@Provide和@Consume,应用于与后代组件的双向数据同步,应用于状态数据在多个层级之间传递的场景。

需求:示例代码中分别使用@Prop与@Link进行数据传递,需要更改为@Provide和@Consume跨组件数据传递

上面示例中:

父组件PropLinkPages,子组件TaskList,子组件TaskStatistics

在父组件中使用@Provide将所需数据传给两个子组件,两个子组件通过使用@Consume去获取@Provide提供的变量,建立在@Provide和@Consume之间的双向数据同步

✍@Provide和@Consume可以通过相同的变量名或者相同的变量别名绑定,变量类型必须相同。

下面代码变量名不一致,但具备相同的别名,使用@Provide和@Consume实现跨组件数据同步👇

五、嵌套类对象属性变化

5.1、@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化

对于多层嵌套的情况,比如二维数组,或者数组项class,或者class的属性是class,他们的第二层的属性变化是无法观察到的。这就引出了@Observed/@ObjectLink装饰器。

限制条件:

a:使用@Observed装饰class会改变class原始的原型链,@Observed和其他类装饰器装饰同一个class可能会带来问题。

b:@ObjectLink装饰器不能在@Entry装饰的自定义组件中使用。

需求: 改造任务进度的代码,当任务完成后,此任务置灰,并有中划线

实现步骤:

①任务数组对应的元素Task是对象类型,给Task对象添加@Observed装饰器

②给嵌套的对象上所对应的变量上添加@ObjectLink装饰器,但源代码中是方法参数,所以将此段代码封装为TaskItem组件,在TaskItem组件中对变量item添加@ObjectLink

问题:子组件需要调父组件的方法,把父组件的方法作为参数传递过来,传递过程中存在this的丢失

解决:子组件中定义onTaskChange方法,传递给父组件时对函数使用bind方法将this传递进去

如下:TaskItem({item:item,onTaskChange:this.handleTaskChange.bind(this)})

// 任务类
@Observed
class Task{static id: number = 1// 任务名称name: string = `任务${Task.id++}`// 任务状态:是否完成finished: boolean = false
}
// 统一的卡片样式
@Styles function card(){.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4})
}
// 任务完成样式
@Extend(Text) function finishedTask(){.decoration({type:TextDecorationType.LineThrough}).fontColor('#B1B2B1')
}@Entry
@Component
struct PropLinkPages {// 总任务数量@Provide totalTask: number = 0// 已完成任务数量@Provide finishTask: number = 0build() {Column({space:10}){//1.任务进度卡片TaskStatistics()//2.任务列表TaskList()}.width('100%').height('100%').backgroundColor('#F1F2F3')}
}@Component
struct TaskList {// 任务数组@State tasks: Task[] = []@Consume totalTask: number@Consume finishTask: number//此函数是更新任务总数量和已完成任务数量的handleTaskChange(){// 1.更新任务总数量this.totalTask = this.tasks.length// 2.更新已完成任务数量this.finishTask = this.tasks.filter(item => item.finished).length}build() {Column(){// 2.新增任务按钮Button('新增任务').width(200).margin({bottom: 10}).onClick(() => {// 1.新增任务数据this.tasks.push(new Task())// 2.更新任务总数量this.handleTaskChange()})//3.任务列表List({space: 10}){ForEach(this.tasks,(item: Task, index) => {ListItem(){TaskItem({item:item,onTaskChange:this.handleTaskChange.bind(this)})}.swipeAction({end: this.DeleteButton(index)})})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}}@Builder DeleteButton(index: number){Button(){Image($r('app.media.ic_public_delete_filled')).fillColor(Color.White).width(20)}.width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5).onClick(() => {this.tasks.splice(index, 1)this.handleTaskChange()})}
}@Component
struct TaskStatistics {@Consume totalTask: number@Consume finishTask: numberbuild() {Row(){Text('任务进度:').fontSize(30).fontWeight(FontWeight.Bold)Stack(){Progress({value: this.finishTask,total: this.totalTask,type: ProgressType.Ring}).width(100)Row(){Text(this.finishTask.toString()).fontSize(24).fontColor('#36D')Text(' / ' + this.totalTask.toString()).fontSize(24)}}}.card().margin({top: 5, bottom: 10}).justifyContent(FlexAlign.SpaceEvenly)}
}@Component
struct TaskItem {@ObjectLink item: TaskonTaskChange: () => voidbuild() {Row(){if(this.item.finished){Text(this.item.name).finishedTask()}else{Text(this.item.name)}Checkbox().select(this.item.finished).onChange(val => {// 1.更新当前任务状态this.item.finished = val// 2.更新已完成任务数量this.onTaskChange()})}.card().justifyContent(FlexAlign.SpaceBetween)}
}

实现效果:

最后:👏👏😊😊😊👍👍

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

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

相关文章

氢气传感器与氢冷发电机:氢能应用中的关键技术及其安全监测

​ ​随着全球对可再生能源的迫切需求,氢能作为一种清洁、高效的能源形式,正逐渐受到人们的青睐。在氢能利用的过程中,氢气传感器和氢冷发电机成为了不可或缺的关键技术。然而,氢气作为一种易燃易爆的气体,其安全使…

P8630 [蓝桥杯 2015 国 B] 密文搜索

P8630 [蓝桥杯 2015 国 B] 密文搜索 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P8630 题目分析 基本上是hash的板子,但实际上对于密码串,只要判断主串中任意连续的八个位置是否存在密码串即可;那么我们…

svn客户端下载、安装、使用

下载、使用 打开360软件管家,选怎宝库,搜索svn,点击安装 可以修改安装路径 使用 在桌面右键弹出菜单,点击 输入地址,点击ok 输入用户名、密码 ,等待检出完成

机器学习基础(四)非监督学习的进阶探索

导语:上一节我们详细探索监督学习的进阶应用,详情可见: 机器学习基础(三)监督学习的进阶探索-CSDN博客文章浏览阅读296次,点赞13次,收藏11次。监督学习作为机器学习的一个主要分支,…

基础中的基础!吴恩达deeplearning.ai:如何搭建一个神经网络

在前面几篇博客的学习之后,你应该了解了如何写出Tensorflow有关的代码,如何在Tensorflow中搭建一个层以及如何在Tensorflow之中实现前向推理(链接),也了解了Tensorflow有关的数据形式(链接) 今天…

深度学习介绍

02-深度学习介绍 1 AI地图2 深度学习任务2.1 图片分类2.2 物体检测和分割2.3 样式迁移2.4 人脸合成2.5 文字生成图片2.6 文字生成2.7 无人驾驶 3 案例研究4 question 1 AI地图 自然语言处理是感知的范围,人几秒内科研感知。 2 深度学习任务 2.1 图片分类 https:/…

Linux离线安装插件

当公司Linux环境无外网情况下,需要先下载好离线安装包,然后上传到服务器,进行安装。 这里介绍一个下载插件安装包的网站,可以搜索到lrzsz、lsof、telnet、unzip、zip等安装包 搜索到想要的插件安装包后,下载并上传到服…

基于Java jsp+mysql+Spring的汽车出租平台租赁网站平台设计和实现

基于Java jspmysqlSpring的汽车出租平台租赁网站平台设计和实现 博主介绍:5年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留…

【若依(ruoyi)】Java---如何在Apifox上传params参数--延伸--如何在Apifox上传Map类型参数

在使用若依开发过程中写接口的时候想在params中添加参数,但是使用params.key这种形式在后端是接收不到传过来的参数的,于是百般调研(百度),终于找到一个解决办法,就是在参数前后加上%5B和%5D,这两个参数会被编译为"["和"]",于是就对得上了,后端成功接受到参…

Covalent Network(CQT)发展新里程碑:SOC 2 数据安全认证通过,进一步加强了其人工智能支持

Covalent Network(CQT)现已完成并通过了严格的 Service Organization Control(SOC) 2 Type II 的合规性审计,通过由备受行业认可的机构执行,进一步证明了 Covalent Network(CQT)团队坚定不移地致…

FlinkCDC详解

1、FlinkCDC是什么 1.1 CDC是什么 CDC是Chanage Data Capture(数据变更捕获)的简称。其核心原理就是监测并捕获数据库的变动(例如增删改),将这些变更按照发生顺序捕获,将捕获到的数据,写入数据…

6.2 数据库

本节介绍Android的数据库存储方式--SQLite的使用方法,包括:SQLite用到了哪些SQL语法,如何使用数据库管理操纵SQLitem,如何使用数据库帮助器简化数据库操作,以及如何利用SQLite改进登录页面的记住密码功能。 6.2.1 SQ…

【C++】类与对象—— 初始化列表 、static 静态成员、

类与对象 1 再谈构造函数1.1 构造函数体赋值1.2 初始化列表语法:建议:初始化顺序:注意: 1.3 explicit关键字 2 static 静态成员2.1 概念2.2 声明成员变量2.3 使用类的静态成员2.4 定义静态成员总结 Thanks♪(・ω&#…

js设计模式:享元模式

作用: 当需要处理很多非常类似操作的时候,可以创建可以共享的对象,并暴露接口供其他对象调用 这个对象内包含这些操作的处理逻辑,可以优化性能 示例: const Ipad function(){const apps {}const useApp function(appName,appFun){if(apps[appName]){return apps[appName]…

Java script 检测手机端,检测UA, js unicode转码 附赠网站

目录 检测是不是手机短检测是不是从百度来的多个搜索引擎java script unicode 转码可以用来写一个标签 检测是不是手机短 如果是手机端则跳转百度 如果不是手机端则不跳转 if (/(Android|IOS|iPhone|iPad|iPod|WindowsPhone|webOS|BlackBerry)/i.test(navigator.userAgent)) …

红队评估四靶场

文章目录 环境搭建1.设置所需网卡2.更改win7设置3.DC设置4.web设置开启docker服务5.kali网段`渗透启动`1.确认对方靶机的IP地址2.端口探测3.web探测`2001端口``2002端口`Tomcat/8.5.19漏洞复现`2003端口`4.docker逃逸5.ssh密钥爆破`域渗透启动`1.提权2.隧道搭建各项配置文件内容…

【Python】OpenCV-图片添加水印处理

图片添加水印处理 1. 引言 图像处理中的水印添加是一种常见的操作,用于在图片上叠加一些信息或标识。本文将介绍如何使用OpenCV库在图片上添加水印,并通过详细的代码注释来解释每一步的操作。 2. 代码示例 以下是一个使用OpenCV库的简单代码示例&…

《TCP/IP详解 卷一》第5章 Internet协议

5.1 引言 TCP UDP ICMP IGMP协议都需要IP封装。 IPv4头部格式: 版本:IP协议版本,值为4。 IHL:头长度,最大值为15,即头部最长4*15字节,一般无IP选项时值为5,即IP头为20字节。 DSEC…

Java智慧工地云综合管理平台SaaS源码 助力工地实现精细化管理

目录 智慧工地系统介绍 1、可视化大屏 2、视频监控 3、Wi-Fi安全教育 4、环境监测 5、高支模监测 6、深基坑监测 7、智能水电监测 8、塔机升降安全监测 智慧工地系统功能模块 1、基础数据管理 2、考勤管理 3、安全隐患管理 4、视频监控 5、塔吊监控 6、升降机监…

c语言经典测试题1

1.题1 int x5,y7; void swap() { int z; zx; xy; yz; } int main() { int x3,y8; swap(); printf("%d,%d\n",x, y); return 0; } A: 5,7 B: 7,5 C: 3,8 D: 8,3 大家思考一下选哪一个呢? 我们来分析一下:上述代码中我们创建了4…
推荐文章