鸿蒙HarmonyOS实战-ArkUI组件(List)

🚀一、List

🔎1.概述

列表是一种非常有用且功能强大的容器,它常用于呈现同类型或多类型数据集合,例如图片、文本、音乐、通讯录、购物清单等。列表对于显示大量内容而不耗费过多空间和内存是非常有帮助的,因为当列表项数量超过屏幕大小时,可以自动提供滚动功能。这使得列表成为构建结构化、可滚动信息的理想容器。

使用列表可以轻松、高效地显示信息。使用List组件,可以按垂直或水平方向线性排列子组件,这些子组件可以是单个视图,也可以使用ForEach迭代一组行或列,或混合任意数量的单个视图和ForEach结构,构建一个灵活的列表。同时,List组件支持使用条件渲染、循环渲染、懒加载等渲染控制方式生成子组件,使得列表变得更加灵活和高效。

列表是一种非常实用的容器,适用于呈现各种类型的数据集合,并且可以为用户提供高效、流畅的滚动浏览体验。

🔎2.布局与约束

ListItemGroup是一个用于列表数据分组展示的组件,它的子组件也是ListItem。

  • ListItem是单个列表项的表示,每个ListItem可以包含一个单独的子组件,用于更详细的展示该列表项的内容。

  • ListItemGroup可以通过增加、删除子组件或调整子组件的位置,来展示不同的分组数据。同时,你也可以扩展ListItem和ListItemGroup,添加更多的属性和方法,以适配不同的使用场景。

🦋2.1 布局

1、垂直滚动列表

2、水平滚动列表(左:单行;右:多行)

🦋2.2 约束

1、列表的主轴与交叉轴

如果List组件主轴或交叉轴方向设置了尺寸,则其对应方向上的尺寸为设置值。

2、一个垂直列表B没有设置高度时,其父组件A高度为200vp,若其所有子组件C的高度总和为150vp,则此时列表B的高度为150vp。

3、同样是没有设置高度的垂直列表B,其父组件A高度为200vp,若其所有子组件C的高度总和为300vp,则此时列表B的高度为200vp。

🔎3.开发布局

🦋3.1 设置主轴方向

复制代码
List() {
  ...
}
.listDirection(Axis.Horizontal)

List组件默认主轴方向是垂直的,可以自动构建垂直滚动列表,无需手动设置。如果需要构建水平滚动列表,只需要将List组件的listDirection属性设置为Axis.Horizontal即可。需要注意的是,listDirection属性的默认值为Axis.Vertical,即默认情况下List组件的主轴方向是垂直方向。

更多鸿蒙最新技术知识点,请关注作者博客:https://t.doruo.cn/14DjR1rEY

🦋3.2 设置交叉轴布局

List组件的交叉轴布局可以通过lanes和alignListItem属性进行设置。lanes属性用于确定交叉轴排列的列表项数量,alignListItem用于设置子组件在交叉轴方向的对齐方式。一般情况下,List组件的lanes属性被用于在不同尺寸的设备上自适应构建不同行数或列数的列表。lanes属性的取值类型为"number | LengthConstrain",即整数或者LengthConstrain类型。

复制代码
List() {
  ...
}
.lanes(2)

List() {
  ...
}
.lanes({ minLength: 200, maxLength: 300 })

List() {
  ...
}
.alignListItem(ListItemAlign.Center)
  • lanes:设置列数
  • alignListItem:设置对齐方式

🔎4.案例

🦋4.1 在列表中显示数据

复制代码
@Entry
@Component
struct Index {
  build() {
    List() {
      ListItem() {
        Text('北京').fontSize(24)
      }

      ListItem() {
        Text('杭州').fontSize(24)
      }

      ListItem() {
        Text('上海').fontSize(24)
      }
    }
    .backgroundColor('#FFF1F3F5')
    .alignListItem(ListItemAlign.Center)
  }
}
复制代码
@Entry
@Component
struct Index {
  build() {
    List() {
      ListItem() {
        Row() {
          Image($r('app.media.app_icon'))
            .width(40)
            .height(40)
            .margin(10)

          Text('小明')
            .fontSize(20)
        }
      }

      ListItem() {
        Row() {
          Image($r('app.media.app_icon'))
            .width(40)
            .height(40)
            .margin(10)

          Text('小红')
            .fontSize(20)
        }
      }
    }
  }
}

🦋4.1 迭代列表内容

复制代码
import util from '@ohos.util';

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

  constructor(name: string, icon: Resource) {
    this.name = name;
    this.icon = icon;
  }
}
@Entry
@Component
struct Index {
  private contacts = [
    new Contact('小明', $r("app.media.app_icon")),
    new Contact('小红', $r("app.media.app_icon")),
    new Contact('张三', $r("app.media.app_icon")),
    new Contact('李四', $r("app.media.app_icon")),
  ]

  build() {
    List() {
      ForEach(this.contacts, (item: Contact) => {
        ListItem() {
          Row() {
            Image(item.icon)
              .width(40)
              .height(40)
              .margin(10)
            Text(item.name).fontSize(20)
          }
          .width('100%')
          .justifyContent(FlexAlign.Start)
        }
      }, item => item.key+item.name)
    }
    .width('100%')
  }
}

