鸿蒙开发-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); // 设置缓存的数据数量
}
}
图示: