【HarmonyOS应用开发】ArkUI 开发框架-进阶篇-管理组件状态(九)
管理组件状态

一、概述
在应用中,界面通常都是动态的。下图所示,在子目标列表中,当用户点击目标一,目标一会呈现展开状态,再次点击目标一,目标一呈现收起状态。界面会根据不同的状态展示不一样的效果。

ArkUI作为一种声明式UI,具有状态驱动UI更新的特点。当用户进行界面交互或有外部事件引起状态改变时,状态的变化会触发组件自动更新。所以在ArkUI中,我们只需要通过一个变量来记录状态。当改变状态的时候,ArkUI就会自动更新界面中受影响的部分。

ArkUI框架提供了多种管理状态的装饰器来修饰变量,使用这些装饰器修饰的变量即称为状态变量。
在组件范围传递的状态管理常见的场景如下:
| 场景 | 装饰器 |
|---|---|
| 组件内的状态管理 | @State |
| 从父组件单向同步状态 | @Prop |
| 与父组件双向同步状态 | @Link |
| 跨组件层级双向同步状态 | @Provide和@Consume |
在组件内使用@State装饰器来修饰变量,可以使组件根据不同的状态来呈现不同的效果。若当前组件的状态需要通过其父组件传递而来,此时需要使用@Prop装饰器;若是父子组件状态需要相互绑定进行双向同步,则需要使用@Link装饰器。使用@Provide和@Consume装饰器可以实现跨组件层级双向同步状态。
在实际应用开发中,应用会根据需要封装数据模型。如果需要观察嵌套类对象属性变化,需要使用@Observed和@ObjectLink装饰器,因为上述表格中的装饰器只能观察到对象的第一层属性变化。@Observed和@ObjectLink装饰器的具体使用方法可参考@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化。
另外,当状态改变,需要对状态变化进行监听做一些相应的操作时,可以使用@Watch装饰器来修饰状态。
二、组件内的状态管理:@State
实际开发中由于交互,组件的内容呈现可能产生变化。当需要在组件内使用状态来控制UI的不同呈现方式时,可以使用@State装饰器。以任务管理应用为例,当点击子目标列表的其中一项,列表项会展开。当再次点击同一项,列表项会收起。所以,对于某一个列表项来说,它的呈现方式会受列表项是否展开这个状态影响。
展开/收起目标项

将是否展开这个状态定义为isExpanded变量,当其值为false表示目标项收起,值为true时表示目标项展开。
此时,需要使用@State装饰器修饰isExpanded,使其成为目标项内部的状态变量。通过@State装饰后,框架内部会建立数据与视图间的绑定,当isExpanded状态变化时,目标项会随之展开或收起。

其具体实现只要用@State修饰isExpanded变量,定义是否展开状态。然后通过条件渲染,实现是否显示进度调整面板和列表项的高度变化。最后,监听列表项的点击事件,在onClick回调中改变isExpanded状态。
这样就实现了对相同列表项点击时,列表项的展开和收起功能。当用户反复点击同一个列表项时,组件内的isExpanded状态变化,列表项会自动更新。
@Component
export default struct TargetListItem {@State isExpanded: boolean = false;...build() {...Column() {...if (this.isExpanded) {Blank()ProgressEditPanel(...)}}.height(this.isExpanded ? $r('app.float.expanded_item_height')
: $r('app.float.list_item_height')).onClick(() => {...this.isExpanded = !this.isExpanded;...})...}
}
三、从父组件单向同步状态:@Prop
当子组件中的状态依赖从父组件传递而来时,需要使用@Prop装饰器,@Prop修饰的变量可以和其父组件中的状态建立单向同步关系。当父组件中状态变化时,该状态值也会更新至@Prop修饰的变量;对@Prop修饰的变量的修改不会影响其父组件中的状态。

在目标管理应用中,当用户点击子目标列表的“编辑”文本,列表进入编辑模式,点击取消,列表退出编辑模式。
整个列表是自定义组件TargetList,顶部是文本显示区域,主要是Text组件,底部是一个Button组件。
中间区域则是用来显示每个目标项,目标项是自定义组件TargetListItem。
从图中可以看出,TargetListItem是TargetList的子组件。TargetList是TargetListItem父组件。

对于父组件TargetList,其顶部显示的文本和底部按钮会随编辑模式的变化而变化,因此父组件拥有编辑模式状态。
对于子组件TargetListItem,其最右侧是否预留位置和显示勾选框也会随编辑模式变化,因此子组件也拥有编辑模式状态。
但是是否进入编辑模式,其触发点是在用户点击列表的“编辑”或取消按钮,状态变化的源头仅在于父组件TargetList。当父组件TargetList中的编辑模式变化时,子组件TargetListItem的编辑模式状态需要随之变化。

