本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、拖拽事件核心概念
1. 功能定位
- 作用:实现组件间数据传递与交互,支持本地/跨应用拖拽(如文件传输、列表排序)。
- 触发条件:
- 手势拖拽:长按≥500ms且移动≥10vp触发 。
- 鼠标拖拽:左键移动≥1vp触发 。
2. 核心流程
participant 拖出组件
participant 拖入目标
拖出组件->>拖入目标: onDragStart (设置数据/背板图)
拖入目标->>拖入目标: onDragEnter (进入范围)
拖入目标->>拖入目标: onDragMove (移动时)
拖入目标->>拖入目标: onDrop (释放处理数据)
拖出组件->>拖出组件: onDragEnd (结束回调)
二、基础使用与API详解
1. 组件使能与事件绑定
// 使能拖拽(默认支持组件如Text、Image可省略)
Text("拖拽我")
.draggable(true)
.onDragStart((event: DragEvent) => {
// 设置拖拽数据
let data = new unifiedDataChannel.PlainText();
data.textContent = "Hello";
event.setData(new unifiedDataChannel.UnifiedData(data));
// 返回自定义背板图
return {
pixelMap: $r('app.media.drag_icon') // 或使用builder
};
});
// 使能拖入
Column()
.allowDrop([uniformTypeDescriptor.UniformDataType.PLAIN_TEXT])
.onDrop((event: DragEvent) => {
let data = event.getData()?.getRecords()[0] as unifiedDataChannel.PlainText;
console.log("接收数据:", data.textContent);
event.setResult(DragResult.DRAG_SUCCESSFUL); // 必须设置结果
});
关键API:
draggable
:控制拖出能力 。allowDrop
:指定接收的数据类型(如IMAGE
、PLAIN_TEXT
) 。onDragStart
:必须设置数据(setData
)或背板图(DragItemInfo
) 。
2. 事件回调全周期
事件 | 触发时机 | 典型应用 |
---|---|---|
onPreDrag | 拖拽前阶段(如准备浮起动效) | 预加载数据/动画 |
onDragEnter | 进入目标范围 | 高亮目标区域 |
onDragMove | 在目标范围内移动 | 实时更新位置 |
onDragLeave | 离开目标范围 | 取消高亮 |
onDrop | 释放拖拽 | 处理数据(必须调用setResult ) |
onDragEnd | 拖拽结束(无论成功与否) | 清理状态 |
三、高级功能实战
1. 自定义拖拽背板与动效
// 自定义背板图(支持动态生成)
.onDragStart((event) => {
return {
builder: () => {
Column() {
Text("自定义背板").fontColor(Color.White)
Image($r('app.media.preview')).width(100)
}
.backgroundColor(Color.Blue)
}
};
});
// 落位动效(API 12+)
.onDrop((event) => {
event.useCustomDropAnimation({
duration: 300,
curve: Curve.Spring
});
});
限制:文本类组件不支持背板自定义。
2. 多选拖拽与列表排序
// 多选数据传递
let selectedItems = [1, 2, 3];
event.setData(new UnifiedData(selectedItems.map(id => {
let item = new unifiedDataChannel.Text();
item.textContent = `Item_${id}`;
return item;
})));
// 列表插入位置判断(通过extraParams)
.onDrop((event, extraParams) => {
let insertIndex = JSON.parse(extraParams).insertIndex; // 来自List组件的自动填充
});
四、性能优化建议
场景 | 优化措施 |
---|---|
高频拖拽 | 使用pixelMap 替代builder 减少渲染 |
复杂数据 | 预解析extraParams 避免重复计算 |
列表拖拽 | 通过insertIndex 优化DOM操作 |
五、案例:文件管理器拖拽
@Entry
@Component
struct FileManager {
@State files: string[] = ["a.jpg", "b.pdf"];
@State draggedFile: string = "";
build() {
List() {
ForEach(this.files, (file) => {
ListItem() {
Text(file)
}
.draggable(true)
.onDragStart(() => {
this.draggedFile = file;
return { pixelMap: $r('app.media.file_icon') };
})
})
}
.allowDrop([uniformTypeDescriptor.UniformDataType.FILE_URI])
.onDrop((event) => {
if (this.draggedFile) {
this.files = this.files.filter(f => f !== this.draggedFile);
event.setResult(DragResult.DRAG_SUCCESSFUL);
}
})
}
}
六、总结
功能 | 推荐API | 适用场景 |
---|---|---|
基础拖拽 | draggable + onDragStart | 简单数据传递 |
高级动效 | useCustomDropAnimation | 交互动画增强 |
跨应用拖拽 | UnifiedData + FileUri | 文件/资源共享 |