[写在前面: 文章多处用到gif动图,如未自动播放,请点击图片]
衔接上一篇: 鸿蒙开发-ArkTS 语言-状态管理
4. 渲染控制
对于 UI 渲染,可以基于数据结构选择一些内置方法(例如:ForEach)快速渲染 UI 结构。
4.1 if-else条件渲染
ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,使用if、else和else if渲染对应状态下的UI内容。
条件渲染语句允许基于条件在组件内构建不同子组件,支持if、else、else if语句,条件可以使用状态变量,但需遵循父子组件关系规则,确保每个分支内创建至少一个组件,且子组件类型和数量需符合父组件限制。
代码示例:
@Entry
@Component
struct ViewA {@State count: number = 0;build() {Column() {Text(`count=${this.count}`)if (this.count >= 0) {Text(`count 为正数`).fontColor(Color.Green)} else {Text(`count 为负数`).fontColor(Color.Red)}Button('增 count').onClick(() => {this.count++;})Button('减 count').onClick(() => {this.count--;})}}
}
图示:
4.2 ForEach:循环渲染
必须使用数组(允许空数组),设置的循环函数不允许改变源数组。
一共三个参数:
-
ForEach组件接受
arr
属性作为数组输入,必须是数组类型,允许为空数组,可用返回数组的函数。数组为空时,不会创建子组件。 -
itemGenerator
是必需的,是一个lambda函数,根据数组中的每个数据项生成一个或多个子组件。 -
可选的
keyGenerator
是一个匿名函数,用于生成数组中每个数据项的唯一 key 值。
简单的示例:
@Entry
@Component
struct MyComponent {@State arr: number[] = [10, 20, 30];build() {Column({ space: 5 }) {Button('翻转数组').onClick(() => {this.arr.reverse();})ForEach(this.arr, (item: number) => {Text(`此项值: ${item}`).fontSize(18)Divider().strokeWidth(2)}, (item: number) => item.toString())}}
}
效果如下:
配合 @ObjectLInk 的 ForEach 示例
let NextID: number = 0;@Observed
class MyCounter {public id: number;public c: number;constructor(c: number) {this.id = NextID++;this.c = c;}
}@Component
struct CounterView {@ObjectLink counter: MyCounter;label: string = '计数器视图';build() {Button(`计数器视图 [${this.label}] this.counter.c=${this.counter.c} +1`).width(400).height(50).onClick(() => {this.counter.c += 1;})}
}@Entry
@Component
struct MainView {@State firstIndex: number = 0;@State counters: Array<MyCounter> = [new MyCounter(0), new MyCounter(0), new MyCounter(0),new MyCounter(0), new MyCounter(0)];build() {Column() {ForEach(this.counters.slice(this.firstIndex, this.firstIndex + 3),(item) => {CounterView({ label: `计数器项目 #${item.id}`, counter: item })},(item) => item.id.toString())Button(`计数器:向上移动`).width(200).height(50).onClick(() => {this.firstIndex = Math.min(this.firstIndex + 1, this.counters.length - 3);})Button(`计数器:向下移动`).width(200).height(50).onClick(() => {this.firstIndex = Math.max(0, this.firstIndex - 1);})}}
}
图示:
4.3 LazyForEach:数据懒加载
LazyForEach是一个用于按需迭代数据并创建组件的接口,适用于滚动容器以提高性能。它接受数据源、子组件生成函数和可选的键值生成函数作为参数。在每次迭代中,子组件生成函数生成一个子组件,并且键值生成函数可选地用于为数据项生成唯一的键值。
同时,数据变化监听器(DataChangeListener)提供了各种通知方法,如重新加载数据、数据添加、数据移动、数据删除和数据变化。在使用LazyForEach时,需要注意它必须在支持懒加载的容器组件内使用,且生成的子组件必须符合容器组件的规定。
IDataSource类型说明
interface IDataSource {totalCount(): number; // 获得数据总数getData(index: number): any; // 获取索引值对应的数据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; // 改变数据时调用
}
懒加载示例:
// 实现基本的IDataSource以处理数据监听器
class BasicDataSource implements IDataSource {private listeners: DataChangeListener[] = [];// 获取数据总数public totalCount(): number {return 0;}// 获取特定索引的数据public getData(index: number): any {return undefined;}// 注册数据变化监听器registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {console.info('添加监听器');this.listeners.push(listener);}}// 注销数据变化监听器unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {console.info('移除监听器');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);});}// 通知数据移动notifyDataMove(from: number, to: number): void {this.listeners.forEach(listener => {listener.onDataMove(from, to);});}
}class MyDataSource extends BasicDataSource {private dataArray: string[] = [];// 获取数据总数public totalCount(): number {return this.dataArray.length;}// 获取特定索引的数据public getData(index: number): any {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
@Component
struct MyComponent {aboutToAppear() {for (var i = 100; i >= 80; i--) {this.data.pushData(`Hello ${i}`);}}private data: MyDataSource = new MyDataSource();build() {List({ space: 3 }) {// 使用LazyForEach按需迭代数据LazyForEach(this.data, (item: string) => {ListItem() {Row() {Text(item).fontSize(50).onAppear(() => {console.info("出现:" + item);});}.margin({ left: 10, right: 10 });}.onClick(() => {this.data.pushData(`Hello ${this.data.totalCount()}`);});}, item => item);}.cachedCount(5); // 设置缓存的数据数量}
}
图示:
持续更新中……