HarmonyOs应用《重要日》开发第23篇 - 排序与置顶
2026/7/19 1:35:03 网站建设 项目流程

本文深入解析 ImportantDays 项目中的重要日列表排序与置顶机制,包括getSortedList()方法的实现、置顶优先策略、三种排序模式的逻辑、排序循环切换的交互设计,以及在 ArkUI 中实现高效列表排序的最佳实践。

一、列表管理的需求

重要日列表需要支持以下管理维度:

  1. 置顶:用户可以将重要的重要日固定在顶部
  2. 排序:按日期升序、日期降序、创建时间排列
  3. 切换:用户可以循环切换排序方式

二、SortTypeEnum 排序类型

exportenumSortTypeEnum{DATE_ASC,// 按日期升序DATE_DESC,// 按日期降序CREATE_TIME// 按创建时间}

三种排序的适用场景

排序方式排序结果适用场景
DATE_ASC最近的在前查看即将到来的重要日
DATE_DESC最远的在前规划远期目标
CREATE_TIME最近添加的在前回顾刚添加的重要日

三、置顶状态管理

切换置顶

asynctogglePin(dayID:string):Promise<void>{constday=this.allDays.find(d=>d.dayID===dayID);if(!day)return;day.isPinned=!day.isPinned;awaitthis.persist();this.triggerRefresh();}

置顶逻辑

  1. 通过dayID查找重要日
  2. 反转isPinned属性
  3. 持久化保存
  4. 触发 UI 刷新

UI 中的置顶标记

if(day.isPinned){Text('📌').fontSize(14).margin({left:4})}

置顶的重要日在标题右侧显示图钉 emoji。

置顶按钮

Text(day.isPinned?'取消置顶':'置顶').onClick(()=>{this.manager.togglePin(day.dayID);})

四、getSortedList() 详解

getSortedList(sortType:SortTypeEnum=SortTypeEnum.DATE_ASC):ImportantDay[]{constlist=[...this.allDays];// 浅拷贝// 第一步:按置顶状态分组list.sort((a,b)=>{if(a.isPinned&&!b.isPinned)return-1;if(!a.isPinned&&b.isPinned)return1;return0;});// 第二步:分离置顶和非置顶constpinnedCount=list.filter(d=>d.isPinned).length;constremaining=list.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];constcurrentIndex=types.indexOf(this.sortBy);this.sortBy=types[(currentIndex+1)%types.length];}

切换流程

DATE_ASC → DATE_DESC → CREATE_TIME → DATE_ASC → ...

实现分析

  1. types数组定义了循环顺序
  2. indexOf找到当前位置
  3. (currentIndex + 1) % types.length计算下一个位置
  4. 取模运算确保循环

排序名称显示

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. 置顶部分也支持排序

当前置顶部分不排序,可以扩展为置顶部分也按排序方式排列:

// 概念性改进constpinned=list.slice(0,pinnedCount);constremaining=list.slice(pinnedCount);// 置顶部分也排序this.sortByType(pinned,sortType);this.sortByType(remaining,sortType);return[...pinned,...remaining];

2. 缓存排序结果

@TracesortedCache:Map<SortTypeEnum,ImportantDay[]>=newMap();@TracecacheVersion:number=0;getSortedList(sortType:SortTypeEnum):ImportantDay[]{constcacheKey=`${sortType}_${this.cacheVersion}`;// 检查缓存...}

3. 拖拽排序

支持用户手动拖拽调整顺序:

// 概念性扩展asyncmoveDay(fromIndex:number,toIndex:number):Promise<void>{constlist=this.getSortedList(SortTypeEnum.CREATE_TIME);const[moved]=list.splice(fromIndex,1);list.splice(toIndex,0,moved);// 更新排序字段...}

十、总结

排序与置顶机制的设计体现了以下原则:

  1. 置顶优先:置顶项始终在顶部,不受排序影响
  2. 多维度排序:三种排序方式覆盖不同需求
  3. 循环切换:一键切换,交互简洁
  4. 实时计算:每次渲染实时排序,确保数据准确
  5. 不修改原数组:浅拷贝保护原始数据
  6. 稳定排序:相同键值保持原有顺序

列表排序是数据管理应用的核心功能,好的排序设计让用户高效找到需要的信息。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询