HarmonyOS 6.0 列表分组与吸顶索引
通讯录按字母分组、商品按类别分组、订单按日期分组——列表分组是高频需求。但分组不只是给数据排序加标题还有吸顶效果、侧边索引条、分组折叠、跨分组全选。每个功能单独做不难组合起来坑就来了。数据分组第一步是把扁平数据按字段分组。最常见的是按首字母分组interfaceContact{id:number;name:string;phone:string;pinyin:string;}interfaceGroupedContact{letter:string;contacts:Contact[];}privategroupByLetter(contacts:Contact[]):GroupedContact[]{letgroups:GroupedContact[][];letgroupMap:Recordstring,Contact[]{};for(leti0;icontacts.length;i){letfirstCharcontacts[i].pinyin.charAt(0).toUpperCase();if(groupMap[firstChar]undefined){groupMap[firstChar][];}groupMap[firstChar].push(contacts[i]);}letlettersObject.keys(groupMap).sort();for(leti0;iletters.length;i){groups.push({letter:letters[i],contacts:groupMap[letters[i]]});}returngroups;}先建一个首字母→联系人的映射再按字母排序输出分组。Object.keys返回的顺序不保证必须sort。按日期分组同理interfaceGroupedOrder{date:string;orders:Order[];}privategroupByDate(orders:Order[]):GroupedOrder[]{letgroups:GroupedOrder[][];letgroupMap:Recordstring,Order[]{};for(leti0;iorders.length;i){letdateStrthis.formatDate(orders[i].createTime);if(groupMap[dateStr]undefined){groupMap[dateStr][];}groupMap[dateStr].push(orders[i]);}letdatesObject.keys(groupMap).sort();dates.reverse();for(leti0;idates.length;i){groups.push({date:dates[i],orders:groupMap[dates[i]]});}returngroups;}日期分组通常是倒序——最新的在前。reverse()翻转排序结果。分组列表渲染用ListItemGroup实现分组渲染List(){ForEach(this.groupedContacts,(group:GroupedContact){ListItemGroup({header:this.groupHeader(group.letter),divider:{strokeWidth:1,color:#f0f0f0}}){ForEach(group.contacts,(contact:Contact){ListItem(){Row(){Text(contact.name).fontSize(16).fontColor(#333333)Text(contact.phone).fontSize(14).fontColor(#999999).margin({left:12})}.width(100%).padding({left:16,right:16,top:12,bottom:12})}},(contact:Contact)contact.id.toString())}},(group:GroupedContact)group.letter)}BuildergroupHeader(letter:string){Row(){Text(letter).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#666666)}.width(100%).padding({left:16,top:8,bottom:8}).backgroundColor(#f5f5f5)}ListItemGroup自带header和divider参数省去手动添加分组标题的麻烦。header接受Builder可以自定义样式。吸顶效果ListItemGroup的header默认随列表滚动而消失。要实现吸顶sticky header设sticky属性ListItemGroup({header:this.groupHeader(group.letter),divider:{strokeWidth:1,color:#f0f0f0}}){// 列表项}.sticky(StickyStyle.Header)StickyStyle.Header让header在滚动到顶部时粘住直到被下一个header推走。这是通讯录字母吸顶的标准实现。吸顶的header会覆盖在列表内容上方所以header要有背景色。透明的header会透出下面的内容看起来很怪。侧边字母索引通讯录右侧的A-Z快速跳转Stack(){List(){ForEach(this.groupedContacts,(group:GroupedContact){ListItemGroup({header:this.groupHeader(group.letter)}){ForEach(group.contacts,(contact:Contact){ListItem(){// 内容}},(contact:Contact)contact.id.toString())}.sticky(StickyStyle.Header)},(group:GroupedContact)group.letter)}.width(100%).height(100%)// 侧边索引Column(){ForEach(this.alphabet,(letter:string){Text(letter).fontSize(10).fontColor(this.activeLetterletter?#1a73e8:#666666).fontWeight(this.activeLetterletter?FontWeight.Bold:FontWeight.Normal).padding(2).onClick((){this.scrollToLetter(letter);})},(letter:string)letter)}.position({x:92%,y:60}).height(70%).justifyContent(FlexAlign.SpaceBetween)}alphabet是A-Z的字符串数组。点击字母时调用scrollToLetter滚动到对应分组。List滚动到指定位置用Scrollerprivatescroller:ScrollernewScroller();privateletterToIndex:Recordstring,number{};// 初始化映射aboutToAppear():void{for(leti0;ithis.groupedContacts.length;i){this.letterToIndex[this.groupedContacts[i].letter]i;}}// 滚动到指定字母privatescrollToLetter(letter:string):void{letindexthis.letterToIndex[letter];if(index!undefined){this.scroller.scrollToIndex(index);this.activeLetterletter;}}scroller.scrollToIndex跳转到指定ListItemGroup的位置。letterToIndex是字母→分组索引的映射表在数据加载后构建。注意scrollToIndex的索引对应的是ListItemGroup在List中的位置不是ListItem。如果List中只有ListItemGroup没有单独的ListItem索引就是ListItemGroup的序号。滑动联动手指滑动列表时右侧索引高亮要跟着变List(){// 内容}.onScrollIndex((start:number,end:number){if(start0startthis.groupedContacts.length){this.activeLetterthis.groupedContacts[start].letter;}})onScrollIndex在滚动时回调当前可见区域的起始和结束索引。start就是当前吸顶的分组索引用它更新activeLetter。索引和高亮的联动是双向的点击索引滚动列表滑动列表更新索引。两边都要处理。手指滑动索引更流畅的体验是手指在索引条上滑动时实时跳转Column(){ForEach(this.alphabet,(letter:string){Text(letter).fontSize(10).fontColor(this.activeLetterletter?#1a73e8:#666666).padding(2)},(letter:string)letter)}.position({x:92%,y:60}).height(70%).justifyContent(FlexAlign.SpaceBetween).gesture(PanGesture().onActionStart((event:GestureEvent){this.handleIndexTouch(event.y);}).onActionUpdate((event:GestureEvent){this.handleIndexTouch(event.yevent.offsetY);}))privatehandleIndexTouch(y:number):void{letindexMath.floor(y/(this.indexHeight));if(index0indexthis.alphabet.length){letletterthis.alphabet[index];this.scrollToLetter(letter);}}PanGesture滑动时根据y坐标计算手指在哪个字母上实时跳转。indexHeight是每个字母的占用高度需要根据索引条总高度和字母数量计算。分组折叠长列表分组可折叠减少滚动距离StatecollapsedGroups:SetstringnewSet();BuildergroupHeader(letter:string,count:number){Row(){Text(letter).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#666666)Text(count.toString()人).fontSize(12).fontColor(#999999).margin({left:8})Blank()Text(this.collapsedGroups.has(letter)?展开:收起).fontSize(12).fontColor(#1a73e8)}.width(100%).padding({left:16,right:16,top:10,bottom:10}).backgroundColor(#f5f5f5).onClick((){if(this.collapsedGroups.has(letter)){this.collapsedGroups.delete(letter);}else{this.collapsedGroups.add(letter);}this.collapsedGroupsnewSet(this.collapsedGroups);})}Set存储折叠的分组key。点击header时切换折叠状态。注意Set的delete不会触发UI刷新需要重新赋值一个新的Set。折叠时只渲染header不渲染childrenListItemGroup({header:this.groupHeader(group.letter,group.contacts.length)}){if(!this.collapsedGroups.has(group.letter)){ForEach(group.contacts,(contact:Contact){ListItem(){// 内容}},(contact:Contact)contact.id.toString())}}分组全选勾选整个分组的所有项StateselectedIds:SetnumbernewSet();privatetoggleGroup(letter:string,contacts:Contact[]):void{letallSelectedtrue;for(leti0;icontacts.length;i){if(!this.selectedIds.has(contacts[i].id)){allSelectedfalse;break;}}if(allSelected){for(leti0;icontacts.length;i){this.selectedIds.delete(contacts[i].id);}}else{for(leti0;icontacts.length;i){this.selectedIds.add(contacts[i].id);}}this.selectedIdsnewSet(this.selectedIds);}先检查分组内是否全选——全选则取消全选否则全选。toggleGroup的调用放在header的onClick中跟折叠互斥时用checkbox代替点击。搜索过滤与分组联动搜索过滤后分组可能变空要清理空分组privatefilterAndGroup(keyword:string):GroupedContact[]{letfiltered:Contact[][];for(leti0;ithis.allContacts.length;i){if(this.allContacts[i].name.indexOf(keyword)0||this.allContacts[i].phone.indexOf(keyword)0){filtered.push(this.allContacts[i]);}}letgroupedthis.groupByLetter(filtered);returngrouped;}先过滤再分组。如果某个字母下没有匹配的联系人groupByLetter自然不会生成该分组不需要额外清理。搜索时侧边索引也要更新——只显示有数据的字母privategetavailableLetters():string[]{letletters:string[][];for(leti0;ithis.groupedContacts.length;i){letters.push(this.groupedContacts[i].letter);}returnletters;}踩坑清单问题原因解决吸顶header透字header背景透明给header加backgroundColorscrollToIndex跳错位置索引对应的是ListItem不是ListItemGroup用ListItemGroup的序号滑动时索引不高亮没处理onScrollIndex回调中更新activeLetterSet操作不刷新UIdelete/add不触发检测重新赋值new Set(oldSet)搜索后索引没更新索引字母是固定的从分组数据动态计算分组数据为空过滤后某分组无匹配项过滤后重新分组ListItemGroup的key重复用index作key用group.letter作key索引条位置偏移position用了百分比跟容器宽度协调计算PanGesture和List冲突索引条手势被List消费索引条用priorityGesture折叠动画闪烁数据变化和动画不同步折叠直接切换不做动画分组列表的核心是数据分组→ListItemGroup渲染→Scroller跳转→索引联动这条链路。吸顶用StickyStyle.Header索引用ScrolleronScrollIndex两者联动就构成了通讯录的完整体验。

相关新闻

Jellium Desktop窗口尺寸记忆设置:为不同内容类型保存大小

Jellium Desktop窗口尺寸记忆设置:为不同内容类型保存大小

Jellium Desktop窗口尺寸记忆设置:为不同内容类型保存大小 【免费下载链接】jellium-desktop An unofficial desktop client for Jellyfin 项目地址: https://gitcode.com/GitHub_Trending/je/jellium-desktop Jellium Desktop作为一款非官方的Jellyfin桌面客…

2026/7/27 21:02:38 阅读更多 →
HarmonyOS 6.0 页面间数据传递与参数序列化

HarmonyOS 6.0 页面间数据传递与参数序列化

页面跳转带数据,这谁不会?pushPath的时候把参数传过去就完了。但现实远没这么简单——传个字符串没问题,传个对象呢?传个Date呢?传个100KB的JSON呢?跨Ability跳转呢?返回时怎么带数据回来&#…

2026/7/27 21:02:38 阅读更多 →
锐龙AI嵌入式X100与Kria AI齐发,AMD打通物理AI“任督二脉”

锐龙AI嵌入式X100与Kria AI齐发,AMD打通物理AI“任督二脉”

作者:毛烁 “物理 AI(Physical AI)是科技领域最艰巨的挑战之一。而实现这一级别的智能,需要一套全栈式解决方案。”在今年的CES大会上,AMD 董事长兼CEO 苏姿丰(Lisa Su)博士的这番判断&#xff…

2026/7/27 21:02:38 阅读更多 →

最新新闻

Chunky场景导入与导出全攻略:高效管理你的Minecraft渲染项目

Chunky场景导入与导出全攻略:高效管理你的Minecraft渲染项目

Chunky场景导入与导出全攻略:高效管理你的Minecraft渲染项目 【免费下载链接】chunky A path tracer to create realistic images of your Minecraft worlds. 项目地址: https://gitcode.com/gh_mirrors/ch/chunky Chunky是一款强大的路径追踪工具&#xff0…

2026/7/27 21:10:41 阅读更多 →
国产化操作系统KeyarchOS部署libexttextcat文本分类工具实践

国产化操作系统KeyarchOS部署libexttextcat文本分类工具实践

1. 项目背景与核心价值 在全球化业务场景中,多语言文本处理能力已成为企业基础建设的刚需。传统方案往往依赖国外开源工具链,存在技术可控性风险。近期我们在浪潮信息KeyarchOS(KOS)国产化操作系统上成功部署了libexttextcat-tool…

2026/7/27 21:10:41 阅读更多 →
2024最新更新:chat_templates新增Llama-3.1与Gemma-2支持详解

2024最新更新:chat_templates新增Llama-3.1与Gemma-2支持详解

2024最新更新:chat_templates新增Llama-3.1与Gemma-2支持详解 【免费下载链接】chat_templates Chat Templates for 🤗 HuggingFace Large Language Models 项目地址: https://gitcode.com/gh_mirrors/ch/chat_templates chat_templates是面向&am…

2026/7/27 21:10:41 阅读更多 →
解决Android运行时权限痛点:PermissionsDispatcher Plugin让开发更简单

解决Android运行时权限痛点:PermissionsDispatcher Plugin让开发更简单

解决Android运行时权限痛点:PermissionsDispatcher Plugin让开发更简单 【免费下载链接】permissions-dispatcher-plugin 项目地址: https://gitcode.com/gh_mirrors/pe/permissions-dispatcher-plugin Android运行时权限管理一直是开发者的痛点&#xff0c…

2026/7/27 21:10:41 阅读更多 →
计算机毕业设计之Python在杭州亚运会数据分析与赛事管理中的应用

计算机毕业设计之Python在杭州亚运会数据分析与赛事管理中的应用

在杭州亚运会这一国际盛事中,Python作为一种强大的数据分析工具,被广泛应用于赛事管理和数据分析。本文将探讨Python在杭州亚运会数据分析与赛事管理中的应用,以及其对赛事成功举办的重要意义。 Python在杭州亚运会数据分析中的应用体现在对比…

2026/7/27 21:10:41 阅读更多 →
PHP实战:5分钟彻底修复存储型XSS漏洞,从原理到代码

PHP实战:5分钟彻底修复存储型XSS漏洞,从原理到代码

1. 项目概述:为什么存储型XSS是“钉子户”漏洞?做Web开发或者安全测试的朋友,对XSS(跨站脚本攻击)肯定不陌生。但说到存储型XSS,很多人可能觉得它和反射型XSS差不多,无非是“弹个窗”的恶作剧。…

2026/7/27 21:09:41 阅读更多 →

日新闻

【JAVA毕设源码分享】基于SpringBoot的社区智能垃圾管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于SpringBoot的社区智能垃圾管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/27 0:00:54 阅读更多 →
SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

SPI实战指南:从时钟模式到寄存器配置,解决嵌入式通信难题

1. 项目概述:从寄存器手册到实战指南 如果你手头有一份类似德州仪器(TI)TMS320x240xA系列DSP的SPI模块技术手册,看着里面密密麻麻的寄存器位定义、时序图和公式,是不是感觉头大?这份资料虽然权威&#xff0…

2026/7/27 0:00:54 阅读更多 →
【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于springboot的水果购物管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/27 0:00:54 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/27 4:33:59 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/27 6:31:56 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/27 4:01:12 阅读更多 →

月新闻