HarmonyOs应用《重要日》开发第23篇 - 排序与置顶

📅 2026/7/19 1:35:09
HarmonyOs应用《重要日》开发第23篇 - 排序与置顶
本文深入解析 ImportantDays 项目中的重要日列表排序与置顶机制包括getSortedList()方法的实现、置顶优先策略、三种排序模式的逻辑、排序循环切换的交互设计以及在 ArkUI 中实现高效列表排序的最佳实践。一、列表管理的需求重要日列表需要支持以下管理维度置顶用户可以将重要的重要日固定在顶部排序按日期升序、日期降序、创建时间排列切换用户可以循环切换排序方式二、SortTypeEnum 排序类型exportenumSortTypeEnum{DATE_ASC,// 按日期升序DATE_DESC,// 按日期降序CREATE_TIME// 按创建时间}三种排序的适用场景排序方式排序结果适用场景DATE_ASC最近的在前查看即将到来的重要日DATE_DESC最远的在前规划远期目标CREATE_TIME最近添加的在前回顾刚添加的重要日三、置顶状态管理切换置顶asynctogglePin(dayID:string):Promisevoid{constdaythis.allDays.find(dd.dayIDdayID);if(!day)return;day.isPinned!day.isPinned;awaitthis.persist();this.triggerRefresh();}置顶逻辑通过dayID查找重要日反转isPinned属性持久化保存触发 UI 刷新UI 中的置顶标记if(day.isPinned){Text().fontSize(14).margin({left:4})}置顶的重要日在标题右侧显示图钉 emoji。置顶按钮Text(day.isPinned?取消置顶:置顶).onClick((){this.manager.togglePin(day.dayID);})四、getSortedList() 详解getSortedList(sortType:SortTypeEnumSortTypeEnum.DATE_ASC):ImportantDay[]{constlist[...this.allDays];// 浅拷贝// 第一步按置顶状态分组list.sort((a,b){if(a.isPinned!b.isPinned)return-1;if(!a.isPinnedb.isPinned)return1;return0;});// 第二步分离置顶和非置顶constpinnedCountlist.filter(dd.isPinned).length;constremaininglist.splice(pinnedCount);// 第三步对非置顶部分按指定方式排序switch(sortType){caseSortTypeEnum.DATE_ASC:remaining.sort((a,b)a.targetDate.getTime()-b.targetDate.getTime());break;caseSortTypeEnum.DATE_DESC:remaining.sort((a,b)b.targetDate.getTime()-a.targetDate.getTime());break;caseSortTypeEnum.CREATE_TIME:remaining.sort((a,b)b.createTime.getTime()-a.createTime.getTime());break;}// 第四步合并返回return[...list,...remaining];}排序流程图原始数组 [ A(置顶) B C(置顶) D E ] ↓ 第一步置顶排序 [ A(置顶) C(置顶) B D E ] ↓ 第二步分离 pinned: [ A(置顶) C(置顶) ] remaining: [ B D E ] ↓ 第三步remaining 按日期升序排序 remaining: [ B D E ] → [ D B E ] (假设 D 最早) ↓ 第四步合并 [ A(置顶) C(置顶) D B E ]关键设计置顶部分不排序置顶的重要日保持原有的相对顺序第一步排序后的顺序不参与第二步排序。这意味着置顶顺序由置顶操作的时间决定先置顶的排在前面切换排序方式只影响非置顶部分为什么用浅拷贝constlist[...this.allDays];// 浅拷贝避免修改原始allDays数组的顺序浅拷贝足够因为排序只改变引用位置不修改对象内容性能优于深拷贝五、排序循环切换privatecycleSortType():void{consttypes[SortTypeEnum.DATE_ASC,SortTypeEnum.DATE_DESC,SortTypeEnum.CREATE_TIME];constcurrentIndextypes.indexOf(this.sortBy);this.sortBytypes[(currentIndex1)%types.length];}切换流程DATE_ASC → DATE_DESC → CREATE_TIME → DATE_ASC → ...实现分析types数组定义了循环顺序indexOf找到当前位置(currentIndex 1) % types.length计算下一个位置取模运算确保循环排序名称显示privategetSortName():string{switch(this.sortBy){caseSortTypeEnum.DATE_ASC:return日期↑;caseSortTypeEnum.DATE_DESC:return日期↓;caseSortTypeEnum.CREATE_TIME:return创建;default:return日期↑;}}UI 中的排序按钮Text(this.getSortName()).fontSize(13).fontColor(#666666).padding({left:8,right:8,top:4,bottom:4}).backgroundColor(#F5F5F5).borderRadius(12).onClick((){this.cycleSortType();})点击排序按钮[日期↑] → 点击 → [日期↓] → 点击 → [创建] → 点击 → [日期↑]六、排序在列表渲染中的应用Scroll(){Column(){ForEach(this.manager.getSortedList(this.sortBy),(day:ImportantDay){this.importantDayCard(day)})}}实时排序每次 UI 渲染时调用getSortedList(this.sortBy)根据当前排序方式实时排序。性能考量getSortedList每次都创建新数组并排序对于少量数据几十条性能可接受对于大量数据数百条可以考虑缓存排序结果七、置顶与排序的交互场景场景1添加新重要日当前列表日期升序 [ A(置顶, 8月) B(7月) C(9月) ] 添加 D(8月15日) ↓ [ A(置顶, 8月) B(7月) D(8月15日) C(9月) ]新添加的重要日按日期插入到正确位置置顶项不受影响。场景2切换排序方式当前列表日期升序 [ A(置顶, 8月) B(7月) D(8月15日) C(9月) ] 切换为日期降序 [ A(置顶, 8月) C(9月) D(8月15日) B(7月) ]置顶项位置不变非置顶部分按日期降序排列。场景3置顶操作当前列表 [ A(置顶) B C D ] 置顶 C [ A(置顶) C(置顶) B D ]C 移到置顶区域末尾A 之后。场景4取消置顶当前列表 [ A(置顶) C(置顶) B D ] 取消 C 的置顶 [ A(置顶) B D C ] (C 回到非置顶区域按当前排序方式插入)八、排序算法分析Array.sort() 的稳定性ArkTS/JavaScript 的Array.sort()在现代引擎中是稳定的相同键值的元素保持原有顺序。这意味着置顶部分的排序结果是稳定的日期相同的多个重要日保持原有顺序时间复杂度步骤操作复杂度浅拷贝[...this.allDays]O(n)置顶排序list.sort(...)O(n log n)过滤置顶数list.filter(...)O(n)分割list.splice(...)O(n)非置顶排序remaining.sort(...)O(m log m)合并[...list, ...remaining]O(n)总复杂度O(n log n)其中 n 是重要日总数。九、改进建议1. 置顶部分也支持排序当前置顶部分不排序可以扩展为置顶部分也按排序方式排列// 概念性改进constpinnedlist.slice(0,pinnedCount);constremaininglist.slice(pinnedCount);// 置顶部分也排序this.sortByType(pinned,sortType);this.sortByType(remaining,sortType);return[...pinned,...remaining];2. 缓存排序结果TracesortedCache:MapSortTypeEnum,ImportantDay[]newMap();TracecacheVersion:number0;getSortedList(sortType:SortTypeEnum):ImportantDay[]{constcacheKey${sortType}_${this.cacheVersion};// 检查缓存...}3. 拖拽排序支持用户手动拖拽调整顺序// 概念性扩展asyncmoveDay(fromIndex:number,toIndex:number):Promisevoid{constlistthis.getSortedList(SortTypeEnum.CREATE_TIME);const[moved]list.splice(fromIndex,1);list.splice(toIndex,0,moved);// 更新排序字段...}十、总结排序与置顶机制的设计体现了以下原则置顶优先置顶项始终在顶部不受排序影响多维度排序三种排序方式覆盖不同需求循环切换一键切换交互简洁实时计算每次渲染实时排序确保数据准确不修改原数组浅拷贝保护原始数据稳定排序相同键值保持原有顺序列表排序是数据管理应用的核心功能好的排序设计让用户高效找到需要的信息。