在父组件TargetList中可以定义一个是否进入编辑模式的状态,即用@State修饰isEditMode。@State修饰的变量不仅是组件内部的状态,也可以作为子组件单向或双向同步的数据源。ArkUI提供了@Prop装饰器,@Prop修饰的变量可以和其父组件中的状态建立单向同步关系,所以用@Prop修饰子组件TargetListItem中的isEditMode变量。
在父组件TargetList中,用@State修饰isEditMode,定义编辑模式状态。然后利用条件渲染实现根据是否进入编辑模式,显示不同的文本和按钮。同时,在父组件中需要在用户点击时改变状态,触发界面更新。
当点击“编辑”事件发生时,进入编辑模式,显示取消、全选文本和勾选框,同时显示删除按钮;当点击“取消”事件发生时,退出编辑模式,显示“编辑”文本和“添加子目标”按钮。
@Component
export default struct TargetList {@State isEditMode: boolean = false;...build() {Column() {Row() {...if (this.isEditMode) {Text($r('app.string.cancel_button')).onClick(() => {this.isEditMode = false;...})...Text($r('app.string.select_all_button'))...Checkbox()...} else {Text($r('app.string.edit_button')).onClick(() => {this.isEditMode = true;})...}...}...List({ space: CommonConstants.LIST_SPACE }) {ForEach(this.targetData, (item: TaskItemBean, index: number) => {ListItem() {TargetListItem({isEditMode: this.isEditMode,...})}}, (item, index) => JSON.stringify(item) + index)}...if (this.isEditMode) {Button($r('app.string.delete_button'))} else {Button($r('app.string.add_task'))}}...}
}
在子组件TargetListItem中,使用@Prop修饰子组件的isEditMode变量,定义子组件的编辑模式状态。然后同样根据是否进入编辑模式,控制目标项最右侧是否预留位置和显示勾选框。
@Component
export default struct TargetListItem {@Prop isEditMode: boolean;...Column() {...}.padding({...right: this.isEditMode ? $r('app.float.list_edit_padding')
: $r('app.float.list_padding')})...if (this.isEditMode) {Row() {Checkbox()...}}...
}
最后,最关键的一步就是要在父组件中使用子组件时,将父组件的编辑模式状态this.isEditMode传递给子组件的编辑模式状态isEditMode。
@Component
export default struct TargetList {@State isEditMode: boolean = false;...build() {Column() {...List({ space: CommonConstants.LIST_SPACE }) {ForEach(this.targetData, (item: TaskItemBean, index: number) => {ListItem() {TargetListItem({isEditMode: this.isEditMode,...})}}, (item, index) => JSON.stringify(item) + index)}...}...}
}
四、与父组件双向同步状态:@Link
若是父子组件状态需要相互绑定进行双向同步时,可以使用@Link装饰器。父组件中用于初始化子组件@Link变量的必须是在父组件中定义的状态变量。

在目标管理应用中,当用户点击同一个目标,目标项会展开或者收起。当用户点击不同的目标项时,除了被点击的目标项展开,同时前一次被点击的目标项会收起。
如上图所示,当目标一展开时,点击目标三,目标三会展开,同时目标一会收起。再点击目标一时,目标一展开,同时目标三会收起。
从目标一切换到目标三的流程中,关键在于最后目标一的收起,当点击目标三时,目标一需要知道点击了目标三,目标一才会收起。

在子目标列表中,每个列表项都有其位置索引值index属性,表示目标项在列表中的位置。index从0开始,即第一个目标项的索引值为0,第二个目标项的索引值为1,以此类推。此外,clickIndex用来记录被点击的目标项索引。当点击目标一时,clickIndex为0,点击目标三时,clickIndex为2。
在父组件子目标列表和每个子组件目标项中都拥有clickIndex状态。当目标一展开时,clickIndex为0。此时点击目标三,目标三的clickIndex变为2,只要其父组件子目标列表感知到clickIndex状态变化,同时将此变化传递给目标一。目标一的clickIndex即可同步改变为2,即目标一感知到此时点击了目标三。

将列表和目标项对应到列表组件TargetList和列表项TargetListItem。首先,需要在父组件TargetList中定义clickIndex状态。
若此时子组件中的clickIndex用@Prop装饰器修饰,当子组件中clickIndex变化时,父组件无法感知,因为@Prop装饰器建立的是从父组件到子组件的单向同步关系。
ArkUI提供了@Link装饰器,用于与父组件双向同步状态。当子组件TargetListItem中的clickIndex用@Link修饰,可与父组件TargetList中的clickIndex建立双向同步关系。
@Component
export default struct TargetList {@State clickIndex: number = CommonConstants.DEFAULT_CLICK_INDEX;...TargetListItem({clickIndex: $clickIndex,...})...
}
首先,在父组件TargetList中用@State装饰器定义点击的目标项索引状态。然后,在子组件TargetListItem中用@Link装饰器定义clickIndex,当点击目标项时,clickIndex更新为当前目标索引值。
完成在父子组件中定义状态后,最关键的就是要建立父子组件的双向关联关系。在父组件中使用子组件时,将父组件的clickIndex传递给子组件的clickIndex。其中父组件的clickIndex加上$表示传递的是引用。
@Component
export default struct TargetListItem {@Link @Watch('onClickIndexChanged') clickIndex: number;@State isExpanded: boolean = false...onClickIndexChanged() {if (this.clickIndex != this.index) {this.isExpanded = false;}}build() {...Column() {...}.onClick(() => {...this.clickIndex = this.index;...})...}
}
当目标一感知到点击了目标三时,还需要将目标一收起,切换列表项的功能才是完整的。此时,目标一感知到clickIndex变为2,需要判断与目标一本身的位置索引值0不相等,从而将目标一收起。此时,就需要用到ArkUI中监听状态变化@Watch的能力。用@Watch修饰的状态,当状态发生变化时,会触发声明时定义的回调。
我们给TargetListItem的中的clickIndex状态加上@Watch("onClickIndexChanged")。这表示需要监听clickIndex状态的变化。当clickIndex状态变化时,将触发onClickIndexChanged回调:如果点击的列表项索引不等于当前列表项索引,则将isExpanded状态置为false,从而收起该目标项。
五、跨组件层级双向同步状态:@Provide和@Consume

