HarmonyOS 6.1 实战:List 高性能列表与分组吸顶详解
前言List是 HarmonyOS ArkUI 中最常用的容器之一专为长列表场景设计内置虚拟化渲染只渲染可见区域的列表项。配合ListItemGroup可实现带分组标题的联系人列表搭配.sticky(StickyStyle.Header)即可让分组标题吸顶。本文完整演示这些能力。运行效果初始状态全部联系人滚动至 D 组分组标题吸顶核心 API 一览API说明List高性能列表容器自动虚拟化渲染ListItem列表中的单个条目ListItemGroup分组容器包含 header 和若干 ListItem.sticky(StickyStyle.Header)分组 header 滚动时吸顶List 的属性.divider({})列表分隔线支持配置宽度、颜色、边距.scrollBar(BarState)控制滚动条显示状态.edgeEffect(EdgeEffect)边缘弹性效果注意吸顶属性是.sticky(StickyStyle.Header)不是.stickyStyle()后者不存在。完整示例代码interface Contact { name: string phone: string avatar: string } interface ContactGroup { letter: string contacts: Contact[] } Entry Component struct Index { private groups: ContactGroup[] [ { letter: A, contacts: [ { name: 阿里木, phone: 138****0001, avatar: }, { name: 安娜, phone: 139****0002, avatar: }, ] }, { letter: B, contacts: [ { name: 白晓雪, phone: 136****0003, avatar: }, { name: 贝贝, phone: 137****0004, avatar: }, { name: 博文, phone: 135****0005, avatar: }, ] }, { letter: C, contacts: [ { name: 陈小明, phone: 133****0006, avatar: }, { name: 崔思思, phone: 180****0007, avatar: }, ] }, { letter: D, contacts: [ { name: 邓磊, phone: 181****0008, avatar: }, { name: 丁一, phone: 182****0009, avatar: }, { name: 董芳, phone: 183****0010, avatar: }, ] }, { letter: E, contacts: [ { name: 峨眉峰, phone: 184****0011, avatar: }, { name: 恩典, phone: 185****0012, avatar: }, ] }, ] Builder headerBuilder(letter: string) { Row() { Text(letter) .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor(#0066ff) .width(36) .textAlign(TextAlign.Center) Text(letter 组) .fontSize(13) .fontColor(#888888) } .width(100%) .height(40) .padding({ left: 16, right: 16 }) .backgroundColor(#f0f5ff) .border({ width: { bottom: 1 }, color: #d0deff }) } Builder contactItem(contact: Contact) { Row({ space: 14 }) { // Avatar circle Column() { Text(contact.avatar).fontSize(22) } .width(48) .height(48) .backgroundColor(#e8f0ff) .borderRadius(24) .justifyContent(FlexAlign.Center) // Name phone Column({ space: 4 }) { Text(contact.name) .fontSize(16) .fontColor(#222222) .fontWeight(FontWeight.Medium) Text(contact.phone) .fontSize(13) .fontColor(#888888) } .layoutWeight(1) .alignItems(HorizontalAlign.Start) // Call icon Text() .fontSize(20) .padding({ right: 4 }) } .width(100%) .height(72) .padding({ left: 16, right: 16 }) .backgroundColor(#ffffff) } build() { Column({ space: 0 }) { // TopBar Row() { Text(通讯录) .fontSize(20) .fontWeight(FontWeight.Bold) .fontColor(#1a1a1a) .layoutWeight(1) .padding({ left: 16 }) Text() .fontSize(22) .padding({ right: 16 }) } .width(100%) .height(56) .backgroundColor(#ffffff) .border({ width: { bottom: 1 }, color: #f0f0f0 }) // List index bar Row() { // Main list with sticky group headers List() { ForEach(this.groups, (group: ContactGroup) { ListItemGroup({ header: this.headerBuilder(group.letter), space: 0 }) { ForEach(group.contacts, (contact: Contact) { ListItem() { this.contactItem(contact) } }) } }) } .sticky(StickyStyle.Header) // 分组 header 吸顶 .scrollBar(BarState.On) .divider({ strokeWidth: 1, color: #f0f0f0, startMargin: 70 }) .width(100%) .layoutWeight(1) .backgroundColor(#fff) // 右侧字母索引条 Column({ space: 2 }) { ForEach([A, B, C, D, E], (letter: string) { Text(letter) .fontSize(12) .fontColor(#0066ff) .fontWeight(FontWeight.Medium) .width(28) .height(28) .textAlign(TextAlign.Center) .backgroundColor(#e8f0ff) .borderRadius(14) }) } .width(36) .padding({ top: 8, bottom: 8, right: 4 }) .justifyContent(FlexAlign.Center) } .width(100%) .layoutWeight(1) } .width(100%) .height(100%) .backgroundColor(#f5f5f5) } }关键知识点1. 吸顶属性名称.sticky() 不是 .stickyStyle()这是高频踩坑点。正确的 API 是 List 的.sticky(StickyStyle.Header)List() { ForEach(groups, (group) { ListItemGroup({ header: headerBuilder }) { ... } }) } .sticky(StickyStyle.Header) // 正确 // .stickyStyle(StickyStyle.Header) // 错误该方法不存在StickyStyle的可选值值效果StickyStyle.None不吸顶StickyStyle.HeaderListItemGroup 的 header 吸顶StickyStyle.FooterListItemGroup 的 footer 吸底2. ListItemGroup 提供 header 和 footerListItemGroup({ header: this.myHeader, // Builder 引用吸顶显示 footer: this.myFooter, // Builder 引用吸底显示 space: 8, // 列表项之间的间距 }) { ForEach(items, (item) { ListItem() { ... } }) }3. List 的分隔线.divider()比手动给每个ListItem加border简洁得多List() .divider({ strokeWidth: 1, color: #f0f0f0, startMargin: 70, // 左侧缩进避开头像 endMargin: 16, // 右侧间距 })4. 虚拟化渲染与性能List自动只渲染可视区域内的列表项超出屏幕的项被回收复用。这意味着即使数据有几千条渲染性能也不会劣化。相比Column ForEach长列表必须用 List。5. scrollBar 控制滚动条.scrollBar(BarState.Auto) // 滚动时显示停止后隐藏默认 .scrollBar(BarState.On) // 始终显示 .scrollBar(BarState.Off) // 永不显示常见列表场景场景关键 API带字母吸顶的通讯录ListItemGroup.sticky(StickyStyle.Header)可侧滑删除的列表ListItem.swipeAction({})拖拽排序List.editMode(true)下拉刷新RefreshList配合分隔线.divider({ strokeWidth, color, startMargin })小结List是高性能长列表容器内置虚拟化长列表不要用Column ForEachListItemGroup实现分组header/footer 可通过Builder自定义吸顶用.sticky(StickyStyle.Header)注意不是.stickyStyle().divider()统一控制分隔线比在每个 ListItem 手动加边框更简洁右侧字母索引条需要配合Scroller的.scrollToIndex()才能跳转可作为进阶扩展