概述
拖拽框架提供了一种通过鼠标或手势触屏的方式传递数据,即从一个组件位置拖出数据,并拖入到另一个组件位置上进行响应,拖出一方提供数据,拖入一方接收和处理数据。该操作可以让用户方便地移动、复制或删除指定内容。
- 拖拽操作:在某个能够响应拖出的组件上长按并滑动触发的拖拽行为,当用户释放时,拖拽操作结束;
- 拖拽背景(背板):用户所拖动数据的形象化表示,开发者可通过 onDragStart 的 CustomerBuilder 或 DragItemInfo 设置,也可以通过 dragPreview 通用属性设置;
- 拖拽内容:拖动的数据,使用UDMF统一API UnifiedData 进行封装;
- 拖出对象:触发拖拽操作并提供数据的组件;
- 拖入目标:可接收并处理拖动数据的组件;
- 拖拽点:鼠标或手指等与屏幕的接触位置,是否进入组件范围的判定是以接触点是否进入范围进行判断。
拖拽流程
手势拖拽
对于手势长按触发拖拽的场景,发起拖拽前框架侧会对当前组件是否可拖拽进行校验,针对默认可拖出的组件( Search 、 TextInput 、 TextArea 、 RichEditor 、 Text 、 Image 、 Hyperlink )需要判断是否设置了 draggable 属性为true(若系统使能分层参数,则draggable默认为true),其他组件需要额外判断是否设置了onDragStart回调函数,在满足上述可拖拽条件下,长按大于等于500ms可触发拖拽,长按800ms开始做预览图的浮起动效。
手势拖拽(手指/手写笔)触发拖拽流程:
鼠标拖拽
鼠标拖拽属于即拖即走,只要鼠标左键在可拖拽的组件上按下并移动大于1vp就可触发拖拽。
当前支持应用内和跨应用拖拽,提供了多个回调事件供开发者感知拖拽状态并干预系统默认拖拽行为,具体如下:
回调事件 | 说明 |
---|---|
onDragStart | 支持拖出的组件产生拖出动作时触发。该回调可以感知拖拽行为的发起,开发者可通过在 onDragStart 方法中设置拖拽所传递的数据以及自定义拖拽背板图。推荐开发者使用pixelmap的方式返回背板图,不推荐使用customBuilder的方式,会有额外的性能开销。 |
onDragEnter | 当拖拽活动的拖拽点进入组件范围内时触发,只有该组件监听了 onDrop 事件时,此回调才会被触发。 |
onDragMove | 拖拽点在组件范围内移动时触发;只有该组件监听了onDrop事件时,此回调才会被触发。 在此过程中可通过 DragEvent 中的setResult方法影响系统部分场景下的外观 1. 设置DragResult.DROP_ENABLED; 2. 设置DragResult.DROP_DISABLED。 |
onDragLeave | 拖拽点离开组件范围时触发;只有该组件监听了onDrop事件时,此回调才会被触发。 针对以下两种情况默认不会发送onDragLeave事件: 1. 父组件移动到子组件; 2. 目标组件与当前组件布局有重叠; API version 12开始可通过 UIContext 中的 setDragEventStrictReportingEnabled 方法严格触发onDragLeave事件。 |
onDrop | 当用户在组件范围内释放时触发,需在此回调中通过DragEvent中的setResult方法设置拖拽结果,否则在拖出方组件的onDragEnd方法中通过getRresult方法只能拿到默认的处理结果DragResult.DRAG_FAILED。 该回调也是开发者干预系统默认拖入处理行为的地方,系统会优先执行开发者的onDrop回调,通过在回调中执行setResult方法来告知系统该如何处理所拖拽的数据; 1. 设置 DragResult.DRAG_SUCCESSFUL,数据完全由开发者自己处理,系统不进行处理; 2. 设置DragResult.DRAG_FAILED,数据不再由系统继续处理; 3. 设置DragResult.DRAG_CANCELED,系统也不需要进行数据处理; 4. 设置DragResult.DROP_ENABLED或DragResult.DROP_DISABLED会被忽略,同设置DragResult.DRAG_FAILED; |
onDragEnd | 当用户释放拖拽时,拖拽活动结束,发起拖出动作的组件会触发该回调。 |
onPreDrag | 绑定此事件的组件,当触发拖拽发起前的不同阶段时,触发回调。 开发者可以使用该方法监听 PreDragStatus 中的枚举在发起拖拽前的不同阶段准备数据。 1. ACTION_DETECTING_STATUS:拖拽手势启动阶段。(按下50ms时触发); 2. READY_TO_TRIGGER_DRAG_ACTION:拖拽准备完成,可发起拖拽阶段。(按下500ms时触发); 3. PREVIEW_LIFT_STARTED:拖拽浮起动效发起阶段。(按下800ms时触发); 4. PREVIEW_LIFT_FINISHED:拖拽浮起动效结束阶段。(浮起动效完全结束时触发); 5. PREVIEW_LANDING_STARTED:拖拽落回动效发起阶段。(落回动效发起时触发); 6. PREVIEW_LANDING_FINISHED:拖拽落回动效结束阶段。(落回动效结束时触发); 7. ACTION_CANCELED_BEFORE_DRAG:拖拽浮起落位动效中断。(已满足READY_TO_TRIGGER_DRAG_ACTION状态后,未达到动效阶段,手指抬手时触发)。 |
拖拽背板图
拖拽移动过程中显示的拖拽背板图,并非是组件本身,其是用户拖动数据的表示,开发者可以将其设置为任意可显示的图像。其中onDragStart 回调返回的customBuilder或pixelmap可以设置拖拽移动过程中的背板图,浮起图默认使用组件本身的截图;dragpreview属性设置的customBuilder或pixelmap可以设置浮起和拖拽过程的背板图;如果开发者没有配置背板图,则系统会默认取组件本身的截图作为浮起及拖拽过程中的背板图。
拖拽背板图当前支持设置透明度、圆角、阴影和模糊,具体用法见: 拖拽控制
约束:
- 对于容器组件,如果内部内容通过position,offset等手段使得绘制区域超出了容器组件范围,则系统截图无法截取到范围之外的内容,此种情况下,如果一定要浮起及拖拽背板能够包含范围之外的内容,则可考虑通过扩大容器范围或自定义方式进行;
- 不管是使用自定义builder或是系统默认截图方式,截图都暂时无法应用 scale 、 rotate 等图形变换效果。
开发步骤
通用拖拽适配
如下以 Image 组件为例,介绍组件拖拽开发的基本步骤,以及开发中需要注意的事项。
- 组件使能拖拽
- 设置draggable属性为true,并设置onDragStart回调,回调中可以通过UDMF设置拖拽的数据,并返回自定义拖拽背板图;
import UDC from '@ohos.data.unifiedDataChannel';
import UTD from '@ohos.data.uniformTypeDescriptor';
Image($r('app.media.app_icon'))
.width(100)
.height(100)
.draggable(true)
.onDragStart((event) => {
let data: UDC.Image = new UDC.Image();
data.imageUri = 'common/pic/img.png';
let unifiedData = new UDC.UnifiedData(data);
event.setData(unifiedData);
let dragItemInfo: DragItemInfo = {
pixelMap: this.pixmap,
extraInfo: "this is extraInfo",
};
// onDragStart回调函数中返回自定义拖拽背板图
return dragItemInfo;
})
- 手势场景触发拖拽抵赖底层绑定的长按手势,若开发者在被拖拽组件上也绑定长按手势,则会与底层的长按手势发生竞争,导致拖拽失败。可以用并行手势解决解决此类问题,如下:
.par