HarmonyOS Developer 学习ArkTS语言

丶龙八夷
发布于 2023-3-22 16:49
浏览
0收藏

ArkTS语法(声明式UI)- 渲染控制

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

条件渲染

使用if/else进行条件渲染。

说明

  • if/else条件语句可以使用状态变量,并且在更新场景下必须使用状态变量。
  • 使用if/else可以使子组件的渲染依赖条件语句。
  • 必须在容器组件内使用。
  • 某些容器组件限制子组件的类型或数量,将if/else用于这些组件内时,这些限制将同样应用于if/else语句内创建的组件。例如,Grid容器组件的子组件仅支持GridItem组件,在Grid内使用if/else时,则if/else语句内也仅允许使用GridItem组件。

if/else语句的每个分支都包含一个构建UI的描述,此类描述中必须创建一个或多个子组件。在初始渲染时,if语句会执行其中的一个分支并将生成的子组件添加到if/else的父组件中。

每当if或else if条件语句中使用的状态变量发生变化时,条件渲染都会更新并重新进行条件评估,如果条件分支发生了变化,这意味着需要构建另一个条件分支,此时框架将:

  1. 删除所有以前渲染的(早期分支的)组件。
  2. 执行分支的UI描述,将生成的子组件添加到其父组件中。

在以下示例中,如果count从0增加到1,那么条件渲染会进行更新并计算各个条件分支值:

  • 条件(count > 0)计算结果为false,未发生变化,不执行额外操作;
  • 条件(this.count % 2 === 0)从true更改为false,该分支内的Text组件将从Column组件中移除并销毁;
  • else分支为true,执行该条件下的UI描述,创建一个Text组件,并将它放在Column组件内。

@Component
struct MyComponent {
  @State count: number = 300
  build() {
    Column() {
      if (this.count < 0) {
        Text('count is negative').fontSize(14)
      } else if (this.count % 2 === 0) {
        Text('count is even').fontSize(14)
      } else {
        Text('count is odd').fontSize(14)
      }
    }
  }
}

循环渲染

通过循环渲染(ForEach)从数组中获取数据,并为每个数据项创建相应的组件,可减少代码复杂度。

ForEach(
  arr: any[], 
  itemGenerator: (item: any, index?: number) => void,
  keyGenerator?: (item: any, index?: number) => string 
)

参数:


参数名

参数类型

必填

参数描述

arr

any[]

必须是数组,允许设置为空数组,空数组场景下将不会创建子组件。同时允许设置返回值为数组类型的函数,例如arr.slice(1, 3),设置的函数不得改变包括数组本身在内的任何状态变量,如Array.splice、Array.sort或Array.reverse这些改变原数组的函数。

itemGenerator

(item: any, index?: number) => void

生成子组件的lambda函数,为数组中的每一个数据项创建一个或多个子组件,单个子组件或子组件列表必须包括在大括号“{...}”中。

keyGenerator

(item: any, index?: number) => string

匿名函数,用于给数组中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,不提供时框架默认使用index+JSON.stringify(item)的方式生成。

为了使开发框架能够更好地识别数组更改,提高性能,建议开发者提供自定义的键值生成器。如将数组反向时,如果没有提供键值生成器,则ForEach中的所有节点都将重建。

说明

  • ForEach必须在容器组件内使用。
  • 生成的子组件应当是允许包含在ForEach父容器组件中的子组件。
  • 允许子组件生成器函数中包含if/else条件渲染。
  • 键值生成器必须针对每个数据生成唯一的值,如果键值相同,将导致键值相同的UI组件被框架忽略,从而无法在父容器内显示。
  • itemGenerator函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:


​ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }), item => Text(`${item.i}. item.data.label`), item => item.data.id.toString())​


示例

// xxx.ets
@Entry
@Component
struct MyComponent {
  @State arr: number[] = [10, 20, 30]

  build() {
    Column({ space: 5 }) {
      Button('Reverse Array')
        .onClick(() => {
          this.arr.reverse()
        })

      ForEach(this.arr, (item: number) => {
        Text(`item value: ${item}`).fontSize(18)
        Divider().strokeWidth(2).color(Color.Black)
      }, (item: number) => item.toString())
    }
  }
}

HarmonyOS Developer 学习ArkTS语言-鸿蒙开发者社区

数据懒加载

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

LazyForEach(
  dataSource: IDataSource,             
  itemGenerator: (item: any) => void,  
  keyGenerator?: (item: any) => string 
): void

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

interface DataChangeListener {
  onDataReloaded(): void;                      
  onDataAdd(index: number): void;            
  onDataMove(from: number, to: number): void; 
  onDataDelete(index: number): void;         
  onDataChange(index: number): void;          
}

参数:

参数名

参数类型

必填

参数描述

dataSource

IDataSource

实现IDataSource接口的对象,需要开发者实现相关接口。

itemGenerator

(item: any, index?: number) => void

生成子组件的lambda函数,为数组中的每一个数据项创建一个或多个子组件,单个子组件或子组件列表必须包括在大括号“{...}”中。

keyGenerator

(item: any, index?: number) => string

匿名函数,用于给数组中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,不提供时框架默认使用index+JSON.stringify(item)的方式生成。

为了使开发框架能够更好地识别数组更改,提高性能,建议提供自定义的键值生成器。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。

IDataSource类型说明

名称

描述

totalCount(): number

获取数据总数。

getData(index: number): any

获取索引值index对应的数据。

registerDataChangeListener(listener:DataChangeListener): void

注册数据改变的监听器。

unregisterDataChangeListener(listener:DataChangeListener): void

注销数据改变的监听器。

DataChangeListener类型说明

名称

描述

onDataReloaded(): void

重新加载所有数据。

onDataAdded(index: number): voiddeprecated

通知组件index的位置有数据添加。从API Version 8开始废弃,建议使用onDataAdd。

onDataMoved(from: number, to: number): voiddeprecated

通知组件数据从from的位置移到to的位置。从API Version 8开始废弃,建议使用onDataMove。

onDataDeleted(index: number): voiddeprecated

通知组件index的位置有数据删除。从API Version 8开始废弃,建议使用onDataDelete。

onDataChanged(index: number): voiddeprecated

通知组件index的位置有数据变化。 从API Version 8开始废弃,建议使用onDataChange。

onDataAdd(index: number): void8+

通知组件index的位置有数据添加。

onDataMove(from: number, to: number): void8+

通知组件数据从from的位置移到to的位置。

onDataDelete(index: number): void8+

通知组件index的位置有数据删除。

onDataChange(index: number): void8+

通知组件index的位置有数据变化。

示例

// xxx.ets
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('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)
    })
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to)
    })
  }
}

class MyDataSource extends BasicDataSource {
  // 初始化数据列表
  private dataArray: string[] = ['/path/image0.png', '/path/image1.png', '/path/image2.png', '/path/image3.png']

  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 {
  private data: MyDataSource = new MyDataSource()

  build() {
    List({ space: 3 }) {
      LazyForEach(this.data, (item: string) => {
        ListItem() {
          Row() {
            Image(item).width(50).height(50)
            Text(item).fontSize(20).margin({ left: 10 })
          }.margin({ left: 10, right: 10 })
        }
        .onClick(() => {
          // 每点击一次列表项,数据增加一项
          this.data.pushData('/path/image' + this.data.totalCount() + '.png')
        })
      }, item => item)
    }
  }
}

说明

  • LazyForEach必须在容器组件内使用,目前仅有List、Grid以及Swiper组件支持数据懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。
  • LazyForEach在每次迭代中,必须创建且只允许创建一个子组件。
  • 生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。
  • 允许LazyForEach包含在if/else条件渲染语句中,也允许LazyForEach中出现if/else条件渲染语句。
  • 键值生成器必须针对每个数据生成唯一的值,如果键值相同,将导致键值相同的UI组件被框架忽略,从而无法在父容器内显示。
  • LazyForEach必须使用DataChangeListener对象来进行更新,第一个参数dataSource使用状态变量时,状态变量改变不会触发LazyForEach的UI刷新。
  • 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,需要生成不同于原来的键值来触发组件刷新。
  • itemGenerator函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:


​LazyForEach(dataSource, item => Text(`${item.i}. item.data.label`), item => item.data.id.toString())​


HarmonyOS Developer 学习ArkTS语言-鸿蒙开发者社区



文章转载自:​​https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/arkts-rendering-control-0000001427744548-V3?catalogVersion=V3​

标签
已于2023-3-22 16:49:00修改
收藏
回复
举报
回复
    相关推荐