前言
鸿蒙中的通过ForEach循环渲染会一次性创建出所有子组件,即便不在可视区域外,而LazyForEach则可以进行按需迭代数据,当组件滑出可视区域外时,框架会进行组件销毁回收以降低内存占用。而LazyForEach
需要开发者实现IDataSource
数据源相关接口。
封装通用数据源类
我们可以封装一个类BasicDataSource
,遵循IDataSource
并实现相关接口,同时我们可以提供相关的数据操作方法,以便调用方在操作数据后可以通知组件进行数据重新渲染。
以下为BasicDataSource
类的实现:
/**
* LazyForEach 基础数据源
*/
export class BasicDataSource<T> implements IDataSource {
// 监听器
private listeners: DataChangeListener[] = [];
// 数据数组
private dataArray: T[]
constructor(dataArray: T[] = []) {
this.dataArray = dataArray
}
// MARK: - IDataSource
// 总条数
totalCount(): number {
return this.dataArray.length
}
// 获取某条数据
getData(index: number): T {
return this.dataArray[index]
}
// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.includes(listener)) {
return
}
this.listeners.push(listener)
}
// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener)
if (pos >= 0) {
this.listeners.splice(pos, 1)
}
}
// MARK: - Public Methods
/**
* 刷新数据
*/
public reloadData(): void {
this.notifyDataReloaded()
}
/**
* 查:获取所有数据
* @returns 数据数组
*/
public getAllArray(): T[] {
return this.dataArray
}
/**
* 增:添加单条数据
* @param data 单条数据
*/
public addData(data: T): void {
this.dataArray.push(data)
this.notifyDataAdd(this.dataArray.length - 1);
}
/**
* 增:添加多条数据
* @param dataArray 多条数据
*/
public addDataArray(dataArray: T[]): void {
dataArray.forEach((data) => {
this.dataArray.push(data)
})
this.notifyDataAdd(this.dataArray.length - 1);
}
/**
* 增:在某个位置插入数据
* @param data 要插入的数据
* @param index 插入位置
*/
public insertData(data: T, index: number) {
this.dataArray.splice(index, 0, data)
this.notifyDataAdd(index);
}
/**
* 改:更新某索引下的数据
* @param data 数据
* @param index 索引
*/
public updateData(data: T, index: number): void {
this.dataArray.splice(index, 1, data)
this.notifyDataChange(index)
}
/**
* 改:交换数据位置
* @param from 起始位置
* @param to 目标位置
*/
public exchangeData(from: number, to: number): void {
let fromData = this.dataArray[from]
let toData = this.dataArray[to]
this.dataArray[from] = toData
this.dataArray[to] = fromData
this.notifyDataMove(from, to)
}
/**
* 改:重置数据
* @param dataArray 新的数据数组
*/
public resetData(dataArray: T[]): void {
this.dataArray = dataArray
this.notifyDataReloaded()
}
/**
* 删:删除指定索引的数据
* @param index 索引
*/
public deleteData(index: number): void {
this.dataArray.splice(index, 1)
this.notifyDataDelete(index)
}
// MARK: - Private Methods
/**
* 通知组件重新加载所有数据
*/
private notifyDataReloaded(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
})
}
/**
* 通知组件index的位置有数据添加
* @param index 当前传入的索引值
*/
private notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
})
}
/**
* 通知组件数据有移动。将from和to位置的数据进行交换
* @param from
* @param to
*/
private notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to) // 系统 api 命名不严谨
})
}
/**
* 通知组件删除index位置的数据并刷新LazyForEach的展示内容
*/
private notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index)
})
}
/**
* 通知组件index的位置有数据有变化
*/
private notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
})
}
}
使用示例
// 复用组件
@Reusable
@Component
export struct CardView {
// 被\@State修饰的变量item才能更新,未被\@State修饰的变量不会更新。
@State item: string = '';
aboutToReuse(params: Record<string, Object>): void {
this.item = params.item as string;
console.info(`>>>>> aboutToReuse: ${this.item}`)
}
aboutToAppear(): void {
console.info(`>>>>> aboutToAppear: ${this.item}`)
}
build() {
Column() {
Text(this.item)
.fontSize(30)
}
.width('100%')
.borderWidth(1)
.height(100)
}
}
import { BasicDataSource } from '../classes/BasicDataSource';
import { CardView } from '../components/CardView';
@Entry
@Component
struct Index {
private dataSource: BasicDataSource<string> = new BasicDataSource<string>()
private deleteIndex: string = ''
aboutToAppear() {
this.reset()
}
build() {
Column() {
this.TopView()
List() {
LazyForEach(this.dataSource, (item: string) => {
ListItem() {
CardView({ item: item })
}
}, (item: string) => item)
}
.width('100%')
.layoutWeight(1)
.borderWidth(1)
.borderColor(Color.Red)
}
}
@Builder
TopView() {
Column() {
Row({ space: 5 }) {
Button('添加').onClick((event: ClickEvent) => {
let date = new Date()
this.dataSource.addData(date.toString())
})
Button('更新').onClick((event: ClickEvent) => {
let date = new Date()
this.dataSource.updateData(`更新:${date.toString()}`, 0)
})
Button('交换 1-3').onClick((event: ClickEvent) => {
this.dataSource.exchangeData(1, 3)
})
}
Row({ space: 5 }) {
Button('重置').onClick((event: ClickEvent) => {
this.reset()
})
Button('刷新').onClick((event: ClickEvent) => {
this.dataSource.reloadData()
})
Button('查询').onClick((event: ClickEvent) => {
let count = this.dataSource.totalCount()
console.info(`>>>>> 总共${count}条数据`)
})
}
Row({ space: 5 }) {
TextInput({
placeholder: '输入删除数据下标'
})
.type(InputType.Number)
.onChange((text) => {
this.deleteIndex = text
})
Button('删除').onClick((event: ClickEvent) => {
let index = Number(this.deleteIndex)
if (index >= 0 && index < this.dataSource.totalCount()) {
this.dataSource.deleteData(index)
}
})
}
.width(200)
}
}
private reset() {
let dataArray: string[] = []
for (let i = 0; i < 100; i++) {
dataArray.push('原始' + i)
}
this.dataSource.resetData(dataArray)
}
}