写点什么

鸿蒙 HarmonyOS 实战 -ArkTS 语言(渲染控制)

作者:蜀道山
  • 2024-04-15
    湖南
  • 本文字数:19538 字

    阅读完需:约 64 分钟

鸿蒙HarmonyOS实战-ArkTS语言(渲染控制)

🚀前言

编程语言中都有自己基本的控制结构,它们在程序设计中起到了非常重要的作用。以下是几个原因:


分支控制: 在程序执行过程中,有时需要根据不同的条件分支来执行不同的代码逻辑。if/else 结构通过判断条件来决定程序如何执行,实现了程序的分支控制。


数据迭代: 在程序中,需要对一些数据进行遍历、操作或者计算。ForEach 和 LazyForEach 提供了一种便捷的方法,可以针对数据集合进行遍历,并对其中的每个元素执行特定操作。


惰性计算:在某些情况下,程序中需要对大量的数据进行遍历或计算。LazyForEach 可以实现惰性计算,只在需要时才计算相应的结果,避免了程序在运行时不必要的计算,从而提高程序效率。

🚀一、ArkTS 语言渲染控制


ArkTS 是一种基于 TypeScript 的编程语言,支持对渲染过程进行控制和调整。以下是 ArkTS 语言中实现渲染控制的三个关键组件:


  1. if/else:if/else 结构可以用于根据条件控制是否渲染某些元素。例如,可以通过 if/else 语句来实现某些物体在特定场景或条件下的显示或隐藏。

  2. ForEach:ForEach 是一个迭代方法,能够遍历数组、对象等数据结构中的元素,并在渲染过程中对它们进行控制。例如,可以使用 ForEach 在场景中定位并控制多个物体的位置、尺寸、颜色等属性。

  3. LazyForEach:与普通 ForEach 不同,LazyForEach 是一个惰性迭代方法,它只在需要迭代数据时才进行计算。这使得 LazyForEach 在处理大型数据集时更加高效,可以减少渲染时间和资源占用。


通过这些关键组件,ArkTS 语言可以实现灵活、高效的渲染控制,帮助开发人员实现各种复杂的渲染效果和场景呈现。

🔎1.if/else:条件渲染

🦋1.1 变化规则


If/else 是一种在程序中用于控制流程的结构。它包括一个 if 语句和一个可选的 else 语句。if 语句表示如果条件成立,那么就执行一段代码,否则跳过该代码;else 语句表示如果条件不成立,那么就执行另一段代码。这种结构允许程序根据不同的条件执行不同的操作,从而实现更灵活的程序控制。

🦋1.2 使用场景

☀️1.2.1 使用 if 进行条件渲染


@Entry@Componentstruct ViewA {  @State count: number = 0;
build() { Column() { Text(`count=${this.count}`)
if (this.count > 0) { Text(`count is positive`) .fontColor(Color.Green) }
Button('increase count') .onClick(() => { this.count++; }) Button('decrease count') .onClick(() => { this.count--; }) } }}
复制代码


☀️1.2.2 使用 if/else 进行条件渲染


@Componentstruct CounterView {  @Link counter: number;  label: string = 'unknown';
build() { Row() { Text(`${this.label}`) Button(`counter ${this.counter} +1`) .onClick(() => { this.counter += 1; }) } }}@Entry@Componentstruct MainView { @State toggle: boolean = true; @State counter: number = 0; build() { Column() { if (this.toggle) { CounterView({ counter: $counter, label: 'CounterView #positive' }) } else { CounterView({ counter: $counter, label: 'CounterView #negative' }) } Button(`toggle ${this.toggle}`) .onClick(() => { this.toggle = !this.toggle; }) } }}
复制代码


☀️1.2.3 嵌套 if 语句


