HarmonyOS 鸿蒙Next ForEach循环渲染

发布于 1周前 作者 sinazl 来自 鸿蒙OS

HarmonyOS 鸿蒙Next ForEach循环渲染 1、
ForEach接口基于数组类型数据来进行循环渲染,需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。例如,ListItem组件要求ForEach的父容器组件必须为List组件。从API version 9开始,该接口支持在ArkTS卡片中使用。

1.1 接口描述

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

关于ForEach参数请参考官方文档:ForEach参数

  • ForEach的itemGenerator函数可以包含if/else条件渲染逻辑。另外,也可以在if/else条件渲染语句中使用ForEach组件。
  • 在初始化渲染时,ForEach会加载数据源的所有数据,并为每个数据项创建对应的组件,然后将其挂载到渲染树上。如果数据源非常大或有特定的性能需求,建议使用LazyForEach组件。

1.2 键值生成规则

在ForEach循环渲染过程中,系统会为每个数组元素生成一个唯一且持久的键值,用于标识对应的组件。当这个键值变化时,ArkUI框架将视为该数组元素已被替换或修改,并会基于新的键值创建一个新的组件。

ForEach提供了一个名为keyGenerator的参数,这是一个函数,开发者可以通过它自定义键值的生成规则。如果开发者没有定义keyGenerator函数,则ArkUI框架会使用默认的键值生成函数,即(item: any, index: number) => { return index + ‘__’ + JSON.stringify(item); }。

ArkUI框架对于ForEach的键值生成有一套特定的判断规则,这主要与itemGenerator函数的第二个参数index以及keyGenerator函数的返回值有关。总的来说,只有当开发者在itemGenerator函数中声明了index参数,并且自定义的keyGenerator函数返回值中不包含index参数时,ArkUI框架才会在开发者自定义的keyGenerator函数返回值前添加index参数,作为最终的键值。在其他情况下,系统将直接使用开发者自定义的keyGenerator函数返回值作为最终的键值。如果keyGenerator函数未定义,系统将使用上述默认的键值生成函数。

1.3 组件创建规则

在确定键值生成规则后,ForEach的第二个参数itemGenerator函数会根据键值生成规则为数据源的每个数组项创建组件。组件的创建包括两种情况:ForEach首次渲染和ForEach非首次渲染。

1.3.1 首次渲染

在ForEach首次渲染时,会根据前述键值生成规则为数据源的每个数组项生成唯一键值,并创建相应的组件。

@Entry
@Component
struct Parent {
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
    Row() {
      Column() {
        ForEach(this.simpleList, (item: string) => {
          ChildItem({ item: item })
        }, (item: string) => item)
      }
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
  item: string;

  build() {
    Text(this.item)
      .fontSize(50)
  }
}

1.3.2 非首次渲染

在ForEach组件进行非首次渲染时,它会检查新生成的键值是否在上次渲染中已经存在。如果键值不存在,则会创建一个新的组件;如果键值存在,则不会创建新的组件,而是直接渲染该键值所对应的组件。例如,在以下的代码示例中,通过点击事件修改了数组的第三项值为"new three",这将触发ForEach组件进行非首次渲染。

@Entry
@Component
struct Parent {
  @State simpleList: Array<string> = ['one', 'two', 'three'];

  build() {
    Row() {
      Column() {
        Text('点击修改第3个数组项的值')
          .fontSize(24)
          .fontColor(Color.Red)
          .onClick(() => {
            this.simpleList[2] = 'new three';
          })

        ForEach(this.simpleList, (item: string) => {
          ChildItem({ item: item })
            .margin({ top: 20 })
        }, (item: string) => item)
      }
      //设置子组件在垂直方向上的对齐格式,默认值:FlexAlign.Start
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('100%')
    }
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ChildItem {
  item: string;

  build() {
    Text(this.item)
      .fontSize(30)
  }
}

1.4 使用场景

ForEach组件在开发过程中的主要应用场景包括:

  • 数据源不变
  • 数据源数组项发生变化(如插入、删除操作)
  • 数据源数组项子属性变化

1.4.1 数据源数组项发生变化

在数据源数组项发生变化的场景下,例如进行数组插入、删除操作或者数组项索引位置发生交换时,数据源应为对象数组类型,并使用对象的唯一ID作为最终键值。例如,当在页面上通过手势上滑加载下一页数据时,会在数据源数组尾部新增新获取的数据项,从而使得数据源数组长度增大。

@Entry
@Component
struct 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 为自定义组件,用于构建单个文章卡片
            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;
            }
          })
      )
      // 为列表添加20单位的内边距
      .padding(20)
      //表示关闭滚动条
      .scrollBar(BarState.Off)
    }
    .width('100%')
    .height('100%')
    .backgroundColor(0xF1F3F5)
  }
}

@Component
struct ArticleCard {
  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 })
      }
      //设置子组件在水平方向上的对齐格式,默认值:HorizontalAlign.Center
      .alignItems(HorizontalAlign.Start)
      .width('80%')
      .height('100%')
    }
    //设置内边缘
    .padding(20)
    //是否圆角
    .borderRadius(12)
    .backgroundColor('#FFECECEC')
    .height(120)
    .width('100%')
    //设置子组件在垂直方向上的对齐格式。默认值:FlexAlign.Start
    //Flex主轴方向均匀分配弹性元素,相邻元素之间距离相同。第一个元素与行首对齐,最后一个元素与行尾对齐。
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

//自定义 Article 类
class Article {
  public id: string
  public title: string
  public brief: string

  constructor(id: string, title: string, brief: string) {
    this.id = id;
    this.title = title;
    this.brief = brief;
  }
}

1.5 使用建议

  • 尽量避免在最终的键值生成规则中包含数据项索引index,以防止出现渲染结果非预期和渲染性能降低。如果业务确实需要使用index,例如列表需要通过index进行条件渲染,开发者需要接受ForEach在改变数据源后重新创建组件所带来的性能损耗。
  • 为满足键值的唯一性,对于对象数据类型,建议使用对象数据中的唯一id作为键值。
  • 基本数据类型的数据项没有唯一ID属性。如果使用基本数据类型本身作为键值,必须确保数组项无重复。因此,对于数据源会发生变化的场景,建议将基本数据类型数组转化为具备唯一ID属性的对象数据类型数组,再使用ID属性作为键值生成规则。

更多关于HarmonyOS 鸿蒙Next ForEach循环渲染的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS 鸿蒙Next ForEach循环渲染的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS(鸿蒙)中,Next ForEach循环渲染通常用于在UI组件中遍历并显示数据列表。鸿蒙系统提供了丰富的UI框架来支持这种操作,虽然具体的API和语法可能与传统的Web前端框架有所不同,但核心思想是一致的。

在鸿蒙的ArkUI(使用TypeScript或eTS语言)中,你可以使用forEach循环或类似机制来遍历数据并动态生成UI组件。例如,如果你有一个包含多个项目的数据数组,并希望将这些项目渲染为列表,你可以这样做:

@Entry
@Component
struct MyComponent {
  @State dataList: Array<any> = [/* 数据项数组 */];

  build() {
    Column() {
      this.dataList.forEach(item => {
        ListItem({ text: item.name }) // 假设每个数据项有一个name属性
      });
    }
  }
}

注意,上述代码是一个简化的示例,实际使用中可能需要根据鸿蒙的UI组件库调整语法。ListItem是一个假设的组件,用于展示每个数据项,你需要替换为鸿蒙实际提供的组件。

鸿蒙的UI框架提供了灵活的布局和组件系统,使得动态渲染列表变得简单高效。确保你查阅最新的鸿蒙开发文档,以获取关于如何正确实现这种功能的最新信息。

如果问题依旧没法解决请联系官网客服,官网地址是 https://www.itying.com/category-93-b0.html

回到顶部