ForEach刷新UI机制

官网地址:ForEach

在ArkUI中,提供了ForEach循环语句,用来初始化一个列表数据,我们知道,当ForEach中的数组发生变化时,会引起UI的刷新,但是究竟如何变化,会引起UI怎样的刷新,还需要进一步的去探索其逻辑。

1. ForEach参数和规则

参数:

参数名 类型 必填 说明
arr Array 数据源,为Array类型的数组。说明:- 可以设置为空数组,此时不会创建子组件。- 可以设置返回值为数组类型的函数,例如arr.slice(1, 3),但设置的函数不应改变包括数组本身在内的任何状态变量,例如不应使用Array.splice(),Array.sort()或Array.reverse()这些会改变原数组的函数。
itemGenerator (item: Object, index: number) => void 组件生成函数。- 为数组中的每个元素创建对应的组件。- item参数:arr数组中的数据项。- index参数(可选):arr数组中的数据项索引。说明:- 组件的类型必须是ForEach的父容器所允许的。例如,ListItem组件要求ForEach的父容器组件必须为List组件。
keyGenerator (item: Object, index: number) => string 键值生成函数。- 为数据源arr的每个数组项生成唯一且持久的键值。函数返回值为开发者自定义的键值生成规则。- item参数:arr数组中的数据项。- index参数(可选):arr数组中的数据项索引。说明:- 如果函数缺省,框架默认的键值生成函数为(item: T, index: number) => { return index + '__' + JSON.stringify(item); }- 键值生成函数不应改变任何组件状态。

键值的生成规则和itemGenerator 、keyGenerator有关:

1.如果是keyGenerator这个函数缺省,此时生成规则由框架确定,生成规则为item和index拼接,(item: any, index: number)=>{ return index +"_"+ JSON.stringify(item); }。

2.如果keyGenerator没有缺省且未包含index,当itemGenerator中包含index,生成的规则是自定义键值与index拼接成的字符串,如(item)=>item+2 对应的键值是 index+'_'+(item+2),如果itemGenerator中未包含index,此时keyGenerator的生成规则是由开发者自定义的键值生成规则。

3.如果keyGenerator没有缺省,且包含index,此时不管itemGenerator中是否包含index,生成的键值规则都是开发者自定义的键值生成规格,框架不会对去拼接index。

2. 场景和示例

ForEach在初始化时,会加载数组中所有的数据,并为其创建控件,也就是厨师数组如果有n项,就会创建n个对象

typescript 复制代码
@Component
export struct ForEachView {
  @State private list: number[] = [1, 2, 3]
  private list1: number[] = [1, 2]
  private list2: number[] = [1, 2, 3, 4, 5]
  private isOne: boolean = true;

  build() {
    Column({ space: 20 }) {
      Column({ space: 10 }) {
        ForEach(this.list, (item: number) => {
          Child({ item: item })
        }, (item: number, index: number) => {
          console.debug(`ForEach: item = ${item}, index = ${index}`)
          return item.toString()
        })
      }
      Row({ space: 10 }) {
        Button("add")
          .onClick(() => {
            this.list.push(4)
          })
        Button("insert")
          .onClick(() => {
            this.list.splice(1, 0, 5)
          })
        Button("delete")
          .onClick(() => {
            this.list.pop();
          })
        Button("changeArray")
          .onClick(() => {
            if (this.isOne) {
              this.list = this.list1
            } else {
              this.list = this.list2
            }
            this.isOne = !this.isOne;
          })
      }
    }

  }
}

@Component
struct Child {
  @Prop item: number;

  aboutToAppear(): void {
    console.debug(`aboutToAppear: child${this.item}`)
  }

  build() {
    Text(this.item.toString())
      .width(100)
      .height(50)
      .border({ width: 2, color: Color.Red })
  }

  aboutToDisappear(): void {
    console.debug(`aboutToDisappear: child${this.item}`)
  }
  
}

2.1. 场景一:初始化

运行结果:

初始化时,list中有三个元素,ForEach遍历所有元素,并创建了三个Child控件

2.2. 场景二:数组增加元素

运行结果:

可以看出,增加了Item 4后,ForEach循环还是遍历的四遍,但是此时只有child4执行了aboutToAppear的方法。这是因为ForEach的keyGenerator设置唯一的key值,ForEach刷新时,发现如果相同的key值的控件如果已经存在,就不会重新创建。

2.3. 场景三:数组插入元素

运行结果:

插入和增加一样,ForEach也会全部遍历,但是只创建了不存在的child5

2.4. 场景四:删除

运行结果:

ForEach遍历剩余的元素,并将删除的元素下树,调用child3的aboutToDisapper的方法

2.5. 场景五:更换数组对象

  1. 将数组list1赋值list, 元素数量减少

运行结果:

由于list1和list之间有部分项的重合,所以,list1赋值给list后,重复的部分之前已经存在,不创建,list1没有的部分则进行了删除

  1. 将list2赋值给list,元素数量增加

运行结果:

ForEach对所有元素遍历,对不存在的元素进行创建

  1. 赋值完全不同的list3

运行结果:

ForEach遍历数组所有元素,将原来的元素全部移除,然后创建新的元素。

3. 总结

对于ForEach而言,只要数组发生了变化, 无论是长度变化还是重新赋值, ForEach都回重新遍历一遍。但是ForEach会根据keyGenerator的值判断是否需要重新创建控件,所以keyGenerator是非常关键的参数,建议自己定义一个生成规则,不要用系统默认的。

相关推荐
千里马学框架18 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台18 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone19 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc19 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo21 小时前
Kotlin 协程中的 Job 结构化并发与取消
android
sun00770021 小时前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui