HarmonyOS鸿蒙Next中懒加载插入数据
HarmonyOS鸿蒙Next中懒加载插入数据 arkTs中,如果使用懒加载我如何批量插入数据,然后不造成全量刷新呢?
开发者您好,
关于您提到的“懒加载”,是否指的是 lazyForEach 的使用?另外,针对批量插入场景,是否可以参考以下本地测试的实现方式?
若遇到长列表数据刷新时出现闪烁问题,建议参考:长列表数据刷新闪烁-行业常见问题-综合办公类行业实践-场景化知识 - 华为HarmonyOS开发者
测试代码:
// BasicDataSource实现了IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新
export class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = [];
private originDataArray: StringData[] = [];
public totalCount(): number {
return this.originDataArray.length;
}
public getData(index: number): StringData {
return this.originDataArray[index];
}
// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
this.listeners.push(listener);
}
}
// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
this.listeners.splice(pos, 1);
}
}
// 通知LazyForEach组件需要重载所有子组件
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded();
});
}
// 通知LazyForEach组件需要在index对应索引处添加子组件
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index);
// 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]);
});
}
// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index);
// 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]);
});
}
// 通知LazyForEach组件需要在index对应索引处删除该子组件
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index);
// 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]);
});
}
// 通知LazyForEach组件将from索引和to索引处的子组件进行交换
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to);
// 写法2:listener.onDatasetChange(
// [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]);
});
}
notifyDatasetChange(operations: DataOperation[]): void {
this.listeners.forEach(listener => {
listener.onDatasetChange(operations);
});
}
}
class StringData {
message: string;
imgSrc: Resource;
constructor(message: string, imgSrc: Resource) {
this.message = message;
this.imgSrc = imgSrc;
}
}
class InitialDataSource extends BasicDataSource {
private dataArray: StringData[] = [];
public totalCount(): number {
return this.dataArray.length;
}
public getData(index: number): StringData {
return this.dataArray[index];
}
public pushData(data: StringData): void {
this.dataArray.push(data);
this.notifyDataAdd(this.dataArray.length - 1);
}
}
@Entry
@Component
struct InitialRendering {
private data: InitialDataSource = new InitialDataSource();
aboutToAppear() {
for (let i = 0; i <= 5; i++) {
this.data.pushData(new StringData(`Click to add ${i}`, $r('app.media.xxx')));
}
}
build() {
Column() {
Button('pushDataBatch').onClick((event: ClickEvent) => {
for (let i = 0; i <= 1; i++) {
this.data.pushData(new StringData(`pushDataBatch ${i}`, $r('app.media.xxx')));
}
});
List({ space: 3 }) {
LazyForEach(this.data, (item: StringData) => {
ListItem() {
Row() {
Text(item.message).fontSize(20)
.onAppear(() => {
console.info('text appear:' + item.message);
});
Image(item.imgSrc)
.width(100)
.height(100);
}.margin({ left: 10, right: 10 });
}
})
};
};
}
}
更多关于HarmonyOS鸿蒙Next中懒加载插入数据的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
@Component
struct StockBondLinkage {
@State currentIndex: number = 0
private listScroller: Scroller = new Scroller()
private bondChartSettings: RenderingContextSettings = new RenderingContextSettings(true)
private bondChartContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.bondChartSettings)
private stockChartSettings: RenderingContextSettings = new RenderingContextSettings(true)
private stockChartContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.stockChartSettings)
// 模拟数据
@State stockBondList: StockBondItem[] = []
aboutToAppear(): void {
this.stockBondList = stockBondList
}
build() {
Column() {
// 顶部导航栏
this.NavigationBar()
// 今日涨幅TOP标签栏
this.TopFilterBar()
// 主要展示卡片(股债对比)
this.MainCompareCard()
// 列表表头
this.ListHeader()
// 股债联动列表
this.BondList()
}
.width('100%')
.height('100%')
.backgroundColor('#1A1A1A')
}
// 顶部导航栏
@Builder
NavigationBar() {
Row() {
Text('<')
.fontSize(22)
.fontColor('#FFFFFF')
.width(40)
Text('股债联动')
.fontSize(18)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Medium)
.layoutWeight(1)
.textAlign(TextAlign.Center)
Row() {
Text('↻')
.fontSize(20)
.fontColor('#FFFFFF')
.margin({ right: 20 })
Text('🔍')
.fontSize(18)
.fontColor('#FFFFFF')
}
.width(60)
.justifyContent(FlexAlign.End)
}
.width('100%')
.height(44)
.padding({ left: 12, right: 12 })
.backgroundColor('#0D0D0D')
}
// 今日涨幅TOP标签栏
@Builder
TopFilterBar() {
Row() {
Text('今日涨幅 TOP')
.fontSize(13)
.fontColor('#888888')
Text('0.11%')
.fontSize(13)
.fontColor('#00AA00')
.margin({ left: 8 })
Text('专用设备')
.fontSize(11)
.fontColor('#FF4444')
.border({ width: 1, color: '#FF4444', radius: 2 })
.padding({
left: 6,
right: 6,
top: 2,
bottom: 2
})
.margin({ left: 10 })
Blank()
Text('最相关')
.fontSize(13)
.fontColor('#FFFFFF')
}
.width('100%')
.padding({
left: 12,
right: 12,
top: 10,
bottom: 10
})
.backgroundColor('#1A1A1A')
}
// 主要展示卡片
@Builder
MainCompareCard() {
Row() {
// 左侧转债信息
Column() {
// 标题行:名称和价格
Row() {
Text('耐普转02')
.fontSize(14)
.fontColor('#FF4444')
Text('157.300')
.fontSize(15)
.fontColor('#FF4444')
.fontWeight(FontWeight.Bold)
.margin({ left: 6 })
}
.width('100%')
.justifyContent(FlexAlign.Start)
// 代码和涨幅
Row() {
Text('123265')
.fontSize(11)
.fontColor('#666666')
Text('+57.30%')
.fontSize(11)
.fontColor('#FF4444')
.margin({ left: 6 })
}
.width('100%')
.margin({ top: 2 })
// 走势图区域(带Y轴标签)
Row() {
// 左侧Y轴
Column() {
Text('157.300')
.fontSize(9)
.fontColor('#FF4444')
Blank()
Text('100.000')
.fontSize(9)
.fontColor('#666666')
Blank()
Text('42.700')
.fontSize(9)
.fontColor('#00AA00')
}
.height(75)
.width(45)
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(HorizontalAlign.Start)
// 走势图
Canvas(this.bondChartContext)
.width(80)
.height(75)
.backgroundColor('#1A1A1A')
.onReady(() => {
this.drawBondChart()
})
// 右侧Y轴
Column() {
Text('57.3%')
.fontSize(9)
.fontColor('#FF4444')
Blank()
Text('-57.3%')
.fontSize(9)
.fontColor('#00AA00')
}
.height(75)
.width(35)
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(HorizontalAlign.End)
}
.width('100%')
.margin({ top: 6 })
}
.layoutWeight(1)
.padding({
left: 8,
right: 4,
top: 8,
bottom: 8
})
// 分隔线
Divider()
.vertical(true)
.height('80%')
.color('#333333')
.strokeWidth(0.5)
// 右侧正股信息
Column() {
// 标题行:名称和价格
Row() {
Text('耐普矿机')
.fontSize(14)
.fontColor('#FFFFFF')
Text('49.58')
.fontSize(15)
.fontColor('#00AA00')
.fontWeight(FontWeight.Bold)
.margin({ left: 6 })
}
.width('100%')
.justifyContent(FlexAlign.Start)
// 代码和跌幅
Row() {
Text('300818')
.fontSize(11)
.fontColor('#666666')
Text('-9.11%')
.fontSize(11)
.fontColor('#00AA00')
.margin({ left: 6 })
}
.width('100%')
.margin({ top: 2 })
// 走势图区域(带Y轴标签)
Row() {
// 左侧Y轴
Column() {
Text('62.29')
.fontSize(9)
.fontColor('#FF4444')
Blank()
Text('54.55')
.fontSize(9)
.fontColor('#666666')
Blank()
Text('46.81')
.fontSize(9)
.fontColor('#00AA00')
}
.height(75)
.width(35)
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(HorizontalAlign.Start)
// 走势图
Canvas(this.stockChartContext)
.width(90)
.height(75)
.backgroundColor('#1A1A1A')
.onReady(() => {
this.drawStockChart()
})
// 右侧Y轴
Column() {
Text('14.2%')
.fontSize(9)
.fontColor('#FF4444')
Blank()
Text('-14.2%')
.fontSize(9)
.fontColor('#00AA00')
}
.height(75)
.width(32)
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(HorizontalAlign.End)
}
.width('100%')
.margin({ top: 6 })
}
.layoutWeight(1)
.padding({
left: 4,
right: 8,
top: 8,
bottom: 8
})
}
.width('100%')
.height(200)
.backgroundColor('#1A1A1A')
.margin({ top: 1 })
}
// 绘制转债走势图(红色填充上涨)
drawBondChart() {
let ctx = this.bondChartContext
let w = 80
let h = 75
ctx.clearRect(0, 0, w, h)
ctx.beginPath()
ctx.moveTo(0, h)
ctx.lineTo(0, h * 0.9)
ctx.lineTo(w * 0.3, h * 0.85)
ctx.lineTo(w * 0.5, h * 0.8)
ctx.lineTo(w * 0.7, h * 0.3)
ctx.lineTo(w * 0.85, h * 0.1)
ctx.lineTo(w, h * 0.05)
ctx.lineTo(w, h)
ctx.closePath()
ctx.fillStyle = '#FF4444'
ctx.globalAlpha = 0.6
ctx.fill()
ctx.globalAlpha = 1.0
}
// 绘制正股走势图(白色线+黄色均线)
drawStockChart() {
let ctx = this.stockChartContext
let w = 90
let h = 75
let midY = h * 0.5
ctx.clearRect(0, 0, w, h)
ctx.beginPath()
ctx.moveTo(0, midY - 5)
ctx.lineTo(w * 0.1, midY - 8)
ctx.lineTo(w * 0.2, midY - 3)
ctx.lineTo(w * 0.3, midY + 2)
ctx.lineTo(w * 0.4, midY - 5)
ctx.lineTo(w * 0.5, midY + 8)
ctx.lineTo(w * 0.6, midY + 5)
ctx.lineTo(w * 0.7, midY + 12)
ctx.lineTo(w * 0.8, midY + 8)
ctx.lineTo(w * 0.9, midY + 15)
ctx.lineTo(w, midY + 18)
ctx.strokeStyle = '#FFFFFF'
ctx.lineWidth = 1
ctx.stroke()
ctx.beginPath()
ctx.moveTo(0, midY)
ctx.lineTo(w * 0.2, midY + 2)
ctx.lineTo(w * 0.4, midY + 5)
ctx.lineTo(w * 0.6, midY + 8)
ctx.lineTo(w * 0.8, midY + 12)
ctx.lineTo(w, midY + 15)
ctx.strokeStyle = '#FFAA00'
ctx.lineWidth = 1
ctx.stroke()
}
// 列表表头
@Builder
ListHeader() {
Row() {
Text('股债联动')
.fontSize(12)
.fontColor('#666666')
.layoutWeight(1)
Text('转债报价▼')
.fontSize(12)
.fontColor('#666666')
.layoutWeight(1)
.textAlign(TextAlign.End)
Text('正股报价▼')
.fontSize(12)
.fontColor('#666666')
.layoutWeight(1)
.textAlign(TextAlign.End)
Text('溢价率▼')
.fontSize(12)
.fontColor('#666666')
.layoutWeight(1)
.textAlign(TextAlign.End)
Row() {
Text('量比')
.fontSize(12)
.fontColor('#666666')
Text('▶')
.fontSize(10)
.fontColor('#666666')
.margin({ left: 4 })
}
.layoutWeight(1)
.justifyContent(FlexAlign.End)
}
.width('100%')
.padding({
left: 12,
right: 12,
top: 10,
bottom: 8
})
.backgroundColor('#1A1A1A')
}
// 股债联动列表
@Builder
BondList() {
List({ scroller: this.listScroller }) {
ForEach(this.stockBondList, (item: StockBondItem) => {
ListItem() {
this.BondListItem(item)
}
}, (item: StockBondItem) => item.bondCode)
}
.width('100%')
.layoutWeight(1)
.backgroundColor('#1A1A1A')
.scrollBar(BarState.Off)
}
// 列表项
@Builder
BondListItem(item: StockBondItem) {
Row() {
// 股债名称
Column() {
Text(item.bondName)
.fontSize(14)
.fontColor('#FFFFFF')
Text(item.bondCode)
.fontSize(10)
.fontColor('#666666')
.margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
// 转债报价
Column() {
Text(item.bondPrice.toFixed(3))
.fontSize(13)
.fontColor('#FF4444')
Text((item.bondChange >= 0 ? '+' : '') + item.bondChange.toFixed(2) + '%')
.fontSize(10)
.fontColor('#FF4444')
.margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.End)
// 正股报价
Column() {
Text(item.stockPrice > 0 ? item.stockPrice.toFixed(2) : '--')
.fontSize(13)
.fontColor(item.stockChange >= 0 ? '#FF4444' : '#00AA00')
Text(item.stockChange !== 0 ? ((item.stockChange >= 0 ? '+' : '') + item.stockChange.toFixed(2) + '%') : '--')
.fontSize(10)
.fontColor(item.stockChange >= 0 ? '#FF4444' : '#00AA00')
.margin({ top: 2 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.End)
// 溢价率
Text(item.premiumRate.toFixed(2) + '%')
.fontSize(13)
.fontColor(item.premiumRate >= 0 ? '#FF4444' : '#00AA00')
.layoutWeight(1)
.textAlign(TextAlign.End)
// 量比
Text(item.volumeRatio > 0 ? item.volumeRatio.toFixed(2) : '--')
.fontSize(13)
.fontColor('#FFFFFF')
.layoutWeight(1)
.textAlign(TextAlign.End)
}
.width('100%')
.padding({
left: 12,
right: 12,
top: 10,
bottom: 10
})
.backgroundColor('#1A1A1A')
}
}
// 股债联动数据模型
interface StockBondItem {
bondName: string
bondCode: string
bondPrice: number
bondChange: number
stockPrice: number
stockChange: number
premiumRate: number
volumeRatio: number
}
const stockBondList: StockBondItem[] = [
{
bondName: '耐普转02',
bondCode: '123265',
bondPrice: 157.300,
bondChange: 57.30,
stockPrice: 49.58,
stockChange: -9.11,
premiumRate: 21.96,
volumeRatio: 0
},
{
bondName: '汇车退债',
bondCode: '404004',
bondPrice: 57.780,
bondChange: 43.85,
stockPrice: 0.07,
stockChange: 0,
premiumRate: -42.22,
volumeRatio: 0.61
},
{
bondName: '联瑞转债',
bondCode: '118064',
bondPrice: 226.512,
bondChange: 20.00,
stockPrice: 66.79,
stockChange: 2.60,
premiumRate: 115.53,
volumeRatio: 60.16
},
{
bondName: '富淼转债',
bondCode: '118029',
bondPrice: 142.608,
bondChange: 11.03,
stockPrice: 27.42,
stockChange: 14.11,
premiumRate: -3.26,
volumeRatio: 11.63
},
{
bondName: '百川转2',
bondCode: '127075',
bondPrice: 149.000,
bondChange: 7.58,
stockPrice: 9.59,
stockChange: 9.98,
premiumRate: 16.99,
volumeRatio: 5.71
},
{
bondName: '广联转债',
bondCode: '123182',
bondPrice: 166.900,
bondChange: 4.81,
stockPrice: 38.41,
stockChange: 4.09,
premiumRate: -0.75,
volumeRatio: 1.88
},
{
bondName: '亿田转债',
bondCode: '123235',
bondPrice: 183.300,
bondChange: 3.65,
stockPrice: 33.76,
stockChange: 3.24,
premiumRate: 15.70,
volumeRatio: 1.86
},
{
bondName: '宇邦转债',
bondCode: '123224',
bondPrice:在HarmonyOS Next中,懒加载插入数据通常指使用LazyForEach组件配合数据源管理。数据源需继承BaseLazyDataSource并实现onDataReloaded、totalCount、getData方法。当列表项即将进入可视区域时,LazyForEach会调用getData加载数据。插入新数据时,需在数据源中更新数据集合,并调用notifyDataReloaded或notifyDataAdd方法触发UI刷新。整个过程由ArkUI框架管理按需加载,避免一次性渲染大量数据。
在HarmonyOS Next的ArkTS中,使用懒加载(例如通过LazyForEach)进行批量数据插入且避免全量刷新,核心在于只更新数据源(@State/@Link修饰的数组),并利用LazyForEach的组件复用机制。以下是具体方法:
关键步骤
- 数据源管理:使用
[@State](/user/State)或[@Link](/user/Link)修饰的数组(如Array<YourDataType>)作为LazyForEach的数据源。 - 批量插入数据:直接对数据源数组进行批量操作(例如
push、splice或解构赋值),ArkUI框架会自动触发差异更新。 - 避免全量刷新:
LazyForEach会根据数据源的变化,仅创建、复用或移除对应的子组件,不会重新渲染整个列表。
代码示例
// 1. 定义数据源
[@State](/user/State) dataArray: Array<YourDataType> = []; // 初始数据
// 2. 批量插入数据(例如追加新数据)
private insertBatchData(newData: Array<YourDataType>) {
// 使用数组解构或concat生成新数组,触发状态更新
this.dataArray = this.dataArray.concat(newData); // 或 [...this.dataArray, ...newData]
}
// 3. 在UI中使用LazyForEach
LazyForEach(
this.dataArray, // 数据源
(item: YourDataType) => item.id, // 键值生成器
(item: YourDataType) => {
// 子组件构建函数
YourListItemComponent({ item: item })
}
)
注意事项
- 数据标识:确保每个数据项有唯一键值(如
id),以便LazyForEach正确复用组件。 - 不可变数据:更新数据源时需生成新数组(如用
concat或解构),直接修改原数组(如this.dataArray.push(...))可能不会触发UI更新。 - 性能优化:对于超大数据集,建议分页加载,避免一次性插入过多数据导致内存压力。
通过以上方式,批量插入数据时只有新增项对应的组件会被渲染,现有组件不会刷新,从而实现高效更新。