🦋4.3 自定义列表样式

☀️4.3.1 内容间距
复制代码
List({ space: 10 }) {
  ...
}
☀️4.3.2 添加分隔线
复制代码
List() {
  ...
}
.divider({
  strokeWidth: 1,
  startMargin: 60,
  endMargin: 10,
  color: '#ffe9f0f0'
})
☀️4.3.3 添加滚动条
复制代码
List() {
  ...
}
.scrollBar(BarState.Auto)
☀️4.3.4 分组列表

1、直接手动分钟

复制代码
@Component
struct ContactsList {
  ...
  
  @Builder itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  build() {
    List() {
      ListItemGroup({ header: this.itemHead('A') }) {
        // 循环渲染分组A的ListItem
        ...
      }
      ...

      ListItemGroup({ header: this.itemHead('B') }) {
        // 循环渲染分组B的ListItem
        ...
      }
      ...
    }
  }
}

2、遍历分组

复制代码
contactsGroups: object[] = [
  {
    title: 'A',
    contacts: [
      new Contact('艾佳', $r('app.media.iconA')),
      new Contact('安安', $r('app.media.iconB')),
      new Contact('Angela', $r('app.media.iconC')),
    ],
  },
  {
    title: 'B',
    contacts: [
      new Contact('白叶', $r('app.media.iconD')),
      new Contact('伯明', $r('app.media.iconE')),
    ],
  },
  ...
]

List() {
  // 循环渲染ListItemGroup,contactsGroups为多个分组联系人contacts和标题title的数据集合
  ForEach(this.contactsGroups, item => {
    ListItemGroup({ header: this.itemHead(item.title) }) {
      // 循环渲染ListItem
      ForEach(item.contacts, contact => {
        ListItem() {
          ...
        }
      }, item => item.key)
    }
    ...
  })
}
☀️4.3.5 添加粘性标题(官方)
复制代码
@Component
struct ContactsList {
  // 定义分组联系人数据集合contactsGroups数组
  ...
 
  @Builder itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  build() {
    List() {
      // 循环渲染ListItemGroup,contactsGroups为多个分组联系人contacts和标题title的数据集合
      ForEach(this.contactsGroups, item => {
        ListItemGroup({ header: this.itemHead(item.title) }) {
          // 循环渲染ListItem
          ForEach(item.contacts, contact => {
            ListItem() {
              ...
            }
          }, item => item.key)
        }
        ...
      })
    }
    .sticky(StickyStyle.Header)  // 设置吸顶,实现粘性标题效果
  }
}

image

☀️4.3.6 控制滚动位置(官方)
复制代码
private listScroller: Scroller = new Scroller();
Stack({ alignContent: Alignment.BottomEnd }) {
  // 将listScroller用于初始化List组件的scroller参数,完成listScroller与列表的绑定。
  List({ space: 20, scroller: this.listScroller }) {
    ...
  }
  ...

  Button() {
    ...
  }
  .onClick(() => {
    // 点击按钮时,指定跳转位置,返回列表顶部
    this.listScroller.scrollToIndex(0)
  })
  ...
}
☀️4.3.7 响应滚动位置(官方)
复制代码
...
const alphabets = ['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];

@Entry
@Component
struct ContactsList {
  @State selectedIndex: number = 0;
  private listScroller: Scroller = new Scroller();
  ...

  build() {
    Stack({ alignContent: Alignment.End }) {
      List({ scroller: this.listScroller }) {
        ...
      }
      .onScrollIndex((firstIndex: number) => {
          this.selectedIndex = firstIndex
        // 根据列表滚动到的索引值,重新计算对应联系人索引栏的位置this.selectedIndex
        ...
      })
      ...
      // 字母表索引组件
      AlphabetIndexer({ arrayValue: alphabets, selected: 0 })
        .selected(this.selectedIndex)
      ...
    }
  }
}
☀️4.3.8 响应列表项侧滑(官方)
复制代码
@Entry
@Component
struct MessageList {
  @State messages: object[] = [
    // 初始化消息列表数据
    ...
  ];

  @Builder itemEnd(index: number) {
    // 侧滑后尾端出现的组件
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.ic_public_delete_filled'))
        .width(20)
        .height(20)
    }
    .onClick(() => {
      this.messages.splice(index, 1);
    })
    ...
  }
  build() {
    ...
      List() {
        ForEach(this.messages, (item, index) => {
          ListItem() {
            ...
          }
          .swipeAction({ end: this.itemEnd.bind(this, index) }) // 设置侧滑属性
        }, item => item.id.toString())
      }
    ...
  }
}
☀️4.3.9 给列表项添加标记(官方)
复制代码
Badge({
  count: 1,
  position: BadgePosition.RightTop,
  style: { badgeSize: 16, badgeColor: '#FA2A2D' }
}) {
  // Image组件实现消息联系人头像
  ...
}
...

☀️4.3.10 下拉刷新与上拉加载(官方)

