常德哪里有做网站,广告费计入什么科目,百度游戏中心官网,海口网站建设维护目录
1 背景
2 使用限制
键值生成规则
组件创建规则
首次渲染
非首次渲染
改变数据子属性
使用状态管理V2
拖拽排序 1 背景
LazyForEach从提供的数据源中按需迭代数据#xff0c;并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach#xff0c;框架…目录
1 背景
2 使用限制
键值生成规则
组件创建规则
首次渲染
非首次渲染
改变数据子属性
使用状态管理V2
拖拽排序 1 背景
LazyForEach从提供的数据源中按需迭代数据并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach框架会根据滚动容器可视区域按需创建组件当组件滑出可视区域外时框架会进行组件销毁回收以降低内存占用。
2 使用限制 LazyForEach必须在容器组件内使用仅有List、Grid、Swiper以及WaterFlow组件支持数据懒加载可配置cachedCount属性即只加载可视部分以及其前后少量数据用于缓冲其他组件仍然是一次性加载所有的数据。容器组件内使用LazyForEach的时候只能包含一个LazyForEach。以List为例同时包含ListItem、ForEach、LazyForEach的情形是不推荐的同时包含多个LazyForEach也是不推荐的。LazyForEach在每次迭代中必须创建且只允许创建一个子组件即LazyForEach的子组件生成函数有且只有一个根组件。生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。允许LazyForEach包含在if/else条件渲染语句中也允许LazyForEach中出现if/else条件渲染语句。键值生成器必须针对每个数据生成唯一的值如果键值相同将导致键值相同的UI组件渲染出现问题。LazyForEach必须使用DataChangeListener对象进行更新对第一个参数dataSource重新赋值会异常dataSource使用状态变量时状态变量改变不会触发LazyForEach的UI刷新。为了高性能渲染通过DataChangeListener对象的onDataChange方法来更新UI时需要生成不同于原来的键值来触发组件刷新。LazyForEach必须和Reusable装饰器一起使用才能触发节点复用。使用方法将Reusable装饰在LazyForEach列表的组件上见使用规则。 键值生成规则
在LazyForEach循环渲染过程中系统会为每个item生成一个唯一且持久的键值用于标识对应的组件。当这个键值变化时ArkUI框架将视为该数组元素已被替换或修改并会基于新的键值创建一个新的组件。
LazyForEach提供了一个名为keyGenerator的参数这是一个函数开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数则ArkUI框架会使用默认的键值生成函数即(item: Object, index: number) { return viewId - index.toString(); }, viewId在编译器转换过程中生成同一个LazyForEach组件内其viewId是一致的。
组件创建规则
在确定键值生成规则后LazyForEach的第二个参数itemGenerator函数会根据组件创建规则为数据源的每个数组项创建组件。组件的创建包括两种情况LazyForEach首次渲染和LazyForEach非首次渲染。
首次渲染
生成不同键值
在LazyForEach首次渲染时会根据上述键值生成规则为数据源的每个数组项生成唯一键值并创建相应的组件。
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}Entry
Component
struct MyComponent {private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(Hello ${i})}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}}, (item: string) item)}.cachedCount(5)}
}
在上述代码中键值生成规则是keyGenerator函数的返回值item。在LazyForEach循环渲染时其为数据源数组项依次生成键值Hello 0、Hello 1 ... Hello 20并创建对应的ListItem子组件渲染到界面上。
运行效果如下图所示。
图1 LazyForEach正常首次渲染 键值相同时错误渲染
当不同数据项生成的键值相同时框架的行为是不可预测的。例如在以下代码中LazyForEach渲染的数据项键值均相同在滑动过程中LazyForEach会对划入划出当前页面的子组件进行预加载而新建的子组件和销毁的原子组件具有相同的键值框架可能存在取用缓存错误的情况导致子组件渲染有问题。
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}Entry
Component
struct MyComponent {private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(Hello ${i})}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}}, (item: string) same key)}.cachedCount(5)}
}
运行效果如下图所示。
图2 LazyForEach存在相同键值 非首次渲染
当LazyForEach数据源发生变化需要再次渲染时开发者应根据数据源的变化情况调用listener对应的接口通知LazyForEach做相应的更新各使用场景如下。
添加数据
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}Entry
Component
struct MyComponent {private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(Hello ${i})}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}.onClick(() {// 点击追加子组件this.data.pushData(Hello ${this.data.totalCount()});})}, (item: string) item)}.cachedCount(5)}
}
当我们点击LazyForEach的子组件时首先调用数据源data的pushData方法该方法会在数据源末尾添加数据并调用notifyDataAdd方法。在notifyDataAdd方法内会又调用listener.onDataAdd方法该方法会通知LazyForEach在该处有数据添加LazyForEach便会在该索引处新建子组件。
运行效果如下图所示。
图3 LazyForEach添加数据 删除数据
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public getAllData(): string[] {return this.dataArray;}public pushData(data: string): void {this.dataArray.push(data);}public deleteData(index: number): void {this.dataArray.splice(index, 1);this.notifyDataDelete(index);}
}Entry
Component
struct MyComponent {private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(Hello ${i})}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}.onClick(() {// 点击删除子组件this.data.deleteData(this.data.getAllData().indexOf(item));})}, (item: string) item)}.cachedCount(5)}
}
当我们点击LazyForEach的子组件时首先调用数据源data的deleteData方法该方法会删除数据源对应索引处的数据并调用notifyDataDelete方法。在notifyDataDelete方法内会又调用listener.onDataDelete方法该方法会通知LazyForEach在该处有数据删除LazyForEach便会在该索引处删除对应子组件。
运行效果如下图所示。
图4 LazyForEach删除数据 交换数据
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public getAllData(): string[] {return this.dataArray;}public pushData(data: string): void {this.dataArray.push(data);}public moveData(from: number, to: number): void {let temp: string this.dataArray[from];this.dataArray[from] this.dataArray[to];this.dataArray[to] temp;this.notifyDataMove(from, to);}
}Entry
Component
struct MyComponent {private moved: number[] [];private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(Hello ${i})}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}.onClick(() {this.moved.push(this.data.getAllData().indexOf(item));if (this.moved.length 2) {// 点击交换子组件this.data.moveData(this.moved[0], this.moved[1]);this.moved [];}})}, (item: string) item)}.cachedCount(5)}
}
当我们首次点击LazyForEach的子组件时在moved成员变量内存入要移动的数据索引再次点击LazyForEach另一个子组件时我们将首次点击的子组件移到此处。调用数据源data的moveData方法该方法会将数据源对应数据移动到预期的位置并调用notifyDataMove方法。在notifyDataMove方法内会又调用listener.onDataMove方法该方法通知LazyForEach在该处有数据需要移动LazyForEach便会将from和to索引处的子组件进行位置调换。
运行效果如下图所示。
图5 LazyForEach交换数据 改变单个数据
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);}public changeData(index: number, data: string): void {this.dataArray.splice(index, 1, data);this.notifyDataChange(index);}
}Entry
Component
struct MyComponent {private moved: number[] [];private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(Hello ${i})}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}.onClick(() {this.data.changeData(index, item 00);})}, (item: string) item)}.cachedCount(5)}
}
当我们点击LazyForEach的子组件时首先改变当前数据然后调用数据源data的changeData方法在该方法内会调用notifyDataChange方法。在notifyDataChange方法内会又调用listener.onDataChange方法该方法通知LazyForEach组件该处有数据发生变化LazyForEach便会在对应索引处重建子组件。
运行效果如下图所示。
图6 LazyForEach改变单个数据 改变多个数据
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public pushData(data: string): void {this.dataArray.push(data);}public reloadData(): void {this.notifyDataReload();}public modifyAllData(): void {this.dataArray this.dataArray.map((item: string) {return item 0;})}
}Entry
Component
struct MyComponent {private moved: number[] [];private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(Hello ${i})}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) {ListItem() {Row() {Text(item).fontSize(50).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}.onClick(() {this.data.modifyAllData();this.data.reloadData();})}, (item: string) item)}.cachedCount(5)}
}
当我们点击LazyForEach的子组件时首先调用data的modifyAllData方法改变了数据源中的所有数据然后调用数据源的reloadData方法在该方法内会调用notifyDataReload方法。在notifyDataReload方法内会又调用listener.onDataReloaded方法通知LazyForEach需要重建所有子节点。LazyForEach会将原所有数据项和新所有数据项一一做键值比对若有相同键值则使用缓存若键值不同则重新构建。
运行效果如下图所示。
图7 LazyForEach改变多个数据 精准批量修改数据
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public operateData(): void {console.info(JSON.stringify(this.dataArray));this.dataArray.splice(4, 0, this.dataArray[1]);this.dataArray.splice(1, 1);let temp this.dataArray[4];this.dataArray[4] this.dataArray[6];this.dataArray[6] tempthis.dataArray.splice(8, 0, Hello 1, Hello 2);this.dataArray.splice(12, 2);console.info(JSON.stringify(this.dataArray));this.notifyDatasetChange([{ type: DataOperationType.MOVE, index: { from: 1, to: 3 } },{ type: DataOperationType.EXCHANGE, index: { start: 4, end: 6 } },{ type: DataOperationType.ADD, index: 8, count: 2 },{ type: DataOperationType.DELETE, index: 10, count: 2 }]);}public init(): void {this.dataArray.splice(0, 0, Hello a, Hello b, Hello c, Hello d, Hello e, Hello f, Hello g, Hello h,Hello i, Hello j, Hello k, Hello l, Hello m, Hello n, Hello o, Hello p, Hello q, Hello r);}
}Entry
Component
struct MyComponent {private data: MyDataSource new MyDataSource();aboutToAppear() {this.data.init()}build() {Column() {Text(change data).fontSize(10).backgroundColor(Color.Blue).fontColor(Color.White).borderRadius(50).padding(5).onClick(() {this.data.operateData();})List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) {ListItem() {Row() {Text(item).fontSize(35).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}}, (item: string) item new Date().getTime())}.cachedCount(5)}}
}
onDatasetChange接口允许开发者一次性通知LazyForEach进行数据添加、删除、移动和交换等操作。在上述例子中点击“change data”文本后,第二项数据被移动到第四项位置第五项与第七项数据交换位置并且从第九项开始添加了数据Hello 1和Hello 2同时从第十一项开始删除了两项数据。
图8 LazyForEach改变多个数据 第二个例子直接给数组赋值不涉及 splice 操作。operations直接从比较原数组和新数组得到。
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public operateData(): void {this.dataArray [Hello x, Hello 1, Hello 2, Hello b, Hello c, Hello e, Hello d, Hello f, Hello g, Hello h]this.notifyDatasetChange([{ type: DataOperationType.CHANGE, index: 0 },{ type: DataOperationType.ADD, index: 1, count: 2 },{ type: DataOperationType.EXCHANGE, index: { start: 3, end: 4 } },]);}public init(): void {this.dataArray [Hello a, Hello b, Hello c, Hello d, Hello e, Hello f, Hello g, Hello h];}
}Entry
Component
struct MyComponent {private data: MyDataSource new MyDataSource();aboutToAppear() {this.data.init()}build() {Column() {Text(Multi-Data Change).fontSize(10).backgroundColor(Color.Blue).fontColor(Color.White).borderRadius(50).padding(5).onClick(() {this.data.operateData();})List({ space: 3 }) {LazyForEach(this.data, (item: string, index: number) {ListItem() {Row() {Text(item).fontSize(35).onAppear(() {console.info(appear: item)})}.margin({ left: 10, right: 10 })}}, (item: string) item new Date().getTime())}.cachedCount(5)}}
}
图9 LazyForEach改变多个数据 使用该接口时有如下注意事项。
onDatasetChange与其它操作数据的接口不能混用。传入onDatasetChange的operations其中每一项operation的index均从修改前的原数组内寻找。因此operations中的index跟操作Datasource中的index不总是一一对应的,而且不能是负数。
第一个例子清楚地显示了这一点:
// 修改之前的数组
[Hello a,Hello b,Hello c,Hello d,Hello e,Hello f,Hello g,Hello h,Hello i,Hello j,Hello k,Hello l,Hello m,Hello n,Hello o,Hello p,Hello q,Hello r]
// 修改之后的数组
[Hello a,Hello c,Hello d,Hello b,Hello g,Hello f,Hello e,Hello h,Hello 1,Hello 2,Hello i,Hello j,Hello m,Hello n,Hello o,Hello p,Hello q,Hello r]
Hello b 从第2项变成第4项因此第一个 operation 为 { type: DataOperationType.MOVE, index: { from: 1, to: 3 } }。
Hello e 跟 Hello g 对调了而 Hello e 在修改前的原数组中的 index4Hello g 在修改前的原数组中的 index6, 因此第二个 operation 为 { type: DataOperationType.EXCHANGE, index: { start: 4, end: 6 } }。
Hello 1,Hello 2 在 Hello h 之后插入而 Hello h 在修改前的原数组中的 index7因此第三个 operation 为 { type: DataOperationType.ADD, index: 8, count: 2 }。
Hello k,Hello l 被删除了而 Hello k 在原数组中的 index10因此第四个 operation 为 { type: DataOperationType.DELETE, index: 10, count: 2 }。
调用一次onDatasetChange一个index对应的数据只能被操作一次若被操作多次LazyForEach仅使第一个操作生效。部分操作可以由开发者传入键值LazyForEach不会再去重复调用keygenerator获取键值需要开发者保证传入的键值的正确性。若本次操作集合中有RELOAD操作则其余操作全不生效。
改变数据子属性
若仅靠LazyForEach的刷新机制当item变化时若想更新子组件需要将原来的子组件全部销毁再重新构建在子组件结构较为复杂的情况下靠改变键值去刷新渲染性能较低。因此框架提供了Observed与ObjectLink机制进行深度观测可以做到仅刷新使用了该属性的组件提高渲染性能。开发者可根据其自身业务特点选择使用哪种刷新方式。
/** BasicDataSource代码见文档末尾附件: StringData类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: StringData[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): StringData {return this.dataArray[index];}public pushData(data: StringData): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}Observed
class StringData {message: string;constructor(message: string) {this.message message;}
}Entry
Component
struct MyComponent {private moved: number[] [];private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(new StringData(Hello ${i}));}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: StringData, index: number) {ListItem() {ChildComponent({data: item})}.onClick(() {item.message 0;})}, (item: StringData, index: number) index.toString())}.cachedCount(5)}
}Component
struct ChildComponent {ObjectLink data: StringDatabuild() {Row() {Text(this.data.message).fontSize(50).onAppear(() {console.info(appear: this.data.message)})}.margin({ left: 10, right: 10 })}
}
此时点击LazyForEach子组件改变item.message时重渲染依赖的是ChildComponent的ObjectLink成员变量对其子属性的监听此时框架只会刷新Text(this.data.message)不会去重建整个ListItem子组件。
图10 LazyForEach改变数据子属性 使用状态管理V2
状态管理V2提供了ObservedV2与Trace装饰器可以实现对属性的深度观测使用Local和Param可以实现对子组件的刷新管理仅刷新使用了对应属性的组件。
嵌套类属性变化观测
/** BasicDataSource代码见文档末尾附件: StringData类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: StringData[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): StringData {return this.dataArray[index];}public pushData(data: StringData): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}class StringData {firstLayer: FirstLayer;constructor(firstLayer: FirstLayer) {this.firstLayer firstLayer;}
}class FirstLayer {secondLayer: SecondLayer;constructor(secondLayer: SecondLayer) {this.secondLayer secondLayer;}
}class SecondLayer {thirdLayer: ThirdLayer;constructor(thirdLayer: ThirdLayer) {this.thirdLayer thirdLayer;}
}ObservedV2
class ThirdLayer {Trace forthLayer: String;constructor(forthLayer: String) {this.forthLayer forthLayer;}
}Entry
ComponentV2
struct MyComponent {private data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(new StringData(new FirstLayer(new SecondLayer(new ThirdLayer(Hello i)))));}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: StringData, index: number) {ListItem() {Text(item.firstLayer.secondLayer.thirdLayer.forthLayer.toString()).fontSize(50).onClick(() {item.firstLayer.secondLayer.thirdLayer.forthLayer !;})}}, (item: StringData, index: number) index.toString())}.cachedCount(5)}
}
ObservedV2与Trace用于装饰类以及类中的属性配合使用能深度观测被装饰的类和属性。示例中展示了深度嵌套类结构下通过ObservedV2和Trace实现对多层嵌套属性变化的观测和子组件刷新。当点击子组件Text修改被Trace修饰的嵌套类最内层的类成员属性时仅重新渲染依赖了该属性的组件。
组件内部状态
/** BasicDataSource代码见文档末尾附件: StringData类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: StringData[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): StringData {return this.dataArray[index];}public pushData(data: StringData): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}ObservedV2
class StringData {Trace message: string;constructor(message: string) {this.message message;}
}Entry
ComponentV2
struct MyComponent {data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(new StringData(Hello i));}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: StringData, index: number) {ListItem() {Row() {Text(item.message).fontSize(50).onClick(() {// 修改ObservedV2装饰类中Trace装饰的变量触发刷新此处Text组件item.message !;})ChildComponent()}}}, (item: StringData, index: number) index.toString())}.cachedCount(5)}
}ComponentV2
struct ChildComponent {Local message: string ?;build() {Row() {Text(this.message).fontSize(50).onClick(() {// 修改Local装饰的变量触发刷新此处Text组件this.message ?;})}}
}
Local使得自定义组件内被修饰的变量具有观测其变化的能力该变量必须在组件内部进行初始化。示例中点击Text组件修改item.message会触发变量更新并刷新使用该变量的组件ChildComponent中Local装饰的变量message变化时也能刷新子组件。
组件外部输入
/** BasicDataSource代码见文档末尾附件: StringData类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: StringData[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): StringData {return this.dataArray[index];}public pushData(data: StringData): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}ObservedV2
class StringData {Trace message: string;constructor(message: string) {this.message message;}
}Entry
ComponentV2
struct MyComponent {data: MyDataSource new MyDataSource();aboutToAppear() {for (let i 0; i 20; i) {this.data.pushData(new StringData(Hello i));}}build() {List({ space: 3 }) {LazyForEach(this.data, (item: StringData, index: number) {ListItem() {ChildComponent({ data: item.message }).onClick(() {item.message !;})}}, (item: StringData, index: number) index.toString())}.cachedCount(5)}
}ComponentV2
struct ChildComponent {Param Require data: string ;build() {Row() {Text(this.data).fontSize(50)}}
}
使用Param装饰器可以让子组件接受外部输入的参数实现父子组件之间的数据同步。在MyComponent中创建子组件时将变量item.message传递使用Param修饰的变量data与之关联。点击ListItem中的组件修改item.message数据变化会从父组件传递到子组件并且触发子组件的刷新。
拖拽排序
当LazyForEach在List组件下使用并且设置了onMove事件可以使能拖拽排序。拖拽排序离手后如果数据位置发生变化则会触发onMove事件上报数据移动原始索引号和目标索引号。在onMove事件中需要根据上报的起始索引号和目标索引号修改数据源。onMove中修改数据源不需要调用DataChangeListener中接口通知数据源变化。
/** BasicDataSource代码见文档末尾附件: string类型数组的BasicDataSource代码 **/class MyDataSource extends BasicDataSource {private dataArray: string[] [];public totalCount(): number {return this.dataArray.length;}public getData(index: number): string {return this.dataArray[index];}public moveDataWithoutNotify(from: number, to: number): void {let tmp this.dataArray.splice(from, 1);this.dataArray.splice(to, 0, tmp[0])}public pushData(data: string): void {this.dataArray.push(data);this.notifyDataAdd(this.dataArray.length - 1);}
}Entry
Component
struct Parent {private data: MyDataSource new MyDataSource();aboutToAppear(): void {for (let i 0; i 100; i) {this.data.pushData(i.toString())}}build() {Row() {List() {LazyForEach(this.data, (item: string) {ListItem() {Text(item.toString()).fontSize(16).textAlign(TextAlign.Center).size({height: 100, width: 100%})}.margin(10).borderRadius(10).backgroundColor(#FFFFFFFF)}, (item: string) item).onMove((from:number, to:number){this.data.moveDataWithoutNotify(from, to)})}.width(100%).height(100%).backgroundColor(#FFDCDCDC)}}
}
图11 LazyForEach拖拽排序效果图