赞
踩
目录
上篇文章中我们介绍了LazyForEach的基本使用,展示了如何使用LazyForEach构造一个列表,并演示数据的添加、删除、修改如何与LazyForEach配合并正确的更新UI。本篇将介绍使用LazyForEach的时候会遇到的一些常见问题。
代码如下:
- import { SimpleStringDataSource } from './base/LazyForeach';
-
- @Entry
- @Component
- struct LazyForEachDemo3Page {
- private data: SimpleStringDataSource = new SimpleStringDataSource();
-
- 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(index);
- // 重置所有子组件的index索引
- // this.data.reloadData();
- })
- }, (item: string, index: number) => item + index.toString())
- }.cachedCount(5).width('100%').height('100%')
- }
- }
运行结果如下:
当我们多次点击子组件时,会发现删除的并不一定是我们点击的那个子组件。原因是当我们删除了某一个子组件后,位于该子组件对应的数据项之后的各数据项,其index均应减1,但实际上后续的数据项对应的子组件仍然使用的是最初分配的index,其itemGenerator中的index并没有发生变化,所以删除结果和预期不符。如何修复呢?把注释的那行代码打开即可(在删除一个数据项后调用reloadData方法,重建后面的数据项,以达到更新index索引的目的)。
如下代码只更新文本时,会引起图片的更新,看上去发生了闪烁:
- import { StringData } from './base/LazyData';
- import { ComplexDataSource } from './base/LazyForeach';
-
- @Entry
- @Component
- struct LazyRenderDemo4Page {
- private moved: number[] = [];
- private data: ComplexDataSource = new ComplexDataSource();
-
- aboutToAppear() {
- for (let i = 0; i <= 20; i++) {
- this.data.pushData(new StringData(`Hello ${i}`, $r('app.media.girl1')));
- }
- }
-
- build() {
- List({ space: 3 }) {
- LazyForEach(this.data, (item: StringData, index: number) => {
- ListItem() {
- Column() {
- Text(item.message).fontSize(50)
- .onAppear(() => {
- console.info("appear:" + item.message)
- })
- Image(item.imgSrc)
- .width(500)
- .height(200)
- }.margin({ left: 10, right: 10 })
- }
- .onClick(() => {
- item.message += '00';
- this.data.reloadData();
- })
- }, (item: StringData, index: number) => JSON.stringify(item))
- }.cachedCount(5).width('100%').height('100%')
- }
- }
运行效果如下:
更新文本时,图片闪烁
在我们点击ListItem子组件时,我们只改变了数据项的message属性,但是LazyForEach的刷新机制会导致整个ListItem被重建。由于Image组件是异步刷新,所以视觉上图片会发生闪烁。为了解决这种情况我们应该使用@ObjectLink和@Observed去单独刷新使用了item.message的Text组件(代码里还修改了键值对的生成规则)。
- import { StringData } from './base/LazyData';
- import { ComplexDataSource } from './base/LazyForeach';
-
- @Entry
- @Component
- struct LazyRenderDemo4Page {
- private moved: number[] = [];
- private data: ComplexDataSource = new ComplexDataSource();
-
- aboutToAppear() {
- for (let i = 0; i <= 20; i++) {
- this.data.pushData(new StringData(`Hello ${i}`, $r('app.media.girl1')));
- }
- }
-
- build() {
- List({ space: 3 }) {
- LazyForEach(this.data, (item: StringData, index: number) => {
- ListItem() {
- LazyDemo4ChildComponent({data: item})
- }
- .onClick(() => {
- item.message += '00';
- this.data.reloadData();
- })
- }, (item: StringData, index: number) => {
- //key不变,此时只更新相关item的数据
- return index.toString()
- })
- }.cachedCount(5).width('100%').height('100%')
- }
- }
-
- @Component
- struct LazyDemo4ChildComponent {
- @ObjectLink data: StringData
- build() {
- Column() {
- Text(this.data.message).fontSize(50)
- .onAppear(() => {
- console.info("appear:" + this.data.message)
- })
- Image(this.data.imgSrc)
- .width(500)
- .height(200)
- }.margin({ left: 10, right: 10 })
- }
- }
修复后运行效果如下:
只更新文本,图片不闪烁
- import { NestedString, StringDataV2 } from './base/LazyData';
- import { MyDataSource } from './base/LazyForeach';
-
- @Entry
- @Component
- struct LazyRenderDemo5Page {
- @State data: MyDataSource<StringDataV2> = new MyDataSource<StringDataV2>();
-
- aboutToAppear() {
- for (let i = 0; i <= 20; i++) {
- this.data.pushData(new StringDataV2(new NestedString(`Hello ${i}`)));
- }
- }
-
- build() {
- List({ space: 3 }) {
- LazyForEach(this.data, (item: StringDataV2, index: number) => {
- ListItem() {
- LazyRenderDemo5ChildComponent({data: item})
- }
- .onClick(() => {
- // item.message.message += '0';
- item.message = new NestedString(item.message.message + '0');
- })
- }, (item: StringDataV2, index: number) => item.toString() + index.toString())
- }.cachedCount(5).width('100%').width('100%')
- }
- }
-
- @Component
- struct LazyRenderDemo5ChildComponent {
- @ObjectLink data: StringDataV2
- build() {
- Row() {
- Text(this.data.message.message).fontSize(50)
- .onAppear(() => {
- console.info("appear:" + this.data.message.message)
- })
- }.margin({ left: 10, right: 10 })
- }
- }
-
-
-
- @Observed
- export class StringDataV2 {
- message: NestedString;
- constructor(message: NestedString) {
- this.message = message;
- }
- }
-
- @Observed
- export class NestedString {
- message: string;
- constructor(message: string) {
- this.message = message;
- }
- }
@ObjectLink装饰的成员变量仅能监听到其子属性的变化,再深入嵌套的属性便无法观测到了,因此我们只能改变它的子属性去通知对应组件重新渲染,具体请查看@ObjectLink与@Observed的详细使用方法和限制条件。
如何修复呢?既然不支持2层嵌套属性变化,那就在第一场嵌套属性变化上作文章,修复代码如下:
- onClick(() => {
- item.message = new NestedString(item.message.message + '0');
- })
比较简单,就是修改第一层嵌套属性的值(@ObjectLink能监听到该层的变化)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。