@Entry@Componentstruct CompA {  @State toggle: boolean = false;  @State toggleColor: boolean = false;
build() { Column() { Text('Before') .fontSize(15) if (this.toggle) { Text('Top True, positive 1 top') .backgroundColor('#aaffaa').fontSize(20) // 内部if语句 if (this.toggleColor) { Text('Top True, Nested True, positive COLOR Nested ') .backgroundColor('#00aaaa').fontSize(15) } else { Text('Top True, Nested False, Negative COLOR Nested ') .backgroundColor('#aaaaff').fontSize(15) } } else { Text('Top false, negative top level').fontSize(20) .backgroundColor('#ffaaaa') if (this.toggleColor) { Text('positive COLOR Nested ') .backgroundColor('#00aaaa').fontSize(15) } else { Text('Negative COLOR Nested ') .backgroundColor('#aaaaff').fontSize(15) } } Text('After') .fontSize(15) Button('Toggle Outer') .onClick(() => { this.toggle = !this.toggle; }) Button('Toggle Inner') .onClick(() => { this.toggleColor = !this.toggleColor; }) } }}
复制代码

🔎2.ForEach:循环渲染

🦋2.1 变化规则


ForEach 是一个数组方法,用于执行一些操作,例如对数组中的每个元素执行一些代码,或者将每个元素转换为新的数组。它可以接收一个回调函数作为参数,该函数将在数组的每个元素上执行一次。在每次调用回调函数时,都会向其传递当前元素的值、索引和整个数组。该方法不会更改原始数组,而是返回一个新的、由回调函数返回的数组。


ForEach 接口描述:


ForEach(  arr: Array,  itemGenerator: (item: Array, index?: number) => void,  keyGenerator?: (item: Array, index?: number): string => string)
复制代码


注意点 keyGenerator 为键值生成函数,可以达到去重的效果:



@Entry@Componentstruct Parent {  @State simpleList: Array<string> = ['one', 'two', 'two', 'three'];
build() { Row() { Column() { ForEach(this.simpleList, (item: string) => { ChildItem({ item: item }) }, (item: string) => item) } .width('100%') .height('100%') } .height('100%') .backgroundColor(0xF1F3F5) }}@Componentstruct ChildItem { @Prop item: string; build() { Text(this.item) .fontSize(50) }}
复制代码


执行结果


🦋2.2 使用场景

☀️2.2.1 数据源不变
@Entry@Componentstruct ArticleList {  @State simpleList: Array<number> = [1, 2, 3, 4, 5];
build() { Column() { ForEach(this.simpleList, (item: string) => { ArticleSkeletonView() .margin({ top: 20 }) }, (item: string) => item) } .padding(20) .width('100%') .height('100%') }}@Builderfunction textArea(width: number | Resource | string = '100%', height: number | Resource | string = '100%') { Row() .width(width) .height(height) .backgroundColor('#FFF2F3F4')}@Componentstruct ArticleSkeletonView { build() { Row() { Column() { textArea(80, 80) } .margin({ right: 20 }) Column() { textArea('60%', 20) textArea('50%', 20) } .alignItems(HorizontalAlign.Start) .justifyContent(FlexAlign.SpaceAround) .height('100%') } .padding(20) .borderRadius(12) .backgroundColor('#FFECECEC') .height(120) .width('100%') .justifyContent(FlexAlign.SpaceBetween) }}
复制代码


执行效果:


☀️2.2.2 数据源数组项发生变化
@Entry@Componentstruct ArticleListView {  @State isListReachEnd: boolean = false;  @State articleList: Array<Article> = [    new Article('001', '第1篇文章', '文章简介内容'),    new Article('002', '第2篇文章', '文章简介内容'),    new Article('003', '第3篇文章', '文章简介内容'),    new Article('004', '第4篇文章', '文章简介内容'),    new Article('005', '第5篇文章', '文章简介内容'),    new Article('006', '第6篇文章', '文章简介内容')  ]
loadMoreArticles() { this.articleList.push(new Article('007', '加载的新文章', '文章简介内容')); }
build() { Column({ space: 5 }) { List() { ForEach(this.articleList, (item: Article) => { ListItem() { ArticleCard({ article: item }) .margin({ top: 20 }) } }, (item: Article) => item.id) } .onReachEnd(() => { this.isListReachEnd = true; }) .parallelGesture( PanGesture({ direction: PanDirection.Up, distance: 80 }) .onActionStart(() => { if (this.isListReachEnd) { this.loadMoreArticles(); this.isListReachEnd = false; } }) ) .padding(20) .scrollBar(BarState.Off) } .width('100%') .height('100%') .backgroundColor(0xF1F3F5) }}@Componentstruct ArticleCard { @Prop article: Article; build() { Row() { Image($r('app.media.icon')) .width(80) .height(80) .margin({ right: 20 }) Column() { Text(this.article.title) .fontSize(20) .margin({ bottom: 8 }) Text(this.article.brief) .fontSize(16) .fontColor(Color.Gray) .margin({ bottom: 8 }) } .alignItems(HorizontalAlign.Start) .width('80%') .height('100%') } .padding(20) .borderRadius(12) .backgroundColor('#FFECECEC') .height(120) .width('100%') .justifyContent(FlexAlign.SpaceBetween) }}
复制代码


当列表滚动到底部时,如果手势滑动距离超过指定的 80,将触发 loadMoreArticle()函数。此函数会在 articleList 数据源的尾部添加一个新的数据项,从而增加数据源的长度。


执行效果:


☀️2.2.3 数据源数组项子属性变化
@Entry@Componentstruct ArticleListView {  @State articleList: Array<Article> = [    new Article('001', '第0篇文章', '文章简介内容', false, 100),    new Article('002', '第1篇文章', '文章简介内容', false, 100),    new Article('003', '第2篇文章', '文章简介内容', false, 100),    new Article('004', '第4篇文章', '文章简介内容', false, 100),    new Article('005', '第5篇文章', '文章简介内容', false, 100),    new Article('006', '第6篇文章', '文章简介内容', false, 100),  ];
build() { List() { ForEach(this.articleList, (item: Article) => { ListItem() { ArticleCard({ article: item }) .margin({ top: 20 }) } }, (item: Article) => item.id) } .padding(20) .scrollBar(BarState.Off) .backgroundColor(0xF1F3F5) }}@Componentstruct ArticleCard { @ObjectLink article: Article; handleLiked() { this.article.isLiked = !this.article.isLiked; this.article.likesCount = this.article.isLiked ? this.article.likesCount + 1 : this.article.likesCount - 1; } build() { Row() { Image($r('app.media.icon')) .width(80) .height(80) .margin({ right: 20 }) Column() { Text(this.article.title) .fontSize(20) .margin({ bottom: 8 }) Text(this.article.brief) .fontSize(16) .fontColor(Color.Gray) .margin({ bottom: 8 }) Row() { Image(this.article.isLiked ? $r('app.media.iconLiked') : $r('app.media.iconUnLiked')) .width(24) .height(24) .margin({ right: 8 }) Text(this.article.likesCount.toString()) .fontSize(16) } .onClick(() => this.handleLiked()) .justifyContent(FlexAlign.Center) } .alignItems(HorizontalAlign.Start) .width('80%') .height('100%') } .padding(20) .borderRadius(12) .backgroundColor('#FFECECEC') .height(120) .width('100%') .justifyContent(FlexAlign.SpaceBetween) }}
复制代码


执行结果:



== 注意:开发者在使用 ForEach 时应特别重视 keyGenerator 为键值生成函数,会根据键值生成函数确定替换的值。==

🔎3.LazyForEach:数据懒加载

🦋3.1 变化规则


LazyForEach 是一种延迟执行的迭代方法,它可以帮助我们更高效地遍历和操作数组。与常规的 ForEach 方法不同,LazyForEach 方法不会立即执行回调函数,而是在需要访问数组元素时才将回调函数应用于该元素。这种延迟执行的方式可以大大减少不必要的计算,提高代码性能。LazyForEach 在处理大型数组或频繁进行迭代时特别有用。


LazyForEach 接口描述:


LazyForEach(    dataSource: IDataSource,             // 需要进行数据迭代的数据源    itemGenerator: (item: any, index?: number) => void,  // 子组件生成函数    keyGenerator?: (item: any, index?: number) => string // 键值生成函数): void
复制代码


IDataSource 接口描述:


interface IDataSource {    totalCount(): number; // 获得数据总数    getData(index: number): Object; // 获取索引值对应的数据    registerDataChangeListener(listener: DataChangeListener): void; // 注册数据改变的监听器    unregisterDataChangeListener(listener: DataChangeListener): void; // 注销数据改变的监听器}
复制代码


DataChangeListener 接口描述:


interface DataChangeListener {    onDataReloaded(): void; // 重新加载数据时调用    onDataAdded(index: number): void; // 添加数据时调用    onDataMoved(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换时调用    onDataDeleted(index: number): void; // 删除数据时调用    onDataChanged(index: number): void; // 改变数据时调用    onDataAdd(index: number): void; // 添加数据时调用    onDataMove(from: number, to: number): void; // 数据移动起始位置与数据移动目标位置交换时调用    onDataDelete(index: number): void; // 删除数据时调用    onDataChange(index: number): void; // 改变数据时调用}
复制代码

🦋3.2 使用场景

☀️3.2.1 首次渲染

🌈3.2.1.1 生成不同键值


1、正常渲染


// Basic implementation of IDataSource to handle data listenerclass BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: string[] = [];
public totalCount(): number { return 0; }
public getData(index: number): string { return this.originDataArray[index]; }
// 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
// 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); 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); }) }
// 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件 notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
// 通知LazyForEach组件需要在index对应索引处删除该子组件 notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: string[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): string { return this.dataArray[index]; }
public addData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: string): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); }}
@Entry@Componentstruct MyComponent { private data: MyDataSource = new MyDataSource(); aboutToAppear() { for (let i = 0; i <= 20; i++) { this.data.pushData(`Hello ${i}`) } }
build() { List({ space: 3 }) { LazyForEach(this.data, (item: string) => { ListItem() { Row() { Text(item).fontSize(50) .onAppear(() => { console.info("appear:" + item) }) }.margin({ left: 10, right: 10 }) } }, (item: string) => item) }.cachedCount(5) }}
复制代码


2、键值相同时错误渲染


class BasicDataSource implements IDataSource { private listeners: DataChangeListener[] = []; private originDataArray: string[] = [];
public totalCount(): number { return 0; }
public getData(index: number): string { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: string[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): string { return this.dataArray[index]; }
public addData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: string): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); }}
@Entry@Componentstruct MyComponent { private data: MyDataSource = new MyDataSource();
aboutToAppear() { for (let i = 0; i <= 20; i++) { this.data.pushData(`Hello ${i}`) } }
build() { List({ space: 3 }) { LazyForEach(this.data, (item: string) => { ListItem() { Row() { Text(item).fontSize(50) .onAppear(() => { console.info("appear:" + item) }) }.margin({ left: 10, right: 10 }) } }, (item: string) => 'same key') }.cachedCount(5) }}
复制代码
☀️3.2.2 非首次渲染

🌈3.2.2.1 添加数据


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: string[] = [];
public totalCount(): number { return 0; }
public getData(index: number): string { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: string[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): string { return this.dataArray[index]; }
public addData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: string): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); }}
@Entry@Componentstruct MyComponent { private data: MyDataSource = new MyDataSource();
aboutToAppear() { for (let i = 0; i <= 20; i++) { this.data.pushData(`Hello ${i}`) } }
build() { List({ space: 3 }) { LazyForEach(this.data, (item: string) => { ListItem() { Row() { Text(item).fontSize(50) .onAppear(() => { console.info("appear:" + item) }) }.margin({ left: 10, right: 10 }) } .onClick(() => { // 点击追加子组件 this.data.pushData(`Hello ${this.data.totalCount()}`); }) }, (item: string) => item) }.cachedCount(5) }}
复制代码


🌈3.2.2.2 删除数据


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: string[] = [];
public totalCount(): number { return 0; }
public getData(index: number): string { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { dataArray: string[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): string { return this.dataArray[index]; }
public addData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: string): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); } public deleteData(index: number): void { this.dataArray.splice(index, 1); this.notifyDataDelete(index); }}
@Entry@Componentstruct MyComponent { private data: MyDataSource = new MyDataSource();
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(this.data.dataArray.indexOf(item)); }) }, (item: string) => item) }.cachedCount(5) }}
复制代码


