【鸿蒙 HarmonyOS 4.0】状态管理

一、介绍

资料来自官网:文档中心

在声明式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 = 0
  build() {
    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: number
  build() {
    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: Task
  onTaskChange: () => void
  build() {
    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.mfbz.cn/a/403868.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux java查看内存消耗 linux查看java程序内存(转载)

Linux java查看内存消耗 linux查看java程序内存 目录 一、jps命令。 二、ps命令。 三、top命令。 四、free命令。 五、df命令。 查看应用的CPU、内存使用情况,使用jps、ps、top、free、df命令查看。 一、jps命令。 可以列出本机所有java应用程序的进程pid。…

浏览器显示「SSL 证书无效」应该如何解决?

作为保护网站传输数据安全的重要工具,SSL证书经常被部署于网站服务器上以实现HTTPS加密。但部分网站部署SSL证书后,访问时有时候会出现SSL 证书无效警示。那么SSL证书无效怎么办?导致SSL证书无效的情况可能是SSL证书本身的原因,也…

轻松掌握opencv的8种图像变换

文章目录 opencv的8种图像变换1. 图像放大、缩小2. 图像平移3. 图像旋转4. 图像仿射变换5. 图像裁剪6. 图像的位运算(AND, OR, XOR)7. 图像的分离和融合8. 图像的颜色空间 opencv的8种图像变换 1. 图像放大、缩小 我们先看下原图 import cv2 import ma…

【论文精读】IBOT

摘要 掩码语言建模(MLM)是一种流行的语言模型预训练范式,在nlp领域取得了巨大的成功。然而,它对视觉Transformer (ViT)的潜力尚未得到充分开发。为在视觉领域延续MLM的成功,故而探索掩码图像建模(MIM),以训练更好的视觉transforme…

mysql 自定义函数create function

方便后续查询,做以下记录; 自定义函数是一种与存储过程十分相似的过程式数据库对象, 它与存储过程一样,都是由 SQL 语句和过程式语句组成的代码片段,并且可以被应用程序和其他 SQL 语句调用。 自定义函数与存储过程之间…

Day17_集合与数据结构(链表,栈和队列,Map,Collections工具类,二叉树,哈希表)

文章目录 Day17 集合与数据结构学习目标1 数据结构2 动态数组2.1 动态数组的特点2.2 自定义动态数组2.3 ArrayList与Vector的区别?2.4 ArrayList部分源码分析1、JDK1.6构造器2、JDK1.7构造器3、JDK1.8构造器4、添加与扩容5、删除元素6、get/set元素7、查询元素8、迭…

无法打开源文件 “csignal“ (dependency of “rclcpp/rclcpp.hpp“).等错误解决方法

#include "rclcpp/rclcpp.hpp"无法打开源文件的问题 报错情况解决流程1、ctrlshiftp2、修改编辑配置3、结果 在进行ros2编程的过程中,出现上述错误,网上没有找到解决方法,为后来者记录下解决经验,少走弯路,节…

10.CSS3的calc函数

CSS3 的 calc 函数 经典真题 CSS 的计算属性知道吗? CSS3 中的 calc 函数 calc 是英文单词 calculate(计算)的缩写,是 CSS3 的一个新增的功能。 MDN 的解释为可以用在任何长度、数值、时间、角度、频率等处,语法如…

基于springboot+vue的植物健康系统(前后端分离)

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…

信号滤波在PID闭环控制中的作用(对比测试实验)

信号滤波在工业中的应用不用多说,这篇博客我们通过PID仿真测试实验,对比分析信号滤波在PID闭环控制中的作用。我们实验里需要用到的PLC算法模块大家可以查看下面文章链接: 1、博途PLC 信号发生器模块 https://rxxw-control.blog.csdn.net/a…

制造业客户数据安全解决方案(数据防泄密需求分析)

机械行业是历史悠久的工业形式,与国民经济密切相关,属于周期性行业,是我国最重要的工业制造行业之一。即使网络经济与IT信息技术在世界范围内占据主导地位,依然离不开一个发达的、先进的物质基础,而机械行业正是为生成…

CSS实现半边边框(只有边框的部分可见)

CSS实现半边边框&#xff08;只有边框的部分可见&#xff09; <div class"part box"><h1>内容</h1><!-- 绘出下面两个对角边框--><div class"part-footer"></div> </div>主要代码 .box {width: 100px;height:…

leetcode hot100打家劫舍三

本题是打家劫舍的变形&#xff0c;数据结构是树形。涉及到树的题目一定要想清楚树的遍历顺序&#xff08;前中后序&#xff09;。之后再考虑利用动态规划来解决。 动态规划是一直记录状态&#xff0c;我们可以根据动态规划的数组来记录变化的状态&#xff0c;最终求的自己想要…

Surely Vue Table表格css、js方法去除水印

文章目录 Surely Vue Table表格css、js方法去除水印用法 css 去除js去除 Surely Vue Table表格css、js方法去除水印 "surely-vue/table": "^4.2.7","ant-design-vue": "^2.1.2",用法 在main.ts文件中全局引入 import STable from su…

STM32-点亮 LED

目录 1 、电路构成及原理图 2 、编写实现代码 3、代码讲解 4、烧录到开发板调试、验证代码 5、检验效果 本人使用的是朗峰 STM32F103 系列开发板&#xff0c;此笔记基于这款开发板记录。 1 、电路构成及原理图 首先&#xff0c;通过朗峰 F1 开发板 LED 部分原理图看到…

VSCode-更改系统默认路径

修改vscode中的默认扩展路径&#xff1a;"%USERPROFILE%\.vscode" 打开目录C:\用户\电脑用户名&#xff0c;将.vscode文件剪切至D:\VSCode文件夹下 用管理员身份打开cmd.exe命令界面输入mklink /D "%USERPROFILE%\.vscode" "D:\VSCode\.vscode\"…

[corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape

前言 题目来源&#xff1a;竞赛官网 – 建议这里下载&#xff0c;文件系统/带符号的 vmlinux 给了 参考 [corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape Exploiting poll_list Objects In The Linux Kernel – 原作者文章&#xff0c;poll_list 利用方式…

如何在rust中输出日志:在rust中打印日志的各种方式对比

有许多库可以在 Rust 中输出日志&#xff0c;有时很难选择该使用哪一个。当 println! 、 dbg! 和 eprintln! 无法解决问题时&#xff0c;找到一种方便记录日志的方法就很重要&#xff0c;尤其是在生产级应用程序中。本文将帮助您深入了解在 Rust 日志记录方面最适合您的用例的日…

什么是Elasticsearch SQL

什么是Elasticsearch SQL 一. 介绍二. SQL 入门 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一. 介绍 Elasticsearch SQL 是一个 X-Pack 组件&#xff0c;允许针对 Elasticsea…

OpenAI文生视频大模型Sora概述

Sora&#xff0c;美国人工智能研究公司OpenAI发布的人工智能文生视频大模型&#xff08;但OpenAI并未单纯将其视为视频模型&#xff0c;而是作为“世界模拟器” &#xff09;&#xff0c;于2024年2月15日&#xff08;美国当地时间&#xff09;正式对外发布。 Sora可以根据用户…