案例:https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_NewsDataLoad?ha_linker=eyJ0cyI6MTcwMTY1Njg2NjU1MywiaWQiOiJjNDFjZDY5NTVjZDQ0NTBjZWE1ZWQxOTQ0MzZkODJkNSJ9

第三方组件:https://gitee.com/openharmony-sig/PullToRefresh

更多鸿蒙最新技术知识点,请关注作者博客:https://t.doruo.cn/14DjR1rEY

☀️4.3.11 编辑列表(官方)

1、定义列表项数据结构

复制代码
import util from '@ohos.util';

export class ToDo {
  key: string = util.generateRandomUUID(true);
  name: string;

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

2、初始化数据

复制代码
@State toDoData: ToDo[] = [];
private availableThings: string[] = ['读书', '运动', '旅游', '听音乐', '看电影', '唱歌'];

3、构建列表布局和列表项

复制代码
List({ space: 10 }) {
  ForEach(this.toDoData, (toDoItem) => {
    ListItem() {
      ...
    }
  }, toDoItem => toDoItem.key)
}

4、响应用户确定新增事件,更新列表数据

复制代码
Text('+')
  .onClick(() => {
    TextPickerDialog.show({
      range: this.availableThings,
      onAccept: (value: TextPickerResult) => {
         this.toDoData.push(new ToDo(this.availableThings[value.index])); // 新增列表项数据toDoData
      },
    })
  })
☀️4.3.12 删除列表项(官方)

1、以待办列表为例,通过监听列表项的长按事件,当用户长按列表项时,进入编辑模式。

复制代码
// ToDoListItem.ets

Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
  ...
}
.gesture(
GestureGroup(GestureMode.Exclusive,
  LongPressGesture()
    .onAction(() => {
      if (!this.isEditMode) {
        this.isEditMode = true; //进入编辑模式
        this.selectedItems.push(this.toDoItem); // 记录长按时选中的列表项
      }
    })
  )
)

2、在待办列表中,通过勾选框的勾选或取消勾选,响应用户勾选列表项变化,记录所有选择的列表项。

复制代码
// ToDoListItem.ets

if (this.isEditMode) {
  Checkbox()
    .onChange((isSelected) => {
      if (isSelected) {
        this.selectedItems.push(this.toDoItem) // 勾选时,记录选中的列表项
      } else {
        let index = this.selectedItems.indexOf(this.toDoItem)
        if (index !== -1) {
          this.selectedItems.splice(index, 1) // 取消勾选时,则将此项从selectedItems中删除
        }
      }
    })
    ...
}

3、需要响应用户点击删除按钮事件,删除列表中对应的选项

复制代码
// ToDoList.ets

Button('删除')
  .onClick(() => {
    // 删除选中的列表项对应的toDoData数据
    let leftData = this.toDoData.filter((item) => {
      return this.selectedItems.find((selectedItem) => selectedItem !== item);
    })

    this.toDoData = leftData;
    this.isEditMode = false;
  })
  ...

🦋4.4 长列表的处理

循环渲染适用于短列表。但当构建具有大量列表项的长列表时,直接采用循环渲染方式会一次性加载所有的列表元素,导致页面启动时间过长,影响用户体验。因此,推荐使用数据懒加载(LazyForEach)方式实现按需迭代加载数据,从而提升列表性能。具体实现可参考数据懒加载章节中的示例。

当使用懒加载方式渲染列表时,为了更好的列表滚动体验,并减少列表滑动时出现白块,List组件提供了cachedCount参数。该参数用于设置列表项缓存数量,只在懒加载LazyForEach中生效。

复制代码
List() {
  LazyForEach(this.dataSource, item => {
    ListItem() {
      ...
    }
  })
}.cachedCount(3)

🚀写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 更多鸿蒙最新技术知识点,请关注作者博客:https://t.doruo.cn/14DjR1rEY
相关推荐
别说我什么都不会12 小时前
ohos.net.http请求HttpResponse header中set-ccokie值被转成array类型
网络协议·harmonyos
码是生活13 小时前
鸿蒙开发排坑:解决 resourceManager.getRawFileContent() 获取文件内容为空问题
前端·harmonyos
鸿蒙场景化示例代码技术工程师13 小时前
基于Canvas实现选座功能鸿蒙示例代码
华为·harmonyos
小脑斧爱吃鱼鱼14 小时前
鸿蒙项目笔记(1)
笔记·学习·harmonyos
鸿蒙布道师15 小时前
鸿蒙NEXT开发对象工具类(TS)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
zhang10620915 小时前
HarmonyOS 基础组件和基础布局的介绍
harmonyos·基础组件·基础布局
马剑威(威哥爱编程)15 小时前
在HarmonyOS NEXT 开发中,如何指定一个号码,拉起系统拨号页面
华为·harmonyos·arkts
GeniuswongAir16 小时前
Flutter极速接入IM聊天功能并支持鸿蒙
flutter·华为·harmonyos
90后的晨仔20 小时前
鸿蒙ArkUI框架中的状态管理
harmonyos
别说我什么都不会1 天前
OpenHarmony 5.0(API 12)关系型数据库relationalStore 新增本地数据变化监听接口介绍
api·harmonyos