HarmonyOS 6.0 拖拽排序与列表动画
列表排序看起来简单——不就是数组元素换个位置吗但加上动画和交互就有很多细节拖拽时视觉反馈、松手后的位置动画、排序过程中的状态同步。这篇从手动排序到拖拽排序把动画细节全说清楚。数组重排算法最基础的操作——把数组元素从位置A移到位置BprivatemoveItem(from:number,to:number):void{letitem:DataItemthis.items[from];letnewItems:DataItem[][];// 先移除from位置的元素for(leti:number0;ithis.items.length;i){if(i!from){newItems.push(this.items[i]);}}// 在to位置插入letresult:DataItem[][];for(leti:number0;inewItems.length;i){if(ito){result.push(item);}result.push(newItems[i]);}if(tonewItems.length){result.push(item);}this.itemsresult;}关键必须给this.items赋值新数组不能直接splice修改。State数组的变更检测依赖引用变化直接修改原数组不会触发UI刷新。更简洁的方式如果运行时支持letitem:DataItemthis.items.splice(from,1)[0];this.items.splice(to,0,item);this.items[...this.items];// 触发刷新但ArkTS不支持解构和展开运算符所以用for循环构建新数组是最安全的做法。上下移动按钮最简单的排序UI——每个列表项旁边有↑↓按钮Row(){Text(item.title).layoutWeight(1)Button(↑).onClick((){if(index0){this.moveItem(index,index-1);}})Button(↓).onClick((){if(indexthis.items.length-1){this.moveItem(index,index2);// 2因为移除后索引偏移}})}moveItem的to参数要注意移除from元素后目标位置的含义会变。上移一位是index-1下移一位是index2因为当前元素被移除后后面的元素都前移了一位。位置动画排序后列表项位置变化加animation让变化有过渡ListItem(){Row(){// 内容}.width(100%).padding(12).borderRadius(10).backgroundColor(#ffffff).animation({duration:200,curve:Curve.EaseInOut})}animation修饰器加在ListItem的内容容器上。当items数组重排后ForEach根据key重新分配数据组件位置变化animation让位移产生过渡动画。ForEach的key必须稳定——用item.id不要用index。index在排序后会变导致ForEach错误地复用组件动画就会错乱。拖拽项视觉反馈拖拽中的列表项需要视觉区分——背景色变化、阴影加深、轻微放大StatedragIndex:number-1;Row(){// 内容}.backgroundColor(this.dragIndexindex?#e3f2fd:#ffffff).shadow({radius:this.dragIndexindex?8:2,color:#1a000000,offsetY:this.dragIndexindex?2:1}).scale({x:this.dragIndexindex?1.02:1,y:this.dragIndexindex?1.02:1}).animation({duration:150,curve:Curve.EaseOut})三个视觉变化同时发生背景变蓝、阴影加深、轻微放大1.02倍。animation让变化有过渡。dragIndex在拖拽开始时设为当前index拖拽结束时恢复-1。PanGesture拖拽排序用PanGesture实现跟手拖拽Row(){Text(≡)// 拖拽手柄Text(item.title)}.gesture(PanGesture().onActionStart((){this.dragIndexindex;}).onActionUpdate((event:GestureEvent){// 根据event.offsetY计算当前应该在的位置letcurrentY:numberthis.itemPositions[index]event.offsetY;letnewIndex:numberthis.calculateNewIndex(currentY);if(newIndex!indexnewIndex!this.targetIndex){this.targetIndexnewIndex;}}).onActionEnd((){if(this.targetIndex0){this.moveItem(this.dragIndex,this.targetIndex);}this.dragIndex-1;this.targetIndex-1;}))PanGesture的onActionUpdate提供offsetY——手指从起始位置的偏移。根据偏移计算当前应该在哪个位置实时预览排序效果。但PanGesture有个问题ListItem的PanGesture会跟List的滚动冲突。需要用priorityGesture或者用专门的拖拽手柄区域。List内置拖拽APIList组件有onDragStart/onDrop等拖拽回调List(){ForEach(this.items,(item:DataItem,index:number){ListItem(){Row(){Text(item.title)}}.onDragStart((event:DragEvent){this.dragIndexindex;returnthis.dragBuilder(item);}).onDrop((event:DragEvent){lettargetIndex:numberthis.calculateDropIndex(event.y);this.moveItem(this.dragIndex,targetIndex);this.dragIndex-1;})},(item:DataItem)item.id)}onDragStart返回一个Builder作为拖拽预览图。onDrop在松手时触发event.y可以计算放置位置。但onDragStart/onDrop在List中的行为不够稳定——长按触发拖拽的灵敏度、跨ListItem的拖拽位置计算都有坑。实际项目中更常用按钮排序动画过渡而不是真正拖拽。删除动画列表项删除时的动画效果StateremovingIndex:number-1;Row(){Text(item.title)}.scale({x:this.removingIndexindex?0.8:1,y:this.removingIndexindex?0.8:1}).opacity(this.removingIndexindex?0:1).animation({duration:200,curve:Curve.EaseIn})// 触发删除removeItem(index:number):void{this.removingIndexindex;setTimeout((){letnewItems:DataItem[][];for(leti:number0;ithis.items.length;i){if(i!index){newItems.push(this.items[i]);}}this.itemsnewItems;this.removingIndex-1;},200);}删除分两步先动画缩小渐隐200ms后真正移除数据。setTimeout延迟匹配animation时长。插入动画新项目插入时渐显放大StatenewItemIndex:number-1;Row(){Text(item.title)}.scale({x:this.newItemIndexindex?1:0.5,y:this.newItemIndexindex?1:0.5}).opacity(this.newItemIndexindex?1:0).animation({duration:300,curve:Curve.EaseOut})// 插入后insertItem(item:DataItem,at:number):void{this.newItemIndexat;letnewItems:DataItem[][];for(leti:number0;ithis.items.length;i){if(iat){newItems.push(item);}newItems.push(this.items[i]);}if(atthis.items.length){newItems.push(item);}this.itemsnewItems;setTimeout((){this.newItemIndex-1;},300);}初始scale 0.5和opacity 0动画到1和1。300ms后清除标记。这样新插入的项有一个出现的动画。列表动画的性能注意动画越多性能开销越大。几个优化点只对可见项做动画removingIndex/newItemIndex只影响一个项animation时长别太长200ms够用超过300ms用户会觉得卡避免大范围重绘排序时ForEach只更新变化项key要稳定shadow开销大大量列表项加shadow会掉帧拖拽中临时加shadow结束后去掉LazyForEach cachedCount大列表排序必须用LazyForEach否则ForEach全量创建组件交错动画列表初始化时每项依次出现StateitemVisible:boolean[][];aboutToAppear():void{for(leti:number0;ithis.items.length;i){setTimeout((){this.itemVisible[i]true;},i*50);}}Row(){Text(item.title)}.opacity(this.itemVisible[index]?1:0).translate({y:this.itemVisible[index]?0:20}).animation({duration:300,curve:Curve.EaseOut})每项延迟50ms出现形成从上到下的波浪效果。50ms * N项 总延迟。10项500ms完成。踩坑清单问题原因解决排序后UI没更新直接修改数组没触发刷新赋值新数组给this.items动画错乱ForEach用index作key改用item.idPanGesture跟滚动冲突两个手势互相消费用priorityGesture或拖拽手柄删除动画后列表跳动画和数据移除不同步动画完成后再移除数据阴影导致掉帧大量shadow组件只在拖拽项加shadow移动位置计算错误移除元素后索引偏移先移除再计算目标位置交错动画闪烁opacity初始值不对初始化itemVisible为false按钮排序↑↓方向反moveItem的to参数理解错误下移用index2不是index1插入位置不准for循环的插入逻辑在iat时先push新项动画中操作列表动画未完成就改数据加loading标记防重复操作列表动画的原则是少量项做动画大量项保性能。排序只影响两三个项的位置变化用animation就够了。全列表出现动画只在初始化做一次不要反复触发。

相关新闻

CC Switch:一站式AI工具管理平台,提升开发者工作效率的5个实用技巧

CC Switch:一站式AI工具管理平台,提升开发者工作效率的5个实用技巧

CC Switch:一站式AI工具管理平台,提升开发者工作效率的5个实用技巧 【免费下载链接】cc-switch A cross-platform desktop All-in-One assistant for Claude Code, Codex, OpenCode, OpenClaw, Gemini CLI & Hermes Agent. Only official website: c…

2026/10/14 12:07:55 阅读更多 →
如何高效使用四叶草拼音:打造纯净无广告的专业中文输入体验 [特殊字符]️

如何高效使用四叶草拼音:打造纯净无广告的专业中文输入体验 [特殊字符]️

如何高效使用四叶草拼音:打造纯净无广告的专业中文输入体验 🍀️ 【免费下载链接】rime-cloverpinyin 🍀️四叶草拼音输入方案,做最好用的基于rime开源的简体拼音输入方案! 项目地址: https://gitcode.com/gh_mirror…

2026/10/14 12:08:39 阅读更多 →
Twitch视频下载终极指南:命令行工具快速保存直播内容

Twitch视频下载终极指南:命令行工具快速保存直播内容

Twitch视频下载终极指南:命令行工具快速保存直播内容 【免费下载链接】twitch-dl CLI tool for downloading videos from Twitch. 项目地址: https://gitcode.com/gh_mirrors/tw/twitch-dl 想要轻松下载Twitch平台的直播录像、精彩片段和聊天记录吗&#xff…

2026/10/9 15:28:03 阅读更多 →

最新新闻

从二进制到可编辑网格:owmdl模型在Blender中的导入解析

从二进制到可编辑网格:owmdl模型在Blender中的导入解析

简介:基于Blender的《守望先锋》owmdl模型导入插件源码包,主要面向需要在Blender中查看和编辑这一专有游戏格式的玩家、三维建模学习者和游戏资产研究者。插件通过io_scene_owm扩展Blender的导入能力,可解析模型网格、纹理、骨骼与动画数据&a…

2026/10/14 12:09:12 阅读更多 →
金融表格抽取新思路:用图神经网络还原复杂表格结构

金融表格抽取新思路:用图神经网络还原复杂表格结构

我拿到这个标题的第一反应是:终于有人认真对待金融表格抽取这件事了。业界做文档解析的不少,但大多数都把表格当成图像里的一个矩形区域,“识别出来了”就算交差。可在金融场景里,表格抽取的终点根本不是“框出表格”,…

2026/10/14 12:09:12 阅读更多 →
TCP窗口扩张系数WSCALE原理与实战配置指南

TCP窗口扩张系数WSCALE原理与实战配置指南

1. 什么是TCP窗口扩张系数(WSCALE)?它到底在解决什么问题? 你可能在抓包分析时见过TCP三次握手的SYN报文里,选项字段中赫然写着“Window Scale: 7”或者“WS8”,但未必真正理解它背后那场持续二十多年的带宽…

2026/10/14 12:09:12 阅读更多 →
零代码RPA工具灵梭:办公自动化实战指南

零代码RPA工具灵梭:办公自动化实战指南

1. 项目概述 "灵梭RPA"是一款面向非技术人员的办公自动化工具,它让普通办公人员也能像专业人士一样创建自动化流程。我使用过市面上十几款RPA工具,发现大多数都需要编程基础,而灵梭RPA真正做到了"零代码"操作&#xff0c…

2026/10/14 12:09:12 阅读更多 →
Android Compose AI组件库:从架构设计到流式渲染的完整实践

Android Compose AI组件库:从架构设计到流式渲染的完整实践

1. 从命令式到声明式:AI组件库的架构选型思考Android 原生 UI 开发在过去很长一段时间里,基本是ViewXML的天下。写一个聊天界面,你得先定义RecyclerView,再写Adapter,再处理ViewHolder,再手动 diff 刷新&am…

2026/10/14 12:09:12 阅读更多 →
Oracle Long、Raw、Blob字段类型读写与避坑指南

Oracle Long、Raw、Blob字段类型读写与避坑指南

简介:这是一份针对Oracle数据库特殊字段类型的实用技术文档,面向需要处理Long、Raw、Blob等非标准字段的数据库开发、运维人员及.NET技术从业者,系统讲解了三类字段的存储上限、适用场景与选型建议(如Long在10g后宜改用Clob&#…

2026/10/14 12:08:11 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →