当前位置: 首页 > news >正文

鸿蒙原生ArkTS布局之ListItemGroup分组列表通讯录实现

鸿蒙原生 ArkTS 布局方式之 List 分组(Group):带标题的分组列表 —— 通讯录风格实现详解




一、引言

在移动端应用开发中,分组列表(Section List)是最常见的 UI 模式之一。从通讯录到商品分类页,从设置菜单到好友列表,分组列表几乎无处不在。其核心特征是将数据按首字母、时间或类别等维度分组展示,每一组拥有一个可吸顶的标题头,帮助用户快速定位。

HarmonyOS NEXT 的原生 UI 框架ArkUI提供了声明式的 ArkTS 语言布局体系。其中,List容器配合ListItemGroup子容器构成了实现分组列表的标准方案,官方将其归纳为「List 分组(Group):带标题的分组列表」,是 ArkTS 六大基础布局之一。

本文将围绕一个通讯录风格的分组列表示例,从数据模型、界面布局到完整代码,逐层剖析这一布局的实现原理。


二、HarmonyOS NEXT 与 ArkTS 布局体系概述

2.1 从 Java 到 ArkTS 的演进

HarmonyOS 的应用开发语言经历了从 Java/JS 双框架(1.0–2.0)到 eTS(3.0),再到 HarmonyOS NEXT(API 11+)全面采用ArkTS的演进。ArkTS 是 TypeScript 的超集,增加了@Component / @Entry / @Builder / @State等装饰器体系,实现声明式 + 数据驱动的 UI 开发范式。

2.2 六大基础布局方式

ArkUI 官方归纳六种核心布局,覆盖绝大多数应用场景:线性布局(Row/Column)、层叠布局(Stack)、弹性布局(Flex)、相对布局(RelativeContainer)、滚动布局(Scroll)、列表布局(List)和网格布局(Grid/GridItem)。本文聚焦的正是列表布局的高阶用法——带标题的分组列表(ListItemGroup)

2.3 为什么选择 ListItemGroup?

在 iOS 开发中,UITableView通过section概念天然支持分组;在 Android 中,RecyclerView配合ItemDecoration可以实现分组效果。而在 ArkUI 中,ListItemGroup就是实现分组语义的第一等公民。

与直接使用多个List拼接或用Divider手动模拟分组相比,ListItemGroup的优势:

  1. 语义明确:代码结构天然反映List > ListItemGroup > ListItem层级;
  2. 标题吸顶:通过sticky属性一键开启,无需手动计算滚动偏移;
  3. 性能优化:继承List的懒加载能力,不会一次性渲染所有分组;
  4. 交互丰富:支持侧滑删除(swipeAction)、拖拽排序等高级交互。

三、示例应用概览

3.1 最终效果

模拟手机通讯录:顶部标题栏「通讯录」,列表按首字母分组(★ 置顶 + A~Z),每组上方有灰色标题栏,滚动时标题吸顶固定。每个联系人条目包含圆形头像(首字+随机色)、姓名、手机号、拨号图标。点击联系人弹出 Toast 提示。

3.2 数据模型

interfaceContactInfo{name:string;phone:string;}interfaceGroupData{title:string;contacts:ContactInfo[];}

整个列表的数据源是一个GroupData[]数组。本示例定义了10 个分组28 个联系人,涵盖 ★(置顶)、A、B、C、D、G、H、L、W、Z 等字母,模拟真实通讯录中「并非每个字母都有联系人」的情况。

3.3 组件树结构

Column ← 页面根容器 ├── Row ← 顶部标题栏 └── List ← 滚动列表(sticky header) ├── ListItemGroup (A) ← 分组 A(header: "A") │ ├── ListItem ← 联系人「阿杰」 │ ├── ListItem ← 联系人「艾米」 │ └── ListItem ← 联系人「安琪拉」 ├── ListItemGroup (B) ← 分组 B └── ListItemGroup (Z) ← 分组 Z

这个层级清晰映射了「列表 → 多个分组 → 每个分组下多个条目」的数据关系。


四、核心代码逐段解析

4.1 导入语句与数据接口

import{promptAction}from'@kit.ArkUI';interfaceContactInfo{name:string;phone:string;}interfaceGroupData{title:string;contacts:ContactInfo[];}

@kit.ArkUI是 HarmonyOS NEXT 的统一 SDK 包入口,替代了旧版本的多个分散导入(如@ohos.promptAction)。promptAction提供了 Toast 弹窗等轻量交互能力。

ContactInfoGroupData两个接口是数据驱动的基石。在真实工程中,这些接口通常由后端 API 返回的数据结构定义,或由数据库 ORM 映射而来。

4.2 @State 数据源与 @Builder 头像

@StategroupDataList:GroupData[]=[/* 10 个分组、28 个联系人 */];@BuildercontactAvatar(name:string){Text(name.charAt(0)).fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Bold).width(44).height(44).borderRadius(22).backgroundColor(this.getAvatarColor(name)).textAlign(TextAlign.Center)}

@State装饰器是 ArkTS 状态管理的核心——被@State修饰的变量变化时,框架自动重渲染相关组件。@Builder装饰的方法是一个可复用的 UI 片段构建器,支持参数传递。此处构建了 44×44 的圆形头像,首字白色粗体居中,背景色根据姓名 Unicode 哈希取模从 10 种预设颜色中选取。

4.3 Build 方法:核心布局逻辑

build(){Column(){Row(){Text('通讯录')...}// 顶部标题List({space:0}){ForEach(this.groupDataList,(groupItem)=>{ListItemGroup({header:this.groupHeaderBuilder(groupItem.title)}){ForEach(groupItem.contacts,(contact)=>{ListItem(){Row(){this.contactAvatar(contact.name)Column(){Text(contact.name)...Text(contact.phone)...}Image($r('app.media.startIcon'))...}}})}})}.sticky(StickyStyle.Header)// 吸顶效果}}

核心是双重 ForEach 循环:外层遍历groupDataList生成ListItemGroup,内层遍历groupItem.contacts生成ListItemForEach的第三个参数是键值生成函数,用于框架追踪列表项变化。

4.4 吸顶属性与交互

List({space:0}).sticky(StickyStyle.Header)

StickyStyle枚举有三个值:None(不吸顶)、Header(标题吸顶)、Footer(底部吸底)。设置Header后,ArkUI 自动处理所有ListItemGroupheader 的位置计算。

点击交互使用promptAction.showToast实现 Toast 提示:

onContactClick(contact:ContactInfo):void{promptAction.showToast({message:`拨打电话:${contact.name}(${contact.phone})`,duration:2000});}

五、ListItemGroup 的关键属性详解

5.1 header 属性

headerListItemGroup最重要的属性,它接收一个@Builder或自定义构建器,用于渲染分组的标题头。

ListItemGroup({header:this.groupHeaderBuilder(groupItem.title)}){// 分组内的列表项}

groupHeaderBuilder的实现(36px 高、浅灰背景的文字区域):

5.2 divider 属性

.divider({strokeWidth:0,startMargin:0,endMargin:0})

divider控制分组之间的分隔线样式。本示例中隐藏了组间分割线,而每个ListItem内部通过.border设置 0.5 像素的底部分割线,形成「组内条目有分割线、组间无额外分割线」的效果。

5.3 children(插槽内容)

ListItemGroup的花括号内容只能放置ListItem组件(或ForEach/LazyForEach生成的ListItem)。


六、BorderOptions 在 API 24 中的正确使用

在 API 24 中,BorderOptions接口的定义发生了变化——不能再将bottomleft等边属性直接放在BorderOptions顶层,而需嵌套在widthcolor内部:

// ❌ 编译错误:'bottom' does not exist in type 'BorderOptions'.border({bottom:{width:0.5,color:'#E8E8E8'}})// ✅ 正确写法.border({width:{bottom:0.5},color:{bottom:'#E8E8E8'}})

BorderOptions的类型定义(简化):

interfaceBorderOptions{width?:EdgeWidths|Dimension;// EdgeWidths = { left?, right?, top?, bottom? }color?:EdgeColors|ResourceColor;// EdgeColors = { left?, right?, top?, bottom? }style?:EdgeStyles|BorderStyle;radius?:BorderRadiuses|Dimension;}

设置单边边框用{ width: { bottom: 1 }, color: { bottom: '#ccc' } },设置四边统一边框用{ width: 1, color: '#ccc' },设置各边不同边框用{ width: { left: 1, right: 2 }, color: { left: 'red', right: 'blue' } }


七、性能考量:ForEach 与 LazyForEach

本示例使用ForEach,它会一次性渲染所有 ListItem。数据量较少时没有问题。当数据量达数百或数千时,应使用LazyForEach替代:

List(){LazyForEach(this.dataSource,(item:GroupData)=>{ListItemGroup({header:...}){LazyForEach(item.contacts,(contact:ContactInfo)=>{ListItem(){...}},(contact)=>contact.phone)}},(item)=>item.title)}.sticky(StickyStyle.Header)

LazyForEach需配合IDataSource接口使用,按需创建、回收复用,大幅降低内存占用。


八、扩展建议

  • 字母索引条:使用AlphabetIndexer组件配合ListController.scrollToIndex实现右侧字母快速跳转
  • 侧滑操作ListItemswipeAction属性支持添加侧滑删除/置顶菜单
  • 数据持久化:实际工程中建议使用LazyForEach+ 数据源接口,从RelationalStore或云端获取数据

九、完整代码清单

以下为ContactGroupList.ets的完整代码(已包含详尽的中文注释),完整文件位于entry/src/main/ets/pages/ContactGroupList.ets

import{promptAction}from'@kit.ArkUI';interfaceContactInfo{name:string;phone:string;}interfaceGroupData{title:string;contacts:ContactInfo[];}@Entry@Componentstruct ContactGroupList{@StategroupDataList:GroupData[]=[{title:'★',contacts:[{name:'张三',phone:'138****1234'},{name:'李四',phone:'139****5678'}]},{title:'A',contacts:[{name:'阿杰',phone:'136****1111'},{name:'艾米',phone:'137****2222'},{name:'安琪拉',phone:'135****3333'}]},{title:'B',contacts:[{name:'白杨',phone:'150****4444'},{name:'本杰明',phone:'151****5555'}]},{title:'C',contacts:[{name:'陈晨',phone:'152****6666'},{name:'程菲',phone:'153****7777'},{name:'柴远',phone:'155****8888'}]},{title:'D',contacts:[{name:'邓超',phone:'156****9999'},{name:'董洁',phone:'157****0000'}]},{title:'G',contacts:[{name:'高天',phone:'158****1111'},{name:'郭靖',phone:'159****2222'}]},{title:'H',contacts:[{name:'韩梅梅',phone:'170****3333'},{name:'何炅',phone:'171****4444'}]},{title:'L',contacts:[{name:'李华',phone:'172****5555'},{name:'刘洋',phone:'173****6666'},{name:'林娜',phone:'174****7777'}]},{title:'W',contacts:[{name:'王伟',phone:'175****8888'},{name:'吴芳',phone:'176****9999'},{name:'魏明',phone:'177****0000'}]},{title:'Z',contacts:[{name:'张宇',phone:'178****1111'},{name:'赵敏',phone:'179****2222'},{name:'周杰',phone:'180****3333'}]}];onContactClick(contact:ContactInfo):void{promptAction.showToast({message:`拨打电话:${contact.name}(${contact.phone})`,duration:2000});}@BuildercontactAvatar(name:string){Text(name.charAt(0)).fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Bold).width(44).height(44).borderRadius(22).backgroundColor(this.getAvatarColor(name)).textAlign(TextAlign.Center)}getAvatarColor(name:string):ResourceColor{constcolors:ResourceColor[]=['#FF6B81','#5B8FF9','#F6BD16','#E8684A','#2FC25B','#9F7EEA','#F4606C','#20B2AA','#FF7F50','#9370DB'];letindex=0;for(leti=0;i<name.length;i++)index=(index+name.charCodeAt(i))%colors.length;returncolors[index];}build(){Column(){Row(){Text('通讯录').fontSize(24).fontWeight(FontWeight.Bold).fontColor('#333333')}.width('100%').padding({left:16,top:12,bottom:8}).backgroundColor('#F7F7F7')List({space:0}){ForEach(this.groupDataList,(groupItem:GroupData)=>{ListItemGroup({header:this.groupHeaderBuilder(groupItem.title)}){ForEach(groupItem.contacts,(contact:ContactInfo)=>{ListItem(){Row(){this.contactAvatar(contact.name)Column(){Text(contact.name).fontSize(16).fontColor('#333333').fontWeight(FontWeight.Medium)Text(contact.phone).fontSize(13).fontColor('#999999').margin({top:3})}.alignItems(HorizontalAlign.Start).layoutWeight(1).margin({left:12})Image($r('app.media.startIcon')).width(24).height(24).objectFit(ImageFit.Contain).opacity(0.4)}.width('100%').height(64).padding({left:16,right:16}).alignItems(VerticalAlign.Center).onClick(()=>{this.onContactClick(contact);})}.border({width:{bottom:0.5},color:{bottom:'#E8E8E8'}})},(contact:ContactInfo)=>contact.phone)}.divider({strokeWidth:0,startMargin:0,endMargin:0})},(groupItem:GroupData)=>groupItem.title)}.sticky(StickyStyle.Header).width('100%').height('100%').backgroundColor('#FFFFFF').edgeEffect(EdgeEffect.Spring)}.width('100%').height('100%').backgroundColor('#F7F7F7')}@BuildergroupHeaderBuilder(title:string){Text(title).fontSize(15).fontColor('#666666').fontWeight(FontWeight.Bold).width('100%').height(36).padding({left:16}).backgroundColor('#F0F0F0').textAlign(TextAlign.Start)}}

十、总结

本文围绕 HarmonyOS NEXT 的List 分组(ListItemGroup)布局方式,以一个完整的通讯录风格应用为例,介绍了:

  1. 数据模型设计:用ContactInfoGroupData两层接口组织分组数据;
  2. 组件层级结构List → ListItemGroup → ListItem的三层树形关系;
  3. 核心属性用法header(分组标题)、sticky(吸顶)、divider(分组分隔线);
  4. 交互与反馈onClick事件与showToast的组合使用;
  5. API 注意事项BorderOptions在 API 24 中的类型变化;
  6. 性能优化LazyForEach用于大数据量分组列表。

掌握List+ListItemGroup组合,就掌握了 HarmonyOS NEXT 应用中 80% 以上的列表页场景。从通讯录到商品分类,从好友列表到信息流,这种布局模式将是 ArkTS 开发工具箱中使用频率最高的利器。


本文代码基于 HarmonyOS NEXT API 24 编译验证。示例工程完整源码可在项目entry/src/main/ets/pages/ContactGroupList.ets路径下找到。

http://www.jsqmd.com/news/1082302/

相关文章:

  • 英雄联盟回放文件终极解析指南:ROFL播放器完整使用教程
  • 【VMware OVF导出黄金法则】:20年老司机亲授5大避坑指南与3种极速导出实战方案
  • MCP 协议如何助力企业实现 Agent 自动化
  • 如何永久保存你的微信回忆:WeChatMsg完整指南让聊天记录永不消失
  • Reloaded-II模块化架构的技术突破与系统稳定性优化
  • 终极抖音直播录制指南:如何搭建40+平台无人值守自动录制系统
  • 2026 变声器横评:4 款主流产品实测,新手入门避坑指南
  • 终极指南:如何用开源工具免费突破网盘限速,实现全平台高速下载
  • 树莓派3启动流程全解析:从OTP配置到USB启动实战
  • 树莓派config.txt遗产选项深度解析:从底层原理到裸机开发实战
  • 别光喊AI Agent,自己动手造一个!LangChain工作机制解析与实战入门
  • 实战指南:docker-wechatbot-webhook如何高效实现微信媒体文件自动保存
  • 3分钟极速激活:Windows和Office的完整免费解决方案
  • 库存管理:定义、工作原理、方法和示例
  • 告别VMware许可证费用:2024年最实用的5款免费替代工具,部署效率提升300%
  • 图的拉普拉斯矩阵特征值比:从正则图到一般图的Spielman猜想
  • 【C/C++】多线程竞争与线程池
  • MuleSoft企业级AI集成:打通LLM与SAP/Workday等核心系统的实战指南
  • 电信网关配置管理系统命令注入漏洞深度剖析与实战复现
  • 全套DFM审查流程与八大高频专属禁忌
  • 国产贴片机如何在中小批量SMT产线中找到“超车弯道”?
  • TVA在物理AI领域的决定性意义(3)
  • 【VMware vs VirtualBox终极选型指南】:20年虚拟化专家亲测的5大核心维度对比,90%开发者都选错了!
  • 5大技术方案深度解析:fanqienovel-downloader如何重构数字阅读体验
  • 磐创科技工业协议转换器介绍
  • 如何快速实现九大网盘高速下载?LinkSwift直链助手完整指南
  • 线上怎么办理出生证翻译件?办理出生证翻译件的流程是什么?
  • DLSS Swapper技术深度解析:游戏超采样版本管理的架构设计与实现
  • 如何永久保存微信聊天记录?这款开源神器让你的对话永不丢失
  • CVE-2024-27198漏洞深度剖析:从路径遍历到CI/CD供应链攻击