【HarmonyOS4.0】第五篇-ArkTS页面渲染

发布时间:2024年01月09日

三、渲染控制语法

ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。参考网址:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ets-rendering-control-0000001149698611

3.1.if/else条件渲染

使用 if/else 进行条件渲染需要注意以下情况:

  • if 条件语句可以使用状态变量。
  • 使用 if 可以使子组件的渲染依赖条件语句。
  • 必须在容器组件内使用。
  • 某些容器组件限制子组件的类型或数量。将if放置在这些组件内时,这些限制将应用于 if 和 else 语句内创建的组件。例如,Grid 组件的子组件仅支持 GridItem 组件,在 Grid 组件内使用条件渲染时,则 if 条件语句内仅允许使用 GridItem 组件。

案例如下:

@Entry
@Component
struct ComponentTest {
  @State showImage: boolean = false;  // 定义showImage状态,默认为false

  build() {
    // 纵向布局元素垂直方向间距。
    Column({space:10}){  // 创建一个垂直布局的Column组件,设置子元素间距为10
      // 如果showImage为true,则显示图片,否则显示"Loading ....."
      if(this.showImage){
        Image($r('app.media.Sns'))  // 显示图片,使用资源管理器获取图片资源
          .width(160)  // 设置宽度为160
          .height(60)  // 设置高度为60
          .backgroundColor(Color.Pink)  // 设置背景颜色为粉色
      }else {
        Text("Loading .....")
          .fontSize(23)  // 设置字体大小为23
          .fontColor('#FFFFF0') //设置字体颜色为象牙色
          .width(160)  // 设置宽度为160
          .height(60)  // 设置高度为60
          .backgroundColor(Color.Pink)  // 设置背景颜色为粉色
          .textAlign(TextAlign.Center)  // 设置文本居中对齐
      }

      Button(this.showImage?'Image Loaded':'Load Image')  // 按钮内容根据showImage状态动态显示
        .size({width:160,height:40})  // 设置按钮的宽和高
        .backgroundColor(this.showImage?'#3F56EA':'#9C554B')  // 根据showImage状态设置按钮背景色
        .onClick(()=>{  // 点击事件处理
          this.showImage = true;  // 点击后设置showImage值为true,显示图片
        })
    }.width('100%')  // 设置宽度为100%
    .height('100%')  // 设置高度为100%
    .padding(10)  // 设置内边距为10
  }
}

预览效果如下:

img

3.2.ForEach循环渲染

ArkUI开发框架提供循环渲染(ForEach组件)来迭代数组,并为每个数组项创建相应的组件。ForEach 定义如下:

interface ForEach {(
    arr: Array<any>, 
    itemGenerator: (item: any, index?: number) => void,
  keyGenerator?: (item: any, index?: number) => string
  ): ForEach;
}

说明:

  • arr:必须是数组,允许空数组,空数组场景下不会创建子组件。
  • itemGenerator:子组件生成函数,为给定数组项生成一个或多个子组件。
  • keyGenerator:匿名参数,用于给定数组项生成唯一且稳定的键值。

案例如下:

@Entry
@Component
struct ComponentTest02 {
  //准备源数据
  private textArray:string[] = ["一","二","三","四","五"]  // 定义一个包含字符串的数组作为源数据

  build() {
    Column({space:10}){  // 创建一个垂直布局的Column组件,设置子元素间距为10
      ForEach(this.textArray,(item:string,index?:number)=>{  // 遍历textArray数组
        Text(`标题:${item}`)  // 显示带有标题前缀的文本
          .fontSize(20)  // 设置字体大小为20
          .backgroundColor('#00B377')  // 设置背景颜色为绿色
          .margin({top:10})  // 设置上边距为10
          .fontColor('#FFFFF0')  // 设置字体颜色为白色
      })
    }.width("100%")  // 设置宽度为100%
    .height("100%")  // 设置高度为100%
    .padding(10)  // 设置内边距为10
  }
}

预览后效果如下:

img

3.3.数据懒加载LazyForEach循环渲染

ArkUI开发框架通过数据懒加载(LazyForEach)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。

1)LazyForEach 定义如下:

// LazyForEach定义
interface LazyForEach {(dataSource: IDataSource, itemGenerator: (item: any, index?: number) => void,keyGenerator?: (item: any, index?: number) => string): LazyForEach;
}

// IDataSource定义
export declare interface IDataSource {
  totalCount(): number;
  getData(index: number): any;
  registerDataChangeListener(listener: DataChangeListener): void;
  unregisterDataChangeListener(listener: DataChangeListener): void;
}

// DataChangeListener定义
export declare interface DataChangeListener {
  onDataReloaded(): void;
  onDataAdded(index: number): void;
  onDataMoved(from: number, to: number): void;
  onDataDeleted(index:number): void;
  onDataChanged(index:number): void;
}

说明如下:

  • itemGenerator:子组件生成函数,为给定数组项生成一个或多个子组件。
  • keyGenerator:匿名参数,用于给定数组项生成唯一且稳定的键值。
  • dataSource:实现 IDataSource 接口的对象,需要开发者实现相关接口。

2)IDataSource 定义如下:

export declare interface IDataSource {
  totalCount(): number;
  getData(index: number): any;
  registerDataChangeListener(listener: DataChangeListener): void;
  unregisterDataChangeListener(listener: DataChangeListener): void;
}

说明如下:

  • totalCount:获取数据总数。
  • getData:获取索引对应的数据。
  • registerDataChangeListener:注册改变数据的监听器。
  • unregisterDataChangeListener:注销改变数据的监听器。

3)DataChangeListener 定义如下:

