ArkTS 框架采用 List 容器创建列表(类似 Android 的 RecycleView、Compose 的 LazyColumn)。

使用列表可以轻松高效地显示结构化、可滚动的信息。通过在 List 组件中按垂直或者水平方向线性排列子组件 ListItemGroup 或 ListItem,为列表中的行或列提供单个视图,或使用ForEach 迭代一组行或列,或混合任意数量的单个视图和 ForEach 结构,构建一个列表。

一、布局和约束

列表作为一种容器,会自动按其滚动方向排列子组件,向列表中添加组件或从列表中移除组件会重新排列子组件。

如下图所示,在垂直列表中,List 按垂直方向自动排列 ListItemGroup 或 ListItem。

ListItemGroup 用于列表数据的分组展示,其子组件也是 ListItem。ListItem 表示单个列表项,可以包含单个子组件。

在这里插入图片描述

List的子组件必须是 ListItemGroup 或 ListItem,ListItem 和 ListItemGroup 必须配合 List 来使用。

二、 静态创建

List 静态地创建其列表项 ListItem 的内容:

@Component
struct ListTest {

  build() {
    List() {
      ListItem() {
        Text("Kotlin").fontSize(10)
      }
      ListItem() {
        Text("TypeScript").fontSize(10)
      }
      ListItem() {
        Text("ArkTS").fontSize(10)
      }
    }
    .backgroundColor('#FFF1F3F5')
    .alignListItem(ListItemAlign.Center)
  }
}

在这里插入图片描述

我们可以丰富一下 ListItem,给它加上图标:

@Component
struct ListTest {

  build() {
    List() {
      ListItem() {
        Row() {
          Image($r('app.media.icon')).width(20).height(20).margin(10)
          Text("Kotlin").fontSize(10)
        }
      }
      ListItem() {
        Row() {
          Image($r('app.media.icon')).width(20).height(20).margin(10)
          Text("TypeScript").fontSize(10)
        }
      }
      ListItem() {
        Row() {
          Image($r('app.media.icon')).width(20).height(20).margin(10)
          Text("ArkTS").fontSize(10)
        }
      }
    }
    .backgroundColor('#FFF1F3F5')
    .alignListItem(ListItemAlign.Start)
  }
}

在这里插入图片描述

三、循环渲染

一个列表项这么创建好了,但是我们可以发现 ListItem 内部的代码结构是一模一样的,这样写会显得代码很臃肿。

ListItem() {
  Row() {
    Image($r('app.media.icon')).width(20).height(20).margin(10)
    Text("Kotlin").fontSize(10)
  }
}
ListItem() {
  Row() {
    Image($r('app.media.icon')).width(20).height(20).margin(10)
    Text("TypeScript").fontSize(10)
  }
}
ListItem() {
  Row() {
    Image($r('app.media.icon')).width(20).height(20).margin(10)
    Text("ArkTS").fontSize(10)
  }
}

ArkTS 通过 ForEach 提供了组件的循环渲染能力。我们可以使用 ForEach,在其中以嵌套 ListItem 的形式来代替多个平铺的、内容相似的 ListItem,从而减少重复代码。如果对于 ForEach 不了解,可以查看 写给初学者的 HarmonyOS 教程 – 循环渲染(ForEach),了解其用法。

import util from '@ohos.util';

class Language {
  key: string = util.generateRandomUUID(true);
  icon: Resource;
  name: string;

  constructor(icon: Resource, name: string) {
    this.icon = icon;
    this.name = name;
  }
}

@Entry
@Component
struct ListTest {

  private devLanguages = [
    new Language($r("app.media.icon"), 'Kotlin'),
    new Language($r("app.media.icon"), 'TypeScript'),
    new Language($r('app.media.icon'), 'ArkTS')
  ]

  build() {
    List() {
      ForEach(this.devLanguages, (item: Language) => {
        ListItem() {
          Row() {
            Image(item.icon).width(20).height(20).margin(10)
            Text(item.name).fontSize(10)
          }
          .width('100%')
          .justifyContent(FlexAlign.Start)
        }
      })
    }
    .backgroundColor('#FFF1F3F5')
    .alignListItem(ListItemAlign.Start)
  }
}

四、自定义列表样式

> 列表项间距

在初始化列表时,可以使用 space 参数添加列表项的间距。例如,在每个列表项之间沿主轴方向添加 30vp 的间距:

build() {
    List({ space: 30 }) {
      ForEach(this.devLanguages, (item: Language) => {
        ... ...
      })
    }
  }

在这里插入图片描述

> 添加分割线

List 提供了divider 属性用于给列表项之间添加分隔线。

List({ space: 30 }) {
  ForEach(this.devLanguages, (item: Language) => {
    ListItem() {
      Row() {
        Image(item.icon).width(20).height(20).margin(10)
        Text(item.name).fontSize(10)
      }
      .width('100%')
      .justifyContent(FlexAlign.Start)
    }
  })
}
.divider({
  strokeWidth: 1,     // 分割线粗细
  startMargin: 40,    // 分隔线距离列表侧边起始端的距离
  endMargin: 10,      // 分隔线距离列表侧边结束端的距离
  color: '#ff085DFF'  // 分割线颜色
})

效果:

在这里插入图片描述

> 添加滚动条

实际 UI 界面可能会有很多列表项,超出屏幕后我们就需要添加滚动条,让用户可以滚动屏幕。比如我们再多添加几个语言:

private devLanguages = [
    new Language($r("app.media.icon"), 'Kotlin'),
    new Language($r("app.media.icon"), 'TypeScript'),
    new Language($r('app.media.icon'), 'ArkTS'),
    new Language($r('app.media.icon'), 'Python'),
    new Language($r('app.media.icon'), 'Java'),
    new Language($r('app.media.icon'), 'JavaScript'),
    new Language($r('app.media.icon'), 'C++'),
    new Language($r('app.media.icon'), 'C#'),
    new Language($r('app.media.icon'), 'Ruby'),
    new Language($r('app.media.icon'), 'Swift'),
    new Language($r('app.media.icon'), 'Go'),
    new Language($r('app.media.icon'), 'PHP'),
    new Language($r('app.media.icon'), 'Flutter'),
    new Language($r('app.media.icon'), 'R'),
  ]

这个时候一个屏幕塞不下这么多列表项,我们可以通过 List 的 scrollBar 属性控制列表滚动条的显示。

scrollBar 的取值类型为 BarState,当取值为 BarState.Auto 表示按需显示滚动条。此时,当触摸到滚动条区域时显示控件,可上下拖拽滚动条快速浏览内容,拖拽时会变粗。若不进行任何操作,2秒后滚动条自动消失。

List() {
  ...
}
.scrollBar(BarState.Auto)

效果:

在这里插入图片描述

五、分组列表

所谓分组列表,就是给列表项分组,比如联系人列表就是分组列表。

在 List 组件中使用 ListItemGroup 对项目进行分组,可以构建二维列表。

在 List 组件中可以直接使用一个或者多个 ListItemGroup 组件,ListItemGroup 的宽度默认充满 List 组件。在初始化ListItemGroup 时,可通过 header 参数设置列表分组的头部组件。

@Component
struct ListTest {

  @Builder itemHead(text: string) {
    // 列表分组的头部组件
    Text(text)
      .fontSize(10)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  private devLanguages1 = [
    new Language($r("app.media.icon"), 'Kotlin'),
    new Language($r("app.media.icon"), 'TypeScript'),
    new Language($r('app.media.icon'), 'ArkTS'),
    new Language($r('app.media.icon'), 'Python'),
  ]

  private devLanguages2 = [
    new Language($r('app.media.icon'), 'Java'),
    new Language($r('app.media.icon'), 'JavaScript'),
    new Language($r('app.media.icon'), 'C++'),
    new Language($r('app.media.icon'), 'C#'),
  ]

  build() {
    List() {
      ListItemGroup( { header: this.itemHead( '开发语言 1')}) {
      // 循环渲染分组 开发语言 1 的 ListItem
        ForEach(this.devLanguages1, (item: Language) => {
          ListItem() {
            Row() {
              Image(item.icon).width(20).height(20).margin(10)
              Text(item.name).fontSize(10)
            }
            .width('100%')
            .justifyContent(FlexAlign.Start)
          }
        })
      }
      .divider({
        strokeWidth: 1,
        startMargin: 40,
        endMargin: 10,
        color: '#ff085DFF'
      })
      ListItemGroup( { header: this.itemHead( '开发语言 2')}) {
        // 循环渲染分组 开发语言 2 的 ListItem
        ForEach(this.devLanguages2, (item: Language) => {
          ListItem() {
            Row() {
              Image(item.icon).width(20).height(20).margin(10)
              Text(item.name).fontSize(10)
            }
            .width('100%')
            .justifyContent(FlexAlign.Start)
          }
        })
      }
      .divider({
        strokeWidth: 1,
        startMargin: 40,
        endMargin: 10,
        color: '#ff085DFF'
      })
    }
    .backgroundColor('#FFF1F3F5')
    .alignListItem(ListItemAlign.Start)
  }
}

效果:

在这里插入图片描述

如果多个 ListItemGroup 结构类似,可以将多个分组的数据组成数组,然后使用 ForEach 对多个分组进行循环渲染。

@Component
struct ListTest {

  @Builder itemHead(text: string) {
    // 列表分组的头部组件
    Text(text)
      .fontSize(10)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  languagesGroups: object[] = [
    {
      title: '开发语言 1',
      languages: [
        new Language($r("app.media.icon"), 'Kotlin'),
        new Language($r("app.media.icon"), 'TypeScript'),
        new Language($r('app.media.icon'), 'ArkTS'),
        new Language($r('app.media.icon'), 'Python'),
      ],
    },
    {
      title: '开发语言 2',
      languages: [
        new Language($r('app.media.icon'), 'Java'),
        new Language($r('app.media.icon'), 'JavaScript'),
        new Language($r('app.media.icon'), 'C++'),
        new Language($r('app.media.icon'), 'C#'),
      ],
    }
  ]
  
  build() {
    List() {
      // 循环渲染 ListItemGroup,languagesGroups 为多个分组开大语言 languages 和标题 title 的数据集合
      ForEach(this.languagesGroups, item => {
        ListItemGroup({ header: this.itemHead(item.title) }) {
          // 循环渲染 ListItem
          ForEach(item.languages, language => {
            ListItem() {
              Row() {
                Image(language.icon).width(20).height(20).margin(10)
                Text(language.name).fontSize(10)
              }
              .width('100%')
              .justifyContent(FlexAlign.Start)
            }
          })
        }
        .divider({
          strokeWidth: 1,
          startMargin: 40,
          endMargin: 10,
          color: '#ff085DFF'
        })
      })
    }
    .backgroundColor('#FFF1F3F5')
    .alignListItem(ListItemAlign.Start)
  }
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