🌈3.2.2.3 改变单个数据


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: string[] = [];
public totalCount(): number { return 0; }
public getData(index: number): string { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: string[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): string { return this.dataArray[index]; }
public addData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: string): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); } public deleteData(index: number): void { this.dataArray.splice(index, 1); this.notifyDataDelete(index); } public changeData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataChange(index); }}
@Entry@Componentstruct MyComponent { private moved: number[] = []; private data: MyDataSource = new MyDataSource();
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.changeData(index, item + '00'); }) }, (item: string) => item) }.cachedCount(5) }}
复制代码


🌈3.2.2.4 改变多个数据


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: string[] = [];
public totalCount(): number { return 0; }
public getData(index: number): string { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: string[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): string { return this.dataArray[index]; }
public addData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: string): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); } public deleteData(index: number): void { this.dataArray.splice(index, 1); this.notifyDataDelete(index); } public changeData(index: number): void { this.notifyDataChange(index); } public reloadData(): void { this.notifyDataReload(); } public modifyAllData(): void { this.dataArray = this.dataArray.map((item: string) => { return item + '0'; }) }}
@Entry@Componentstruct MyComponent { private moved: number[] = []; private data: MyDataSource = new MyDataSource();
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.modifyAllData(); this.data.reloadData(); }) }, (item: string) => item) }.cachedCount(5) }}
复制代码


