HarmonyOS/OpenHarmony应用开发-ArkTS语言渲染控制ForEach循环渲染

ForEach基于数组类型数据执行循环渲染。说明,从API version 9开始,该接口支持在ArkTS卡片中使用。
一、接口描述

ForEach(arr: any[], itemGenerator: (item: any, index?: number) => void,keyGenerator?: (item: any, index?: number) => string 
)

#2023盲盒+码#HarmonyOS/OpenHarmony应用开发-ArkTS语言渲染控制ForEach循环渲染-开源基础软件社区


二、使用限制
ForEach必须在容器组件内使用。
生成的子组件应当是允许包含在ForEach父容器组件中的子组件。
允许子组件生成器函数中包含if/else条件渲染,同时也允许ForEach包含在if/else条件渲染语句中。
itemGenerator函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正确运行

ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }), item => Text(`${item.i}. item.data.label`),item => item.data.id.toString())

三、开发者的建议
建议开发者不要假设项构造函数的执行顺序。执行顺序可能不能是数组中项的排列顺序。
不要假设数组项是否是初始渲染。ForEach的初始渲染在@Component首次渲染时构建所有数组项。后续框架版本中可能会将此行为更改为延迟加载模式。
使用 index参数对UI更新性能有严重的负面影响,请尽量避免。
如果项构造函数中使用index参数,则项索引函数中也必须使用该参数。否则,如果项索引函数未使用index参数,ForEach在生成实际的键值时,框架也会把index考虑进来,默认将index拼接在后面。
四、使用场景
1.简单ForEach示例
根据arr数据分别创建3个Text和Divide组件。

@Entry
@Component
struct MyComponent {@State arr: number[] = [10, 20, 30];build() {Column({ space: 5 }) {Button('Reverse Array').onClick(() => {this.arr.reverse();})ForEach(this.arr, (item: number) => {Text(`item value: ${item}`).fontSize(18)Divider().strokeWidth(2)}, (item: number) => item.toString())}}
}

2.复杂ForEach示例

