鸿蒙列表新的实现方式
鸿蒙系统推出了全新的列表组件Repeat,相比之前的ForEach和LazyForEach,Repeat性能更优且使用更简便。Repeat基于数组数据循环渲染,需与List、Grid等滚动容器配合使用,支持.each()单模板和.template()多模板渲染方式,提供条件渲染能力并通过virtualScroll实现懒加载优化性能。
鸿蒙列表新的实现方式
前言
鸿蒙的更新还是很快的,一不注意,就有性能提升的组件或者api 出现。从一开始api9,一直到现在,其实经历了很多,比如List组件,最开始使用的是ForEach来循环子组件,接着如果是长列表为了更好的性能,就需要使用LazyForEach,不过它有点难用,直到现在又出现了新的 Repeat。
所以呢,如果新开发的页面中涉及列表,直接用Repeat,使用起来也非常简单。这就好比,在android开发的时候,使用ListView 和 使用 Recyclerview的感觉。当有了RecyclerView,可以直接放弃ListView,因为RecyclerView 性能更好,而且覆盖了ListView的功能。同理,鸿蒙有了Repeat,写列表相关的页面时直接用 List + Repeat 即可。
一、Repeat
1、概述
Repeat基于数组类型数据来进行循环渲染,一般与容器组件配合使用。Repeat根据容器组件的有效加载范围(屏幕可视区域+预加载区域)加载子组件。当容器滑动/数组改变时,Repeat会根据父容器组件的布局过程重新计算有效加载范围,并管理列表子组件节点的创建与销毁。Repeat通过组件节点更新/复用从而优化性能表现。
2、使用限制
- Repeat必须在滚动类容器组件内使用,仅有List、Grid、Swiper 以及 WaterFlow 组件支持 Repeat 懒加载场景。循环渲染只允许创建一个子组件,子组件应当是允许包含在容器组件中的子组件。例如:Repeat 与 List 组件配合使用时,子组件必须为 ListItem 组件。
- Repeat不支持V1装饰器,混用V1装饰器会导致渲染异常。
- Repeat当前不支持动画效果。
- 滚动容器组件内只能包含一个Repeat。以List为例,同时包含ListItem、ForEach、LazyForEach的场景是不推荐的;同时包含多个Repeat也是不推荐的。
- 当Repeat与自定义组件或@Builder函数混用时,必须将RepeatItem类型整体进行传参,组件才能监听到数据变化。
二、使用说明
Repeat子组件由.each()和.template()属性定义,只允许包含一个子组件。当页面首次渲染时,Repeat根据当前的有效加载范围(屏幕可视区域+预加载区域)按需创建子组件
1、循环渲染能力
.each()适用于只需要循环渲染一种子组件的场景
代码如下(示例):
// 在List容器组件中使用Repeat
@Entry
@ComponentV2 // 推荐使用V2装饰器
struct RepeatExample {
@Local dataArr: Array<string> = []; // 数据源
aboutToAppear(): void {
for (let i = 0; i < 50; i++) {
this.dataArr.push(`data_${i}`); // 为数组添加一些数据
}
}
build() {
Column() {
List() {
Repeat<string>(this.dataArr)
.each((ri: RepeatItem<string>) => {
ListItem() {
Text('each_' + ri.item).fontSize(30)
}
})
.virtualScroll({ totalCount: this.dataArr.length }) // 打开懒加载,totalCount为期望加载的数据长度
}
.cachedCount(2) // 容器组件的预加载区域大小
.height('70%')
.border({ width: 1 }) // 边框
}
}
}
.template()适用于在同一个数据源中渲染多种子组件
每个数据项会根据.templateId()得到template type,从而渲染type对应的.template()中的子组件。
- 如果.templateId()缺省,则type默认为空字符串。
- 当多个template type相同时,Repeat会覆盖先定义的.template()函数,仅生效最后定义的.template()。
- 如果找不到对应的template type,Repeat会先渲染type为空的.template()中的子组件,如果没有则渲染.each()中的子组件。
- 只有相同template的节点可以互相复用。
代码如下(示例):
// 在List容器组件中使用Repeat
@Entry
@ComponentV2 // 推荐使用V2装饰器
struct RepeatExampleWithTemplates {
@Local dataArr: Array<string> = []; // 数据源
aboutToAppear(): void {
for (let i = 0; i < 50; i++) {
this.dataArr.push(`data_${i}`); // 为数组添加一些数据
}
}
build() {
Column() {
List() {
Repeat<string>(this.dataArr)
.each((ri: RepeatItem<string>) => { // 默认渲染模板
ListItem() {
Text('each_' + ri.item).fontSize(30).fontColor('rgb(161,10,33)') // 文本颜色为红色
}
})
.key((item: string, index: number): string => JSON.stringify(item)) // 键值生成函数
.virtualScroll({ totalCount: this.dataArr.length }) // 打开懒加载,totalCount为期望加载的数据长度
.templateId((item: string, index: number): string => { // 根据返回值寻找对应的模板子组件进行渲染
return index <= 4 ? 'A' : (index <= 10 ? 'B' : ''); // 前5个节点模板为A,接下来的5个为B,其余为默认模板
})
.template('A', (ri: RepeatItem<string>) => { // 'A'模板
ListItem() {
Text('A_' + ri.item).fontSize(30).fontColor('rgb(23,169,141)') // 文本颜色为绿色
}
}, { cachedCount: 3 }) // 'A'模板的缓存列表容量为3
.template('B', (ri: RepeatItem<string>) => { // 'B'模板
ListItem() {
Text('B_' + ri.item).fontSize(30).fontColor('rgb(39,135,217)') // 文本颜色为蓝色
}
}, { cachedCount: 4 }) // 'B'模板的缓存列表容量为4
}
.cachedCount(2) // 容器组件的预加载区域大小
.height('70%')
}
}
}
2、条件渲染
通过if/else 在each中可显示不同模板,实现条件化渲染
.each((item: RepeatItem<number>)=>{
ListItem(){
//条件渲染,不同的条件显示不同的布局
if (item.index % 2 === 0) {
Text('' + item.item)
.textAlign(TextAlign.Center)
.width('90%')
.height(72)
.backgroundColor('#FFFFFF')
.borderRadius(24)
}else{
Text('我是奇数布局' + item.item)
.textAlign(TextAlign.Center)
.width('90%')
.height(72)
.backgroundColor('#FFFFFF')
.borderRadius(24)
}
}
})
3、VirtualScrollOptions对象说明
-
totalCount:期望加载的数据长度,默认为原数组长度,可以大于已加载数据项的数量。假设 arr.length 表示数据源长度,则
totalCount 缺省或是非自然数时,totalCount 默认为 arr.length,列表正常滚动。
0 <= totalCount < arr.length 时,界面中只渲染区间 [0, totalCount - 1] 范围内的数据。
totalCount > arr.length 时,代表 Repeat 将渲染区间 [0, totalCount - 1] 范围内的数据,滚动条样式根据 totalCount 值变化。 -
onLazyLoading:数据精准懒加载
-
onTotalCount:计算期望的数据长度
详细说明参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-rendering-control-repeat#virtualscrolloptions%E5%AF%B9%E8%B1%A1%E8%AF%B4%E6%98%8E
// arr是Array<string>类型的数组,在List容器组件中使用Repeat,并打开virtualScroll
// 将加载的数据项总数设为数据源的长度,并开启复用功能
List() {
Repeat<string>(this.arr)
.each((obj: RepeatItem<string>) => { ListItem() { Text(obj.item) }})
.virtualScroll( { totalCount: this.arr.length, reusable: true } )
}
// 假设数据项总数为100,首屏渲染需3项数据
// 初始数组提供前3项数据(arr = ['No.0', 'No.1', 'No.2']),并开启数据懒加载功能
List() {
Repeat<string>(this.arr)
.each((obj: RepeatItem<string>) => { ListItem() { Text(obj.item) }})
.virtualScroll({
onTotalCount: () => { return 100; },
onLazyLoading: (index: number) => { this.arr[index] = `No.${index}`; }
})
}
4、Repeat性能上的优化
- key 函数确保列表高效复用
- virtualScroll 启用懒加载,提升长列表性能
- 模板复用
5、注意事项
使用 Repeat 组件确保是在V2中使用,避免出现问题。
更多推荐
所有评论(0)