鸿蒙开发教程-管理组件状态

概述

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

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

ArkUI框架提供了多种管理状态的装饰器来修饰变量,使用这些装饰器修饰的变量即称为状态变量。

在组件范围传递的状态管理常见的场景如下:

场景装饰器
组件内的状态管理@State
从父组件单向同步状态@Prop
与父组件双向同步状态@Link
跨组件层级双向同步状态@Provide和@Consume

在组件内使用@State装饰器来修饰变量,可以使组件根据不同的状态来呈现不同的效果。若当前组件的状态需要通过其父组件传递而来,此时需要使用@Prop装饰器;若是父子组件状态需要相互绑定进行双向同步,则需要使用@Link装饰器。使用@Provide和@Consume装饰器可以实现跨组件层级双向同步状态。

在实际应用开发中,应用会根据需要封装数据模型。如果需要观察嵌套类对象属性变化,需要使用@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修饰的变量的修改不会影响其父组件中的状态。

图4 列表的编辑模式

如图4所示,在目标管理应用中,当用户点击子目标列表的“编辑”文本,列表进入编辑模式,点击取消,列表退出编辑模式。

整个列表是自定义组件TargetList,顶部是文本显示区域,主要是Text组件,底部是一个Button组件。中间区域则是用来显示每个目标项,目标项是自定义组件TargetListItem。

从图中可以看出,TargetListItem是TargetList的子组件。TargetList是TargetListItem父组件。

图5 TargetList和TargetListItem

对于父组件TargetList,其顶部显示的文本和底部按钮会随编辑模式的变化而变化,因此父组件拥有编辑模式状态。

对于子组件TargetListItem,其最右侧是否预留位置和显示勾选框也会随编辑模式变化,因此子组件也拥有编辑模式状态。

但是是否进入编辑模式,其触发点是在用户点击列表的“编辑”或取消按钮,状态变化的源头仅在于父组件TargetList。当父组件TargetList中的编辑模式变化时,子组件TargetListItem的编辑模式状态需要随之变化。

图6 从父组件单向同步isEditMode状态

在父组件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变量的必须是在父组件中定义的状态变量。

图7 切换目标项

在目标管理应用中,当用户点击同一个目标,目标项会展开或者收起。当用户点击不同的目标项时,除了被点击的目标项展开,同时前一次被点击的目标项会收起。

如图7所示,当目标一展开时,点击目标三,目标三会展开,同时目标一会收起。再点击目标一时,目标一展开,同时目标三会收起。

从目标一切换到目标三的流程中,关键在于最后目标一的收起,当点击目标三时,目标一需要知道点击了目标三,目标一才会收起。

图8 子目标列表目标项位置索引

在子目标列表中,每个列表项都有其位置索引值index属性,表示目标项在列表中的位置。index从0开始,即第一个目标项的索引值为0,第二个目标项的索引值为1,以此类推。此外,clickIndex用来记录被点击的目标项索引。当点击目标一时,clickIndex为0,点击目标三时,clickIndex为2。

在父组件子目标列表和每个子组件目标项中都拥有clickIndex状态。当目标一展开时,clickIndex为0。此时点击目标三,目标三的clickIndex变为2,只要其父组件子目标列表感知到clickIndex状态变化,同时将此变化传递给目标一。目标一的clickIndex即可同步改变为2,即目标一感知到此时点击了目标三。

图9 与父组件双向同步clickIndex状态

将列表和目标项对应到列表组件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数据的更新后,会触发当前自定义组件的重新渲染。

使用@Provide的好处是开发者不需要多次将变量在组件间传递。

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

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

相关文章

【Unity】AB包下载

【Unity】AB包下载 1.使用插件打AB包 a.AB包分类 一般地,将预制体作为AB包资源,不仅需要对预制体本身进行归类,还要对其涉及的动画(AnimationClip)、动画状态机(AnimatorController)、以及所…

数据可视化 | 期末复习 | 补档

文章目录 📚介绍可视化🐇什么是可视化🐇科学可视化,信息可视化,可视分析系统三者之间有什么区别🔥🐇可视化的基本流程🐇可视化的两个基本设计原则🐇数据属性&#x1f407…

Linux基本常用命令大全(一)

一、基本命令 1.1 关机和重启 关机 shutdown -h now 立刻关机 shutdown -h 5 5分钟后关机 poweroff 立刻关机 重启 shutdown -r now 立刻重启 shutdown -r 5 5分钟后重启 reboot 立刻重启 1.2 帮助命令 –help命令 shutdown --help: ifconfig --help&#xff1a…

蓝牙资产标签

在高科技信息行业的迅猛发展下,信息化管理在各领域的应用体现了很大的优势,从人员、资产、车辆、物料等的各类应用的定位信息是数字化管理的重要组成部分,小编详细 介绍下蓝牙资产标签在物资管理的应用。 G806采用低功耗蓝牙技术&#xff0c…

JavaScript 操作(DOM)文档对象模型

文章目录 什么是DOM浏览器内置解析器JavaSiript 如何修改结构与样式浏览器解析顺序DOM相关APIgetElementByIdgetElementsByTagNamecreateElementinnerText与innerHTMLappendChild JavaScript 修改结构与样式案例 什么是DOM DOM:DOM(文档对象模型&#x…

gradle构建spring-framework源码

5.3.22版本构建 通过启动的jvm参数配置代理下载 Could not download jruby-stdlib-9.2.20.1.jar (org.jruby:jruby-stdlib:9.2.20.1) Could not get resource https://repo.maven.apache.org/maven2/org/jruby/jruby-stdlib/9.2.20.1/jruby-stdlib-9.2.20.1.jar. Could not GE…

如何在Linux部署JumpServer堡垒机并实现远程访问本地服务

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpS…

数据结构:非完全二叉树(递归实现)

非完全二叉树是指在二叉树中,除了叶子节点(无子节点)外,其他节点的子节点个数可以不同,即不一定是每个节点都有两个子节点,有右孩子时也不一定有左孩子。 tree.h /* * 文件名称:tree.h * …

VMware workstation平台下配置Fedora-Server-39-1.5虚拟机网络

VMware workstation平台下配置Fedora-Server-39-1.5虚拟机网络 Fedora包含的软件以自由及开放源码许可来发布,并旨在成为该技术领域的领先者。Fedora在专注创新、抢先集成新技术、与上游Linux社区紧密工作方面拥有良好名声。该文档适用于在VMware workstation平台下…

项目03——《古罗马战场》

接下来我们布置场景,我们的预期结果(功能分析)是: 实现两角色阵列面向冲锋 首先进入资源商店准备下载角色模型资源, 搜索Monster, 将免费资源导入unity包, 创建一个地面Plane, 对两…

2023.1.17 关于 Redis 持久化 AOF 策略详解

目录 引言 AOF 策略 实例演示一 缓冲区 重写机制 手动触发 自动触发 AOF 重写流程 实例演示二 引言 Redis 实现持久化的两大策略 RDB ——> Redis DataBase(定期备份)AOF ——> Append Only File(实时备份) 注意&…

从零开始的OpenGL光栅化渲染器构建3-法线贴图和视差贴图

前言 我们可以用一张纹理贴图来表现物体表面的基础反射颜色,也可以用一张镜面反射贴图,来指派表面是否产生高光。除此之外,我们可以用贴图来存储表面的法线信息,以及高度信息,从而让渲染效果更加精细。 法线贴图 我…

vulhub之redis篇

CVE-2022-0543 | redis的远程代码执行漏洞 简介 CVE-2022-0543 该 Redis 沙盒逃逸漏洞影响 Debian 系的 Linux 发行版本,并非 Redis 本身漏洞, 漏洞形成原因在于系统补丁加载了一些redis源码注释了的代码 原理分析 redis一直有一个攻击面,就是在用户连接redis后,可以通过ev…

四、RHCE--远程连接服务器

四、RHCE--远程连接服务器 1、远程连接服务器简介2、连接加密技术简介(1)版本协商阶段(2)密钥和算法协商阶段(3)认证阶段 3、ssh服务配置4、用户登录ssh服务器 1、远程连接服务器简介 (1&#…

【21K⭐】Legado:一款免费无广且支持高度自定义的必备阅读软件

【21K⭐】Legado:一款免费无广且支持高度自定义的必备阅读软件 随着数字化时代的到来,我们的阅读方式已经发生了巨大的变化。传统的纸质书籍逐渐被电子书取代,人们越来越倾向于使用电子设备来阅读。 然而对于一个喜欢阅读网络小说的人来说&…

从编程中思考:大脑的局部与全局模式(一)

郭靖正在帐篷中用Unity写代码,刚写完一段代码。欧阳锋从帐篷外走进来,正要说点什么,郭靖反应敏捷,转身反手一招神龙摆尾击出,将欧阳锋震出帐篷,灰溜溜逃跑。 using UnityEngine;public class LocalGlobalD…

旧路由重置新路由设置新路由设置教程|适用于自动获取IP模式

前言 如果你的光猫是直接拨号(路由模式)的,就可以按照本教程进行路由重置或者更换新路由器。 本文章适合电脑小白,请注意每一步哦! 注意事项 开始之前需要确认光猫是桥接模式还是路由模式。如果光猫是路由模式&…

《二叉树》——1

目录 前言: 二叉树的链式结构 二叉树的遍历 前序遍历: 中序遍历: 后序遍历: 总结: 前言: 从本文开始,将进一步深入学习编程语言思想,从二叉树开始我们将接触许许多多的递归算…

GoLang基础

1 与其他语言相比,使用go有什么好处? 简洁易学:相比其他编程语言,Go语言具有清晰简洁的语法和规范,减少了代码的复杂性。Go语言拥有较少的关键字和一致的格式,使得代码易于编写、阅读和维护。新手可以很快…

MyBatis 的XML实现方法(JAVA)

数据库表的结构如下: DROP DATABASE IF EXISTS test; CREATE DATABASE test DEFAULT CHARACTER SET utf8mb4; -- 使⽤数据数据 USE test; -- 创建表[⽤⼾表] DROP TABLE IF EXISTS userinfo; CREATE TABLE userinfo ( id INT ( 11 ) NOT NULL AUTO_INCREMENT, user…