跨组件层级双向同步状态是指@Provide修饰的状态变量自动对提供者组件的所有后代组件可用,后代组件通过使用@Consume装饰的变量来获得对提供的状态变量的访问。@Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。@Consume在感知到@Provide数据的更新后,会触发当前自定义组件的重新渲染。
示例源码下载
https://download.csdn.net/download/Mr_Roki/88797964
由于内容过长,在同一篇博文中,会显得难于阅读。
-
第一大点( Video组件的使用)
-
第二大点(给您的应用添加弹窗)
在下一篇系列里继续接上,请有兴趣的童鞋持续关注更新。
相关文章:
【HarmonyOS应用开发】ArkUI 开发框架-进阶篇-管理组件状态(九)
管理组件状态 一、概述 在应用中,界面通常都是动态的。下图所示,在子目标列表中,当用户点击目标一,目标一会呈现展开状态,再次点击目标一,目标一呈现收起状态。界面会根据不同的状态展示不一样的效果。 Ar…...
EF Core入门例子(以SqLite为数据库)
测试环境: visual studio 2017 .net core 2.1 具体步骤如下: 1 新增名称为EFCoreDemo的.net core控制台程序,版本选择.net core 2.1,项目不能放到带中文的目录下,不然到后面执行Add-Migration命令时会报如下的错误…...
centos7 安装nginx
在 CentOS 7 上安装 Nginx,你可以选择从官方仓库(EPEL)安装,或者手动编译安装。以下是通过 EPEL 安装 Nginx 的步骤: 方法一:通过 EPEL 仓库安装 添加 EPEL 仓库: sudo yum install epel-relea…...
【Linux Day14 UDP网络通讯】
UDP网络通讯 UDP报文结构: 16位源端口:用于记录发送端的端口号(占用两个字节)16位目的端口:用于记录接收端的端口号(占用两个字节)16位UDP长度:确定UDP报文总长度,&…...
指针的深入了解6
1.回调函数 回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数 时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用࿰…...
PHP之PDO_MYSQL扩展安装步骤
1,如果有php源码包可以不用下载,在 源码包下的 ext/ 下面可以找到 cd php-8.1.9/ext/pdo_mysql 2,如果存在,直接安装 /usr/local/php-8.1.9/bin/phpize ./configure --with-php-config/usr/local/php-8.1.9/bin/php-config make &&a…...
【pytorch】nn.linear 中为什么是y=xA^T+b
我记得读教材的时候是yWxb, 左乘矩阵W,这样才能表示线性变化。 但是pytorch中的nn.linear中,计算方式是yxA^Tb,其中A是权重矩阵。 为什么右乘也能表示线性变化操作呢?因为pytorch中,照顾到输入是多个样本一起算的&…...
vite打包原理
vite 工程化开发:打包工具 启动速度很快 核心原理还是webpack 把webpack封装了,把webpack对象封装了 和vue2整体结构几乎一致 webpack两种模式:开发&生产 代码打包编译,本地起一个web服务器实时预览编译后的结果 build 命令模…...
导出pdf 加密、加水印、加页脚
1.依赖 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.10</version> </dependency> <dependency> …...
Flutter 仿抖音 TikTok 上下滑动 播放视频
Flutter 仿抖音 TikTok 上下滑动 播放视频UI框架,视频播放使用 video_player github:GitHub - PangHaHa12138/TiktokVideo: Flutter 仿抖音 TikTok 上下滑动 播放视频UI框架 实现功能: 1.上下滑动自动播放切换视频,loading 封面…...
计算机网络——网络层(2)
计算机网络——网络层(2) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家, [跳转到网站](https://www.captainbed.…...
01-16Maven-SpringBoot入门
Maven继承Maven高级SpringSpringBoot入门 Maven 一、概念及功能 概念:Maven是Apache软件基金会组织维护的一款专门为Java项目提供项目构建和依赖管理的工具 1.1作用: 项目构建 构建:是一个将代码从开发阶段到生产阶段的一个过程…...
微信小程序(二十七)列表渲染改变量名
注释很详细,直接上代码 上一篇 新增内容: 1.改变默认循环单元item变量名 2.改变默认循环下标index变量名 基础模板有问题可以先看上一篇 源码: index.wxml <view class"students"><view class"item"><te…...
k8s之安装部署及kuboard发布应用
目录 环境准备 系统规划 配置免密 将桥接的IPv4流量传递到iptables的链 系统基础配置 安装docker 安装docker及基础依赖 配置docker的仓库下载地址 部署k8s 添加阿里云的k8s源 安装kubeadm,kubelet和kubectl 初始化masteer节点 部署node节点 部署flanne…...
JProfiler for Mac:提升性能和诊断问题的终极工具
在当今的高性能计算和多线程应用中,性能优化和问题诊断是至关重要的。JProfiler for Mac 是一个强大的性能分析工具,旨在帮助开发者更好地理解其应用程序的运行情况,提升性能并快速诊断问题。 JProfiler for Mac 的主要特点包括:…...
力扣202-快乐数
快乐数 题目链接 解题思路: 两个指针,一快一慢,如果相遇,就会生成环如果环内元素为1,那么就可以返回 class Solution { public:int get(int n){int res 0;while(n){res (n%10) * (n%10);n / 10;}return res;}bool isHappy(int …...
牛客寒假训练营H题
思路:找出所有m的子集,加到价值中,找出最大价值即可。 代码: void solve(){int n, m;cin >> n >> m;vector<pii>a(n 1);for(int i 1;i < n;i )cin >> a[i].first >> a[i].second;int ans 0…...
ubuntu22.04@laptop 常用基础环境安装
ubuntu22.04laptop 常用基础环境安装 1. 源由2. 步骤2.1 安装ubuntu22.04 LTS系统2.2 必备软件安装2.3 基本远程环境2.3.1 远程ssh登录2.3.2 samba局域网2.3.3 VNC远程登录 2.4 开发环境安装 3. 总结 1. 源由 应朋友要求,整理下一个个人常用的工作笔记本常用开发环…...
Linux第41步_移植ST公司uboot的第2步_修改网络驱动_USB OTG设备树_LCD驱动_以及编译和烧写测试
移植ST公司uboot的第1步,创建配置文件、设备树、修改电源管理和sdmmc节点后,还需要进一部修改,如:网络驱动、USB OTG设备树、LCD驱动,以及编译和烧写测试。 一、在虚拟机中,使用VSCode打开my_uboot工作区 …...
瑞芯微1808模型转换(onnx到rknn)环境配置过程
瑞芯微1808模型转换(onnx → \to →rknn)环境配置 阅读本解决方案前,请读者确保已经根据官方的相关教程【rknn_model_zoo/common/rknn_converter at v1.5.0 airockchip/rknn_model_zoo (github.com)】完成其他配置文件的修改,以…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
前端中slice和splic的区别
1. slice slice 用于从数组中提取一部分元素,返回一个新的数组。 特点: 不修改原数组:slice 不会改变原数组,而是返回一个新的数组。提取数组的部分:slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
