华为仓颉鸿蒙HarmonyOS NEXT仓颉List性能优化LazyForEach(懒加载)

204 阅读5分钟

LazyForEach从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach,框架会根据滚动容器可视区域按需创建组件,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。

👇🏻👇🏻👇🏻求关注👇🏻👇🏻👇🏻

public class DataChangeListener <: RemoteData {
        func onDataReloaded(): Unit
        func onDataAdd(index: Int64): Unit
        func onDataDelete(index: Int64): Unit
        func onDataChange(index: Int64): Unit
        func onDataMove(fromIdx: Int64, toIdx: Int64): Unit
    }

构造函数

init(IDataSource, (T, Int64) -> Unit,(T, Int64) -> String)

public init(dataSource: IDataSource<T>, itemGeneratorFunc!: (T, Int64) -> Unit, keyGeneratorFunc!: (T, Int64) -> String)

构建一个LazyForEach对象。

参数名参数类型必填默认值描述
dataSourceIDataSource-LazyForEach数据源,需要开发者实现相关接口。
itemGeneratorFunc(T, Int64) -> Unit-子组件生成函数,为数组中的每一个数据项创建一个子组件。lambda函数的第一个泛型参数为数据类型,必须为FFIData的子类;第二个参数为当前列表项的索引值。
keyGeneratorFunc(T, Int64) -> String-匿名函数,用于键值生成,为给定数组项生成唯一且稳定的键值。当子项在数组中的位置更改时,子项的键值不得更改,当数组中的子项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。

自定义类型说明

IDataSource

public interface IDataSource<T> {
        func totalCount(): Int64
        func getData(index: Int64): T
        func onRegisterDataChangeListener(listener: DataChangeListener): Unit
        func onUnregisterDataChangeListener(listener: DataChangeListener): Unit
    }

函数

totalCount()

public func totalCount(): Int64

获得数据总数。

getData(Int64)

public func getData(index: Int64): T

获取索引值index对应的数据。

参数名参数类型必填默认值描述
indexInt64-对应的索引值。

onRegisterDataChangeListener(DataChangeListener)

public func onRegisterDataChangeListener(listener: DataChangeListener): Unit

注册数据改变的监听器

参数名参数类型必填默认值描述
listenerDataChangeListener-数据变化监听器。

onUnregisterDataChangeListener(DataChangeListener)

public func onUnregisterDataChangeListener(listener: DataChangeListener): Unit

注销数据改变的监听器。

参数名参数类型必填默认值描述
listenerDataChangeListener-数据变化监听器。

DataChangeListener

public class DataChangeListener {
        public init(id: Int64)
        public func onDataReloaded(): Unit
        public func onDataAdd(index: Int64): Unit
        public func onDataDelete(index: Int64): Unit
        public func onDataChange(index: Int64): Unit
        public func onDataMove(fromIdx: Int64, toIdx: Int64): Unit
    }

数据变化监听器类型。

构造函数

init(Int64)

public init(id: Int64)

创建一个DataChangeListener类型的对象。

函数

onDataReloaded()

public func onDataReloaded(): Unit

通知组件重新加载所有数据。键值没有变化的数据项会使用原先的子组件,键值发生变化的会重建子组件。

onDataAdd(Int64)

public func onDataAdd(index: Int64): Unit

通知组件index的位置有数据添加。

参数名参数类型必填默认值描述
indexInt64-数据添加位置的索引值。

onDataDelete(Int64)

public func onDataDelete(index: Int64): Unit

通知组件删除index位置的数据并刷新LazyForEach的展示内容。

参数名参数类型必填默认值描述
indexInt64-数据删除位置的索引值。

onDataChange(Int64)

public func onDataChange(index: Int64): Unit

通知组件index的位置有数据有变化。

参数名参数类型必填默认值描述
indexInt64-数据变化位置的索引值。

onDataMove(Int64, Int64)

public func onDataMove(fromIdx: Int64, toIdx: Int64): Unit

通知组件数据有移动。

参数名参数类型必填默认值描述
fromIdxInt64-数据移动起始位置。
toIdxInt64-数据移动目标位置。

使用限制

  • ​ 数据懒加载必须在容器组件内使用,且仅有List、Grid以及Swiper组件支持数据的懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次加载所有的数据。
  • ​ LazyForEach在每次迭代中,必须且只允许创建一个子组件。
  • ​ 生成的子组件必须允许在LazyForEach的父容器组件中。
  • ​ 允许LazyForEach包含在if/else条件渲染语句中,也允许LazyForEach中出现if/else条件渲染语句。
  • ​ 键值生成器必须针对每个数据生成唯一的值,如果键值相同,将导致键值相同的UI组件渲染出现问题。
  • ​ LazyForEach必须使用DataChangeListener对象来进行更新,第一个参数dataSource使用状态变量时,状态变量改变不会触发LazyForEach的UI刷新。
  • ​ 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,需要生成不同于原来的键值来触发组件刷新。
  • ​ List使用LazyForEach加载子组件时,没有设置List的宽高,会加载所有子组件,设置了List的宽高,会加载List显示区域内的子组件。

示例代码

import ohos.base.*
    import ohos.component.*
    import ohos.state_manage.*
    import ohos.state_macro_manage.*
    import std.collection.*
    
    public class Student {
        public Student(
            let name: String,
            let id: Int64
        ) {}
    }
    
    class StudentDataSource <: IDataSource<Student> {
        public StudentDataSource(let data_: ArrayList<Student>) {}
        public var listenerOp: Option<DataChangeListener> = None
        public func totalCount(): Int64 {
            return data_.size
        }
        public func getData(index: Int64): Student {
            return data_[index]
        }
    
        public func onRegisterDataChangeListener(listener: DataChangeListener): Unit {
            listenerOp = listener
        }
    
        public func onUnregisterDataChangeListener(listener: DataChangeListener): Unit {
            listenerOp = None
        }
    
        public func notifyChange(): Unit {
            let listener: DataChangeListener = listenerOp.getOrThrow()
            listener.onDataReloaded()
        }
    }
    
    func getDS(): StudentDataSource
    {
        let data: ArrayList<Student> = ArrayList<Student>()
        for (i in 0..50) {
            data.append(Student("name ${i}", i * i))
        }
        let dataSourceStu: StudentDataSource = StudentDataSource(data)
        return dataSourceStu
    }
    
    let dataSourceStu: StudentDataSource = getDS()
    var changeID: Int64 = 0
    
    @Entry
    @Component
    public class MyView {
    
        public func build(): Unit {
            Column(30) {
                Text("-----------------")
                Column {
                    LazyForEach(dataSourceStu, itemGeneratorFunc: {stu: Student, idx: Int64 =>
                        Column {
                            Text(stu.name)
                        }
                    })
                }
                .height(400.0)
    
                Text("click to notifyChange").onClick({ evt =>
                    dataSourceStu.data_.remove(changeID)
                    dataSourceStu.data_.insert(changeID, Student("xiaoming", 10086))
                    dataSourceStu.notifyChange()
                    changeID += 1
                })
            }
        }
    }

如对您有帮助,帮忙点个“在看 、关注” 让更多的人受益~!

技术交流群可加wx“LB-9191”备注cangjie