写点什么

【HarmonyOS】应用开发拖拽功能详解

作者:GeorgeGcs
  • 2025-07-04
    上海
  • 本文字数:3312 字

    阅读完需:约 11 分钟

【HarmonyOS】应用开发拖拽功能详解

【HarmonyOS】应用开发拖拽功能详解

一、前言

拖拽交互本质上是一种通过鼠标或手势触屏传递数据的机制,用户可以从一个组件位置拖出数据并将其拖入到另一个组件位置,从而触发相应的响应。


在鸿蒙 中,ArkUI 框架对拖拽功能提供了完整的支持,从基础的单组件拖拽到复杂的多选拖拽、跨应用数据传递,再到自定义动效和悬停检测,形成了一套完善的解决方案。系统级别的支持,让我们对复杂的拖拽功能实现,非常容易就可完成。

二、拖拽事件流程与实现步骤

1、核心流程

因为鸿蒙是面向多设备的操作系统,拖拽流程主要包含手势拖拽鼠标拖拽两种模式,两者在触发条件和交互细节上略有不同,但核心逻辑一致。



整个拖拽过程:(1)拖拽操作:长按并滑动触发,释放时结束对于手势操作,当用户在可拖拽组件上长按超过 500ms 时会触发拖拽,长按 800ms 时系统会执行预览图的浮起动效。


而鼠标拖拽则遵循"即拖即走"模式,当鼠标左键在可拖拽组件上按下并移动超过 1vp 时,即可触发拖拽功能。


(2)拖拽背板:拖动数据时的可视化表示,可自定义(3)拖拽内容:使用 UDMF 统一数据框架封装,确保数据一致性和安全性(4)拖出对象:触发拖拽并提供数据的组件(5)拖入目标:接收并处理拖拽数据的组件

2、回调事件

ArkUI 提供了一系列回调事件,帮助开发者感知拖拽状态并调整系统默认行为:


3、数据传递与背板定制

拖拽数据通过 UDMF(用户数据管理框架)进行封装,确保跨组件和跨应用的数据一致性。在 onDragStart 回调中,可通过 setData 方法设置传递的统一数据:


onDragStart((event) => {  let data: unifiedDataChannel.Image = new unifiedDataChannel.Image();  data.imageUri = 'common/pic/img.png';  let unifiedData = new unifiedDataChannel.UnifiedData(data);  event.setData(unifiedData);})
复制代码

三、DEMO 源码

拖拽功能实现

import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';import { promptAction } from '@kit.ArkUI';import { image } from '@kit.ImageKit';
@Entry@Componentstruct DragAndDropDemo { @State targetImage: string = ''; @State imageWidth: number = 100; @State imageHeight: number = 100; @State imgState: Visibility = Visibility.Visible; @State pixmap: image.PixelMap | undefined = undefined;
@Builder pixelMapBuilder() { Column() { Image($r('app.media.startIcon')) .width(120) .height(120) .backgroundColor(Color.Yellow) } }
// 获取UDMF数据,包含重试机制 getDataFromUdmfRetry(event: DragEvent, callback: (data: DragEvent) => void) { try { let data: unifiedDataChannel.UnifiedData = event.getData(); if (!data) { return false; } let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords(); if (!records || records.length <= 0) { return false; } callback(event); return true; } catch (e) { console.log("getData failed, message: " + (e as Error).message); return false; } }
getDataFromUdmf(event: DragEvent, callback: (data: DragEvent) => void) { if (this.getDataFromUdmfRetry(event, callback)) { return; } setTimeout(() => { this.getDataFromUdmfRetry(event, callback); }, 1500); }
// 生成自定义背板图 private getComponentSnapshot(): void { this.getUIContext().getComponentSnapshot().createFromBuilder(() => { this.pixelMapBuilder(); }, (error: Error, pixmap: image.PixelMap) => { if (error) { console.log("error: " + JSON.stringify(error)); return; } this.pixmap = pixmap; }) }
// 长按准备阶段处理 private preDragChange(preDragStatus: PreDragStatus): void { if (preDragStatus === PreDragStatus.ACTION_DETECTING_STATUS) { this.getComponentSnapshot(); } }
build() { Row() { Column() { Text('拖动源') .fontSize(18) .width('100%') .height(40) .margin(10) .backgroundColor('#008888') Row() { Image($r('app.media.app_icon')) .width(100) .height(100) .draggable(true) .margin({ left: 15 }) .visibility(this.imgState) // 平行手势处理长按冲突 .parallelGesture(LongPressGesture().onAction(() => { this.getUIContext().getPromptAction().showToast({ duration: 100, message: '长按手势触发' }); })) .onDragStart((event) => { let data: unifiedDataChannel.Image = new unifiedDataChannel.Image(); data.imageUri = 'common/pic/img.png'; let unifiedData = new unifiedDataChannel.UnifiedData(data); event.setData(unifiedData);
let dragItemInfo: DragItemInfo = { pixelMap: this.pixmap, extraInfo: "拖拽背板额外信息", }; return dragItemInfo; }) .onPreDrag((status: PreDragStatus) => { this.preDragChange(status); }) .onDragEnd((event) => { if (event.getResult() === DragResult.DRAG_SUCCESSFUL) { this.getUIContext().getPromptAction().showToast({ duration: 100, message: '拖拽成功' }); } else if (event.getResult() === DragResult.DRAG_FAILED) { this.getUIContext().getPromptAction().showToast({ duration: 100, message: '拖拽失败' }); } }) } } Column() { Text('目标区域') .fontSize(20) .width('100%') .height(40) .margin(10) .backgroundColor('#008888') Row() { Image(this.targetImage) .width(this.imageWidth) .height(this.imageHeight) .draggable(true) .margin({ left: 15 }) .border({ color: Color.Black, width: 1 }) .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE]) .onDragMove((event) => { event.setResult(DragResult.DROP_ENABLED); event.dragBehavior = DragBehavior.MOVE; }) .onDrop((dragEvent?: DragEvent) => { this.getDataFromUdmf((dragEvent as DragEvent), (event: DragEvent) => { let records: Array<unifiedDataChannel.UnifiedRecord> = event.getData().getRecords(); let rect: Rectangle = event.getPreviewRect(); this.imageWidth = Number(rect.width); this.imageHeight = Number(rect.height); this.targetImage = (records[0] as unifiedDataChannel.Image).imageUri; this.imgState = Visibility.None; event.setResult(DragResult.DRAG_SUCCESSFUL); }); }) } } } .height('100%') }}
复制代码


发布于: 刚刚阅读数: 4
用户头像

GeorgeGcs

关注

路漫漫其修远兮,吾将上下而求索。 2024-12-24 加入

鸿蒙创作先锋,华为HDE专家,鸿蒙讲师,作者。 目前任职鸿蒙应用架构师。历经腾讯,宝马,研究所,金融。 待过私企,外企,央企。 深耕大应用开发领域十年。 OpenHarmony,HarmonyOS,Flutter,H5,Android,IOS。

评论

发布
暂无评论
【HarmonyOS】应用开发拖拽功能详解_GeorgeGcs_InfoQ写作社区