HarmonyOS 鸿蒙Next中利用 drag 事件实现跨容器拖拽排序
HarmonyOS 鸿蒙Next中利用 drag 事件实现跨容器拖拽排序 想要实现一个经典的“两栏任务看板”拖拽排序功能。可以将任务卡片从“待办”列拖拽到“已完成”列,并实时更新数据源,应该如何使用drag 事件来实现呢?
实现思路
首先定义 TaskData 类,包含任务的唯一 ID、标题和当前状态。
其次,我们使用 Row 容器并排展示两个 Column 容器(分别代表“待办”和“已完成”)。
再然后我们完成拖拽,在任务卡片上绑定 onDragStart。当长按并拖动时,记录被拖拽任务的信息(ID),并通过 dragPreview 设置拖拽时的缩略图样式。在目标 Column 容器上绑定 onDrop。当卡片被拖入并释放时,触发 onDrop。
最后在回调中获取 draggingId,修改对应数据的 status 属性,从而实现数据在不同列表间的移动。
应用场景
最常见的就是在任务看板视图中,任务在“待处理”、“进行中”、“已完成”之间流转。
实现效果

完整代码
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';
class TaskData {
id: string;
title: string;
status: string;
constructor(title: string, status: string) {
this.id = `task_${Math.random().toString(36).substr(2, 9)}`;
this.title = title;
this.status = status;
}
}
@Builder
function DragPreviewBuilder(text: string) {
Text(text)
.padding(12)
.backgroundColor('#0A59F7')
.fontColor(Color.White)
.borderRadius(8)
.fontSize(14)
.shadow({ radius: 10, color: '#40000000' })
}
interface DragStartEventTarget {
startDrag(data: string | ESObject | undefined, callback: AsyncCallback<void>): void;
}
@Entry
@Component
struct DragDropFinalStrict {
@State taskList: TaskData[] = [
new TaskData("长按拖动我 1", "todo"),
new TaskData("长按拖动我 2", "todo"),
new TaskData("任务 3", "done")
];
@State draggingId: string = '';
build() {
Column() {
Text("拖拽演示")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 20 })
Row() {
this.TaskColumn("待办", "todo", this.taskList.filter(t => t.status === 'todo'))
this.TaskColumn("完成", "done", this.taskList.filter(t => t.status === 'done'))
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F1F3F5')
.padding(20)
}
@Builder
TaskColumn(title: string, targetStatus: string, items: TaskData[]) {
Column() {
Text(title)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.margin({ bottom: 15 })
Column() {
ForEach(items, (item: TaskData) => {
Row() {
Text(item.title)
.fontSize(16)
.layoutWeight(1)
}
.width('100%')
.padding(16)
.margin({ bottom: 10 })
.backgroundColor(this.draggingId === item.id ? '#E0E0E0' : Color.White)
.borderRadius(8)
.shadow({ radius: 4, color: '#1A000000', offsetX: 0, offsetY: 2 })
.opacity(this.draggingId === item.id ? 0.4 : 1)
.animation({ duration: 200 })
.hitTestBehavior(HitTestMode.Transparent)
.gesture(
LongPressGesture({ repeat: false, duration: 200 })
.onAction((event: GestureEvent) => {
console.info("Long press detected, starting drag...");
this.draggingId = item.id;
try {
const target = event.target as object as DragStartEventTarget;
target.startDrag(
JSON.stringify({ id: item.id }),
(err: BusinessError) => {
if (err) {
console.error(`startDrag failed, code: ${err.code}, message: ${err.message}`);
} else {
console.info("startDrag success");
}
}
);
} catch (e) {
console.error("startDrag exception: " + JSON.stringify(e));
}
})
)
.onDragStart((event: DragEvent) => {
console.info(`onDragStart callback: ${item.title}`);
return {
builder: DragPreviewBuilder.bind(this, item.title)
};
})
.onDragEnd(() => {
this.draggingId = '';
})
}, (item: TaskData) => item.id)
}
.width('100%')
.layoutWeight(1)
.padding(10)
.backgroundColor('#E0E0E0')
.borderRadius(12)
.onDrop((event: DragEvent) => {
console.info("onDrop triggered");
if (this.draggingId) {
const targetIndex = this.taskList.findIndex(t => t.id === this.draggingId);
if (targetIndex !== -1 && this.taskList[targetIndex].status !== targetStatus) {
this.taskList[targetIndex].status = targetStatus;
}
}
})
}
.width('45%')
}
}
更多关于HarmonyOS 鸿蒙Next中利用 drag 事件实现跨容器拖拽排序的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,可通过@Drag装饰器实现跨容器拖拽排序。
拖拽组件需设置draggable(true),接收容器使用onDrop事件处理放置逻辑。
通过DragEvent获取拖拽数据,利用insertChild或removeChild动态调整组件位置。
关键点:统一管理拖拽数据标识,确保跨容器数据传递准确。
在HarmonyOS Next中,利用ArkTS的拖拽事件(DragEvent)实现跨容器拖拽排序,可以通过以下步骤完成:
1. 定义数据模型与状态
首先,定义任务卡片的数据结构,并使用@State装饰器管理两列的任务列表,确保UI能响应数据变化。
@State todoList: Task[] = [...];
@State doneList: Task[] = [...];
2. 为可拖拽元素添加手势
在任务卡片的组件上添加Gesture组件的DragGesture,并设置onDragStart来初始化拖拽数据。通过DragEvent的setData方法传递任务信息。
.gesture(
DragGesture()
.onDragStart((event: DragEvent) => {
event.setData('task', JSON.stringify(task));
})
)
3. 为容器添加拖拽响应
在两列的容器上添加onDragDrop和onDragEnter等事件监听:
onDragEnter:当拖拽元素进入容器时,更新视觉反馈(如高亮)。onDragDrop:当拖拽元素在容器内释放时,处理数据更新逻辑。
4. 实现跨列数据交换
在目标容器的onDragDrop回调中:
- 通过
event.getData获取拖拽任务的数据。 - 根据拖拽起始列和目标列,从原列表移除任务,并添加到目标列表。
- 使用数组操作更新
@State变量,触发UI重新渲染。
5. 实时更新数据源
由于两列列表均使用@State装饰,任何修改都会自动同步到UI。若需持久化,可在数据变更后调用后端接口或本地存储方法。
示例代码片段
// 拖拽释放处理函数
onDragDrop(event: DragEvent) {
let taskData = event.getData('task');
let task: Task = JSON.parse(taskData);
// 从原列表移除
this.todoList = this.todoList.filter(item => item.id !== task.id);
// 添加到目标列表
this.doneList.push(task);
}
注意事项
- 拖拽过程中可使用
onDragEnter/onDragLeave优化视觉体验。 - 跨容器拖拽时需注意事件冒泡和容器层级。
- 复杂场景可结合
@Provide/@Consume或全局状态管理共享数据。
以上方案通过ArkTS拖拽事件与状态管理,可实现高效的跨容器拖拽排序功能。