@Component
struct CounterView {label: string;@State count: number = 0;build() {Button(`${this.label}-${this.count} click +1`).width(300).height(40).backgroundColor('#a0ffa0').onClick(() => {this.count++;})}
}@Entry
@Component
struct MainView {@State arr: number[] = Array.from(Array(10).keys()); // [0.,.9]nextUnused: number = this.arr.length;build() {Column() {Button(`push new item`).onClick(() => {this.arr.push(this.nextUnused++)}).width(300).height(40)Button(`pop last item`).onClick(() => {this.arr.pop()}).width(300).height(40)Button(`prepend new item (unshift)`).onClick(() => {this.arr.unshift(this.nextUnused++)}).width(300).height(40)Button(`remove first item (shift)`).onClick(() => {this.arr.shift()}).width(300).height(40)Button(`insert at pos ${Math.floor(this.arr.length / 2)}`).onClick(() => {this.arr.splice(Math.floor(this.arr.length / 2), 0, this.nextUnused++);}).width(300).height(40)Button(`remove at pos ${Math.floor(this.arr.length / 2)}`).onClick(() => {this.arr.splice(Math.floor(this.arr.length / 2), 1);}).width(300).height(40)Button(`set at pos ${Math.floor(this.arr.length / 2)} to ${this.nextUnused}`).onClick(() => {this.arr[Math.floor(this.arr.length / 2)] = this.nextUnused++;}).width(300).height(40)ForEach(this.arr,(item) => {CounterView({ label: item.toString() })},(item) => item.toString())}}

MainView拥有一个@State装饰的数字数组。添加、删除和替换数组项是可观察到的变化事件,当这些事件发生时,MainView内的ForEach都会更新。
项目索引函数为每个数组项创建唯一且持久的键值,ArkUI框架通过此键值确定数组中的项是否有变化,只要键值相同,数组项的值就假定不变,但其索引位置可能会更改。此机制的运行前提是不同的数组项不能有相同的键值。
使用计算出的ID,框架可以对添加、删除和保留的数组项加以区分:
(1)框架将删除已删除数组项的UI组件。
(2)框架仅对新添加的数组项执行项构造函数。
(3)框架不会为保留的数组项执行项构造函数。如果数组中的项索引已更改,框架将仅根据新顺序移动其UI组件,但不会更新该UI组件。
建议使用项目索引函数,但这是可选的。生成的ID必须是唯一的,这意味着不能为数组中的不同项计算出相同的ID。即使两个数组项具有相同的值,其ID也必须不同。
如果数组项值更改,则ID必须更改。
示例:如前所述,id生成函数是可选的。以下是不带项索引函数的ForEach:
ForEach(this.arr,
(item) => {
CounterView({ label: item.toString() })
}
)
如果没有提供项ID函数,则框架会尝试在更新ForEach时智能检测数组更改。但是,它可能会删除子组件,并为在数组中移动(索引被更改)的数组项重新执行项构造函数。在上面的示例中,这将更改应用程序针对CounterView counter状态的行为。创建新的CounterView实例时,counter的值将初始化为0。
3.使用@ObjectLink的ForEach示例
当需要保留重复子组件的状态时,@ObjectLink可将状态在组件树中向父组件推送。

let NextID: number = 0;@Observed
class MyCounter {public id: number;public c: number;constructor(c: number) {this.id = NextID++;this.c = c;}
}@Component
struct CounterView {@ObjectLink counter: MyCounter;label: string = 'CounterView';build() {Button(`CounterView [${this.label}] this.counter.c=${this.counter.c} +1`).width(200).height(50).onClick(() => {this.counter.c += 1;})}
}@Entry
@Component
struct MainView {@State firstIndex: number = 0;@State counters: Array<MyCounter> = [new MyCounter(0), new MyCounter(0), new MyCounter(0),new MyCounter(0), new MyCounter(0)];build() {Column() {ForEach(this.counters.slice(this.firstIndex, this.firstIndex + 3),(item) => {CounterView({ label: `Counter item #${item.id}`, counter: item })},(item) => item.id.toString())Button(`Counters: shift up`).width(200).height(50).onClick(() => {this.firstIndex = Math.min(this.firstIndex + 1, this.counters.length - 3);})Button(`counters: shift down`).width(200).height(50).onClick(() => {this.firstIndex = Math.max(0, this.firstIndex - 1);})}}
}

当增加firstIndex的值时,Mainview内的ForEach将更新,并删除与项ID firstIndex-1关联的CounterView子组件。对于ID为firstindex + 3的数组项,将创建新的CounterView子组件实例。由于CounterView子组件的状态变量counter值由父组件Mainview维护,故重建CounterView子组件实例不会重建状态变量counter值。
说明,违反上述数组项ID规则是最常见的应用开发错误,尤其是在Array场景下,因为执行过程中很容易添加重复的数字。
4.ForEach的嵌套使用
允许将ForEach嵌套在同一组件中的另一个ForEach中,但更推荐将组件拆分为两个,每个构造函数只包含一个ForEach。下面为ForEach嵌套使用反例。

class Month {year: number;month: number;days: number[];constructor(year: number, month: number, days: number[]) {this.year = year;this.month = month;this.days = days;}
}
@Component
struct CalendarExample {// 模拟6个月@State calendar : Month[] = [new Month(2020, 1, [...Array(31).keys()]),new Month(2020, 2, [...Array(28).keys()]),new Month(2020, 3, [...Array(31).keys()]),new Month(2020, 4, [...Array(30).keys()]),new Month(2020, 5, [...Array(31).keys()]),new Month(2020, 6, [...Array(30).keys()])]build() {Column() {Button() {Text('next month')}.onClick(() => {this.calendar.shift()this.calendar.push(new Month(year: 2020, month: 7, days: [...Array(31).keys()]))})ForEach(this.calendar,(item: Month) => {ForEach(item.days,(day : number) => {// 构建日期块},(day : number) => day.toString())// 内部ForEach},(item: Month) => (item.year * 12 + item.month).toString() // 字段与年和月一起使用,作为月份的唯一ID。)// 外部ForEach}}
}

以上示例存在两个问题:
(1)代码可读性差。
(2)对于上述的年月份数据的数组结构形式,由于框架无法观察到针对该数组中Month数据结构的改变(比如day数组变化),从而内层的ForEach无法刷新日期显示。
建议应用设计时将Calendar拆分为Year、Month和Day子组件。定义一个“Day”模型类,以保存有关day的信息,并用@Observed装饰此类。DayView组件利用ObjectLink装饰变量以绑定day数据。对MonthView和Month模型类执行同样的操作。
5.ForEach中使用可选index参数示例
可以在构造函数和ID生成函数中使用可选的index参数。

@Entry
@Component
struct ForEachWithIndex {@State arr: number[] = [4, 3, 1, 5];build() {Column() {ForEach(this.arr,(it, indx) => {Text(`Item: ${indx} - ${it}`)},(it, indx) => {return `${indx} - ${it}`})}}
}

必须正确构造ID生成函数。当在项构造函数中使用index参数时,ID生成函数也必须使用index参数,以生成唯一ID和给定源数组项的ID。当数组项在数组中的索引位置发生变化时,其ID会发生变化。
此示例还说明了index参数会造成显著性能下降。即使项在源数组中移动而不做修改,因为索引发生改变,依赖该数组项的UI仍然需要重新渲染。例如,使用索引排序时,数组只需要将ForEach未修改的子UI节点移动到正确的位置,这对于框架来说是一个轻量级操作。而使用索引时,所有子UI节点都需要重新构建,这操作负担要重得多。

 

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

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

相关文章

Java云原生框架Quarkus初探

Java云原生框架Quarkus初探 Quarkus 介绍 Quarkus 是一个云原生&#xff0c;容器优先的Java应用框架&#xff0c;它号称是超音速和亚原子的框架&#xff0c;主要特点是构建速度、启动速度快和占用资源少等特点。它为OpenJDK HotSpot和GraalVM量身定制&#xff0c; 根据Java库和…

postgresql中基础sql查询

postgresql中基础sql查询 创建表插入数据创建索引删除表postgresql命令速查简单查询计算查询结果 利用查询条件过滤数据模糊查询 创建表 -- 部门信息表 CREATE TABLE departments( department_id INTEGER NOT NULL -- 部门编号&#xff0c;主键, department_name CHARACTE…

LLM提示词工程和提示词工程师Prompting and prompt engineering

你输入模型的文本被称为提示&#xff0c;生成文本的行为被称为推断&#xff0c;输出文本被称为完成。用于提示的文本或可用的内存的全部量被称为上下文窗口。尽管这里的示例显示模型表现良好&#xff0c;但你经常会遇到模型在第一次尝试时无法产生你想要的结果的情况。你可能需…

vue开发环境搭建(WebStorm)

一、安装Node.js&#xff0c;搭建Vue环境 1、访问Node.js官网&#xff08;https://nodejs.org/en/download/&#xff09;进行安装包下载。 2、下载成功之后运行安装程序&#xff0c;进行安装。 如果是用安装程序进行安装&#xff0c;在安装过程中会自动进行Nodejs环境变量的配置…

css 实现svg动态图标效果

效果演示&#xff1a; 实现思路&#xff1a;主要是通过css的stroke相关属性来设置实现的。 html代码: <svgt"1692441666814"class"icon"viewBox"0 0 1024 1024"version"1.1"xmlns"http://www.w3.org/2000/svg"p-id"…

数据结构:栈和队列

文章目录 一、栈1.栈的概念及结构1.栈的概念及结构2.栈的实现 2.栈的顺序表实现1.栈的结构体和实现的功能函数2.栈的初始化&#xff0c;入栈和出栈操作3.栈的其他操作 3.栈的链表实现1.栈的结构体和实现的功能函数2.栈功能函数的实现 二、队列1.队列的概念及结构1.队列的概念及…

java面向对象——继承以及super关键字

继承的概念 1. 被继承的类称为父类&#xff08;超类&#xff09;&#xff0c;继承父类的类都称为子类&#xff08;派生类&#xff09; 2. 继承是指一个对象直接使用另一个对象的属性和方法&#xff0c;但是能继承非私有的属性和方法&#xff1b;(1) 构造方法不能被继承。(2) 但…

商城-学习整理-高级-商城业务-异步线程池(十三)

目录 一、线程1、初始化线程的 4 种方式2、线程池的七大参数3、线程池的运行流程&#xff1a;4、例子5、常见的 4 种线程池6、开发中为什么使用线程池 二、CompletableFuture 异步编排0、业务场景&#xff1a;1、创建异步对象2、计算完成时回调方法3、handle 方法4、线程串行化…

【无监督】6、SimSiam | 基于孪生网络的对比学习的成功源于梯度截断!

文章目录 一、背景二、方法三、效果 论文&#xff1a;Exploring Simple Siamese Representation Learning 出处&#xff1a;FAIR | 何恺明大佬 本文作者抛出了两个爆炸&#x1f4a5;性结论&#xff1a; 结论一&#xff1a;基于孪生网络的对比的学习的成功&#xff0c;不源于 …

亚马逊云科技 云技能孵化营——机器学习心得

亚马逊云科技 云技能孵化营机器学习心得 前言什么是机器学习&#xff1f;机器学习如何解决业务问题&#xff1f;什么时候适合使用机器学习模型&#xff1f;总结 前言 很荣幸参加了本次亚马逊云科技云技能孵化营&#xff0c;再本期的《亚马逊云科技云技能孵化营》中&#xff0c…

Leetcode-每日一题【剑指 Offer 33. 二叉搜索树的后序遍历序列】

题目 输入一个整数数组&#xff0c;判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true&#xff0c;否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树&#xff1a; 5 / \ 2 6 / \ 1 3 示例 1&#xff1a; 输入: […

基于樽海鞘群算法优化的BP神经网络(预测应用) - 附代码

基于樽海鞘群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于樽海鞘群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.樽海鞘群优化BP神经网络2.1 BP神经网络参数设置2.2 樽海鞘群算法应用 4.测试结果&#xff1a;5…

Flink内核源码解析--Flink中重要的工作组件和机制

Flink内核源码 1、掌握Flink应用程序抽象2、掌握Flink核心组件整体架构抽象3、掌握Flink Job三种运行模式4、理解Flink RPC网络通信框架Akka详解5、理解TaskManager为例子&#xff0c;分析Flink封装Akka Actor的方法和整个调用流程6、理解Flink高可用服务HighAvailabilityServ…

初识网络原理(笔记)

目录 ​编辑局域网 网络通信基础 IP 地址 端口号 协议 协议分层 TCP / IP 五层网络模型 网络数据传输的基本流程 发送方的情况&#xff1a; 接收方的情况 局域网 搭建网络的时候&#xff0c;需要用到 交换机 和 路由器 路由器上&#xff0c;有 lan 口 和 wan 口 虽…

基础恢复1-c语言

用书&#xff1a;c primer plus 学习时间&#xff1a;21-25 重点知识&#xff1a; 1.编译-链接-运行 编译&#xff1a;编译器将源码转换为可执行代码 链接&#xff1a;编译器从c库中获取标准例程放入源码中一同编译 运行&#xff1a;运行可执行文件 2.关键字 数据类型&…

Android oaid

官方GitHub地址 https://github.com/gzu-liyujiang/Android_CN_OAID 生成和用途介绍 https://www.jianshu.com/p/1c7ef27d6db4 图片来源于上述网站 其他关于id的介绍 https://www.cnblogs.com/chenKnowledgeConllection/p/17380960.html https://zhuanlan.zhihu.com/p/55…

分享图片 | 快速浏览网页资源,批量保存、一键分享图片

前言 小伙伴学习吉他&#xff0c;有时需要在互联网搜索曲谱资源&#xff0c;而多数曲谱均为图片&#xff0c;并且为多页&#xff0c;在电脑上显示练习很不方便&#xff0c;需要停下来点击鼠标进行翻页&#xff0c;影响练习的连贯性。 为了解决上述问题&#xff0c;通常把图片…

博客系统之单元测试

对博客系统进行单元测试 1、测试查找已存在的用户 测试名称 selectByUsernameTest01 测试源码 //查找用户&#xff0c;存在 Test public void selectByUsernameTest01 () { UserDao userDao new UserDao(); String ret1 userDao.selectByUsername("张三").toStr…

MYSQL完全卸载、安装与账号创建、权限控制

一、卸载mysql CentOS 卸载 MySQL 1. 查看安装情况 使用以下命令查看当前安装mysql情况&#xff0c;查找以前是否装有mysql rpm -qa|grep -i mysql这里显示我安装的 MySQL 服务有有&#xff1a; 2. 停止 mysql 服务、删除之前安装的 mysql 删除命令&#xff1a;rpm -e –n…

Eslint error, configuration for rule “import/no-cycle“ is invalid

可以参考stackoverflow.comEslint error, configuration for rule "import/no-cycle" is invalid他的意思是有个∞符号不支持&#xff0c;解决方案&#xff0c;把 eslint-plugin-import 的版本增加到 ^2.22.1&#xff0c;重新下载依赖包如&#xff1a;