export declare interface DataChangeListener {
  onDataReloaded(): void;
  onDataAdded(index: number): void;
  onDataMoved(from: number, to: number): void;
  onDataDeleted(index:number): void;
  onDataChanged(index:number): void;
}

说明如下:

  • onDataReloaded:item重新加载数据时的回调。
  • onDataAdded:item新添加数据时的回调。
  • onDataMoved:item数据移动时的回调。
  • onDataDeleted:item数据删除时的回调。
  • onDataChanged:item数据变化时的回调。

案例如下:

//##########################构造数据################################
//定义student
class Student{
  public sid: number; // 学生ID
  public name: string; // 学生姓名
  public age: number; // 学生年龄
  public address: string; // 学生地址
  public avatar: string; // 学生头像

  //构造方法
  constructor(sid: number, name: string, age: number, address: string, avatar: string) {
    this.sid = sid; // 初始化学生ID
    this.name = name; // 初始化学生姓名
    this.age = age; // 初始化学生年龄
    this.address = address; // 初始化学生地址
    this.avatar = avatar; // 初始化学生头像
  }
}

// 定义一个抽象类 BaseDataSource,使用泛型 T,实现接口 IDataSource
abstract class BaseDataSource<T> implements IDataSource {
  private dataSource: T[] = new Array(); // 数据源,使用泛型数组

  // 构造方法,接收一个泛型数组作为参数,用于初始化数据源
  constructor(dataList: T[]) {
    this.dataSource = dataList; // 初始化数据源
  }

  // 返回数据源的长度
  totalCount(): number {
    return this.dataSource == null ? 0 : this.dataSource.length;
  }

  // 获取指定索引的数据,如果索引合法则返回数据,否则返回 null
  getData(index: number): T | null {
    return index >= 0 && index < this.totalCount() ? this.dataSource[index] : null;
  }

  // 注册数据变化监听器,这里的方法体为空,需要在子类中实现具体逻辑
  registerDataChangeListener(listener: DataChangeListener) {}

  // 取消注册数据监听器,这里的方法体为空,需要在子类中实现具体逻辑
  unregisterDataChangeListener() {}
}

// 继承 BaseDataSource 类,指定泛型为 Student 类型
class StudentDataSource extends BaseDataSource<Student> {
  constructor(students: Student[]) {
    super(students); // 调用父类构造函数进行初始化
  }
}

//产生数据
function mock():Student[]{
  let students = [];

  for (let i = 1; i < 21; i++) {
    //模拟学生数据
    students[i] = new Student(i,"student:"+i, i+10, "address:"+i, "app.media.Sns");
  }
  return students;
}

@Entry
@Component
struct ComponentTest03 {
  // mock数据
  private student: Student[] = mock(); // 模拟学生数据

  // 创建dataSource
  private dataSource: StudentDataSource = new StudentDataSource(this.student); // 创建学生数据源

  build() {
    Column({ space: 10 }) { // 创建一个垂直布局的列,设置间距为10
      List() { // 创建一个列表
        LazyForEach(this.dataSource, (item: Student) => { // 使用自定义dataSource进行懒加载
          ListItem() { // 创建列表项
            Row() { // 创建一个水平布局的行
              Image($r("app.media.Sns")) // 显示学生头像
                .height('100%') // 设置高度为100%
                .width(80) // 设置宽度为80
              Column() { // 创建一个垂直布局的列
                Text(this.getName(item)) // 调用getName方法验证懒加载
                  .fontSize(20) // 设置字体大小为20
                Text('address: ' + item.address) // 显示学生地址
                  .fontSize(17) // 设置字体大小为17
              }
              .margin({ left: 5 }) // 设置左边距为5
              .alignItems(HorizontalAlign.Start) // 设置子元素水平方向对齐方式为起始位置
              .layoutWeight(1) // 设置布局权重为1
            }
            .width('100%') // 设置宽度为100%
            .height('100%') // 设置高度为100%
          }
          .width('100%') // 设置宽度为100%
          .height(60) // 设置高度为60
        })
      }
      .divider({ // 设置列表的分隔线样式
        strokeWidth: 3, // 分隔线宽度为3
        color: Color.Gray // 分隔线颜色为灰色
      })
      .width('90%') // 设置宽度为90%
      .height(160) // 设置高度为160
      .backgroundColor(Color.Pink) // 设置背景颜色为粉色
    }
    .width('100%') // 设置宽度为100%
    .height('100%') // 设置高度为100%
    .padding(10) // 设置内边距为10
  }

  getName(item: Student): string {
    console.log("index: " + item.sid); // 打印学生索引日志
    return 'index:' + item.sid + ", " + item.name; // 返回学生索引和姓名
  }
}

执行后效果如下:

img

输出的日志如下:

img

说明:

  • LazyForEach必须在容器组件内使用,目前仅有List、Grid以及Swiper组件支持数据懒加载(即只加载可视部分以及其前后少量数据用于缓冲,避免过多的占用资源),其他组件仍然是一次性加载所有的数据。
  • LazyForEach在每次迭代中,必须创建且只允许创建一个子组件。
  • 生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。
  • 允许LazyForEach包含在if/else条件渲染语句中,但不允许LazyForEach中出现if/else条件渲染语句。
  • 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,仅当itemGenerator中创建的子组件内使用了状态变量时,才会触发组件刷新。
  • itemGenerator函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:
LazyForEach(dataSource, 
  item => Text(`${item.i}. item.data.label`)),
  item => item.data.id.toString())
文章来源:https://blog.csdn.net/weixin_42533732/article/details/135462478
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。