🌈3.2.2.5 改变数据子属性


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: StringData[] = [];
public totalCount(): number { return 0; }
public getData(index: number): StringData { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: StringData[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): StringData { return this.dataArray[index]; }
public addData(index: number, data: StringData): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: StringData): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); }}
@Observedclass StringData { message: string; constructor(message: string) { this.message = message; } }
@Entry@Componentstruct MyComponent { private moved: number[] = []; @State data: MyDataSource = new MyDataSource();
aboutToAppear() { for (let i = 0; i <= 20; i++) { this.data.pushData(new StringData(`Hello ${i}`)); } }
build() { List({ space: 3 }) { LazyForEach(this.data, (item: StringData, index: number) => { ListItem() { ChildComponent({data: item}) } .onClick(() => { item.message += '0'; }) }, (item: StringData, index: number) => index.toString()) }.cachedCount(5) }}@Componentstruct ChildComponent { @ObjectLink data: StringData build() { Row() { Text(this.data.message).fontSize(50) .onAppear(() => { console.info("appear:" + this.data.message) }) }.margin({ left: 10, right: 10 }) }}
复制代码
☀️3.2.3 问题点

🌈3.2.3.1 索引重置


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: string[] = [];
public totalCount(): number { return 0; }
public getData(index: number): string { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: string[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): string { return this.dataArray[index]; }
public addData(index: number, data: string): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: string): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); } public deleteData(index: number): void { this.dataArray.splice(index, 1); this.notifyDataDelete(index); } public reloadData(): void { this.notifyDataReload(); }}
@Entry@Componentstruct MyComponent { private data: MyDataSource = new MyDataSource();
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) }}
复制代码


