LazyForEach常见使用问题

       

目录

1、渲染结果非预期

2、重新渲染时图片闪烁

3、@ObjectLink属性变化UI未更新


        上篇文章中我们介绍了LazyForEach的基本使用,展示了如何使用LazyForEach构造一个列表,并演示数据的添加、删除、修改如何与LazyForEach配合并正确的更新UI。本篇将介绍使用LazyForEach的时候会遇到的一些常见问题。

1、渲染结果非预期

        代码如下:

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%')
  }
}

        运行结果如下:

LazyForEach常见使用问题_第1张图片

        

        当我们多次点击子组件时,会发现删除的并不一定是我们点击的那个子组件。原因是当我们删除了某一个子组件后,位于该子组件对应的数据项之后的各数据项,其index均应减1,但实际上后续的数据项对应的子组件仍然使用的是最初分配的index,其itemGenerator中的index并没有发生变化,所以删除结果和预期不符。如何修复呢?把注释的那行代码打开即可(在删除一个数据项后调用reloadData方法,重建后面的数据项,以达到更新index索引的目的)。

2、重新渲染时图片闪烁

        如下代码只更新文本时,会引起图片的更新,看上去发生了闪烁:

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 })
  }
}

        修复后运行效果如下:

只更新文本,图片不闪烁

3、@ObjectLink属性变化UI未更新

import { NestedString, StringDataV2 } from './base/LazyData';
import { MyDataSource } from './base/LazyForeach';

@Entry
@Component
struct LazyRenderDemo5Page {
  @State data: MyDataSource = new MyDataSource();

  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能监听到该层的变化)。

你可能感兴趣的:(鸿蒙4.0开发,ArkTS,UI,鸿蒙4.0,harmonyos,ForEach循环渲染)