【前言】
之前在"
十一、【鸿蒙 NEXT】@Observed装饰器和@ObjectLink装饰器"这边文章中介绍了如何动态刷新列表数据,由于@Observed状态的刷新还是有很多限制,比如父组件持有的是数组对象,想要动态刷新,则无法实现等,这里介绍一个功能强大的状态类型@ObservedV2/@Trace的使用方式,更加简洁,且效率更高,这里结合LazyFoeach的使用,来实现列表数据的动态刷新
1、我们首先看下LazyFoeach的接口定义

其实和Foreach差不多,只不过第一个参数是一个IDataSource对象,而不是一个数组对象。列表的刷新机制也和Foreach一样,如果第三个参数生成的key变化了,就会销毁老的组件,重新创建新的组件。但是这种方式和第十一章的Foreach刷新一样会导致图片闪烁的问题。因此下面介绍下
@ObservedV2/@Trace动态刷新每个列表的子组件
2、@ObservedV2/@Trace动态刷新
使用@ObservedV2动态刷新
(1)首先定义消息对象,也就是数组中的消息类型,声明@ObservedV2,同时对于需要动态刷新的属性声明@Trace

(2)对于LazyForeach的第三个参数,直接用Message中的id作为每个item的key,只要id不变,就不重新创建子组件,这样能保证不会出现图片闪烁的情况

(3)刷新数据时,直接找到对应id的数据,修改name值,就会实现刷新

实现效果如下,这里图片不会出现闪烁情况:

使用LazyForeach的key值变更实现刷新:
(1)在上面基础上,将LazyForeach的第三个参数改为对Message的json序列化字符串

(2)找到对应id的index,然后修改对应index的值,刷新数据:

LazyForeach的key值变更实现刷新效果如下,会出现图片闪烁情况:

@ObservedV2/@Trace完整代码如下:
javascript
import { DateSource } from './DateSource'
@Entry
@ComponentV2
struct LazyForeachPage {
dataSource: DateSource<Message> = new DateSource()
private index = 1
aboutToAppear(): void {
let data1 = new Message()
data1.id = '1'
data1.name = '张三'
data1.icon = $r('app.media.touxiang1')
let data2 = new Message()
data2.id = '2'
data2.name = '张四'
data2.icon = $r('app.media.touxiang1')
let data3 = new Message()
data3.id = '3'
data3.name = '张五'
data3.icon = $r('app.media.touxiang1')
this.dataSource.addItems([data1,data2,data3])
}
build() {
Column(){
Button('ObservedV2刷新').onClick(() => {
this.index++
this.dataSource.getAllData().find(item => item.id === '3')!.name = `张${this.index}`
})
// Button('原始刷新').onClick(() => {
// this.index++
// this.dataSource.changeData(2,{id:'3', name: `张${this.index}`, icon:$r('app.media.touxiang1')} as Message)
// })
List({space:10}) {
LazyForEach(this.dataSource, (item:Message)=> {
ListItem() {
Row(){
Image(item.icon).height(40).width(40)
Text(item.name)
}
}
}, (item:Message) => item.id)
}
}
.padding({left:32})
.height('100%')
.width('100%')
}
}
@ObservedV2
class Message {
id:string = ''
@Trace name:string = ''
icon:ResourceStr = ''
}
dataSource代码如下
javascript
export class DateSource<T> implements IDataSource{
private dataArray: T[] = [];
private listeners: DataChangeListener[] = [];
// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): T {
return this.dataArray[index];
}
public getAllData(): T[] {
return this.dataArray;
}
// 通知LazyForEach组件需要重载所有子组件
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
});
}
// 通知LazyForEach组件需要在index对应索引处添加子组件
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
});
}
// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
});
}
public changeData(index: number, data: T): void {
this.dataArray.splice(index, 1, data);
this.notifyDataChange(index);
}
public refreshItems(list: T[]): void {
this.dataArray = [...list]
this.notifyDataReload()
}
public addItems(list:T[]) {
list.forEach(item => {
this.dataArray.push(item)
this.notifyDataAdd(this.dataArray.length - 1)
})
}
}