🌈3.2.3.2 图片闪烁


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: StringData[] = [];
public totalCount(): number { return 0; }
public getData(index: number): StringData { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: StringData[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): StringData { return this.dataArray[index]; }
public addData(index: number, data: StringData): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: StringData): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); }}
@Observedclass StringData { message: string; imgSrc: Resource; constructor(message: string, imgSrc: Resource) { this.message = message; this.imgSrc = imgSrc; } }
@Entry@Componentstruct MyComponent { @State data: MyDataSource = new MyDataSource();
aboutToAppear() { for (let i = 0; i <= 20; i++) { this.data.pushData(new StringData(`Hello ${i}`, $r('app.media.img'))); } }
build() { List({ space: 3 }) { LazyForEach(this.data, (item: StringData, index: number) => { ListItem() { ChildComponent({data: item}) } .onClick(() => { item.message += '0'; }) }, (item: StringData, index: number) => index.toString()) }.cachedCount(5) }}@Componentstruct ChildComponent { @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.2.3.3 ObjectLink 属性变化后 UI 更新


class BasicDataSource implements IDataSource {  private listeners: DataChangeListener[] = [];  private originDataArray: StringData[] = [];
public totalCount(): number { return 0; }
public getData(index: number): StringData { return this.originDataArray[index]; }
registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } }
unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } }
notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) }
notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) }
notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) }
notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) }}
class MyDataSource extends BasicDataSource { private dataArray: StringData[] = [];
public totalCount(): number { return this.dataArray.length; }
public getData(index: number): StringData { return this.dataArray[index]; }
public addData(index: number, data: StringData): void { this.dataArray.splice(index, 0, data); this.notifyDataAdd(index); }
public pushData(data: StringData): void { this.dataArray.push(data); this.notifyDataAdd(this.dataArray.length - 1); }}
@Observedclass StringData { message: NestedString; constructor(message: NestedString) { this.message = message; } }
@Observedclass NestedString { message: string; constructor(message: string) { this.message = message; } }
@Entry@Componentstruct MyComponent { private moved: number[] = []; @State data: MyDataSource = new MyDataSource();
aboutToAppear() { for (let i = 0; i <= 20; i++) { this.data.pushData(new StringData(new NestedString(`Hello ${i}`))); } }
build() { List({ space: 3 }) { LazyForEach(this.data, (item: StringData, index: number) => { ListItem() { ChildComponent({data: item}) } .onClick(() => { item.message = new NestedString(item.message.message + '0'); }) }, (item: StringData, index: number) => item.toString() + index.toString()) }.cachedCount(5) }}@Componentstruct ChildComponent { @ObjectLink data: StringData build() { Row() { Text(this.data.message.message).fontSize(50) .onAppear(() => { console.info("appear:" + this.data.message.message) }) }.margin({ left: 10, right: 10 }) }}
复制代码

看完三件事❤️

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我四个小忙:

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。

  • 关注公众号 『 蜀道衫 』,不定期分享原创知识。

  • 同时可以期待后续文章 ing🚀

  • 关注后回复【666】扫码即可获取鸿蒙学习资料包


用户头像

蜀道山

关注

欢迎关注作者公众号:【 蜀道衫】 2023-12-29 加入

3年Java后端,5年Android应用开发,精通Java高并发、JVM调优、以及Android开发各种技能。现专研学习鸿蒙HarmonyOS Next操作系统

评论

发布
暂无评论
鸿蒙HarmonyOS实战-ArkTS语言(渲染控制)_鸿蒙_蜀道山_InfoQ写作社区