ToB企服应用市场:ToB评测及商务社交产业平台

标题: HarmonyOS NEXT实战(5.0 )ArkUI开发>拖拽框架事件 [打印本页]

作者: 十念    时间: 2024-10-27 08:06
标题: HarmonyOS NEXT实战(5.0 )ArkUI开发>拖拽框架事件
 鸿蒙NEXT开发实战往期必看文章:

HarmonyOS NEXT应用开发案例实践总联合(持续更新......)
HarmonyOS NEXT应用开发性能优化实践总结(持续更新......)
一分钟了解”纯血版!鸿蒙HarmonyOS Next应用开发!
“非常详细的” 鸿蒙HarmonyOS Next应用开发学习蹊径!(从零底子入门到醒目)

概述

拖拽框架提供了一种通过鼠标或手势触屏的方式通报数据,即从一个组件位置拖出数据,并拖入到另一个组件位置上进行响应,拖出一方提供数据,拖入一方接收和处理数据。该操作可以让用户方便地移动、复制或删除指定内容。

拖拽流程

​手势拖拽

​对于手势长按触发拖拽的场景,发起拖拽前框架侧会对当前组件是否可拖拽进行校验,针对默承认拖出的组件(Search、TextInput、TextArea、RichEditor、Text、Image、FormComponent、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可以设置浮起和拖拽过程的背板图;如果开发者没有配置背板图,则体系会默认取组件本身的截图作为浮起及拖拽过程中的背板图。
拖拽背板图当前支持设置透明度、圆角、阴影和含糊,详细用法见:拖拽控制
约束:

开发步骤

通用拖拽适配

如下以Image组件为例,先容组件拖拽开发的根本步骤,以及开发中需要留意的事项。




多选拖拽适配

API version 12开始Grid组件和List组件中的GridItem和ListItem组件支持多选拖拽,当前只支持onDragStart的方式。如下以Grid为例,先容多选拖拽的根本步骤,以及开发中的留意事项。


完备示例

通用拖拽适配案例

  1. import UDC from '@ohos.data.unifiedDataChannel';
  2. import UTD from '@ohos.data.uniformTypeDescriptor';
  3. import promptAction from '@ohos.promptAction';
  4. import { BusinessError } from '@ohos.base';
  5. import image from '@ohos.multimedia.image'
  6. import componentSnapshot from '@ohos.arkui.componentSnapshot'
  7. @Entry
  8. @Component
  9. struct Index {
  10.   @State targetImage: string = '';
  11.   @State imageWidth: number = 100;
  12.   @State imageHeight: number = 100;
  13.   @State imgState: Visibility = Visibility.Visible;
  14.   @State pixmap: image.PixelMap|undefined = undefined
  15.   @Builder
  16.   pixelMapBuilder() {
  17.     Column() {
  18.       Image($r('app.media.startIcon'))
  19.         .width(120)
  20.         .height(120)
  21.         .backgroundColor(Color.Yellow)
  22.     }
  23.   }
  24.   getDataFromUdmfRetry(event: DragEvent, callback: (data: DragEvent) => void) {
  25.     try {
  26.       let data: UnifiedData = event.getData();
  27.       if (!data) {
  28.         return false;
  29.       }
  30.       let records: Array<UDC.UnifiedRecord> = data.getRecords();
  31.       if (!records || records.length <= 0) {
  32.         return false;
  33.       }
  34.       callback(event);
  35.       return true;
  36.     } catch (e) {
  37.       console.log("getData failed, code: " + (e as BusinessError).code + ", message: " + (e as BusinessError).message);
  38.       return false;
  39.     }
  40.   }
  41.   // 获取UDMF数据,首次获取失败后添加1500ms延迟重试机制
  42.   getDataFromUdmf(event: DragEvent, callback: (data: DragEvent) => void) {
  43.     if (this.getDataFromUdmfRetry(event, callback)) {
  44.       return;
  45.     }
  46.     setTimeout(() => {
  47.       this.getDataFromUdmfRetry(event, callback);
  48.     }, 1500);
  49.   }
  50.   // 调用componentSnapshot中的createFromBuilder接口截取自定义builder的截图
  51.   private getComponentSnapshot(): void {
  52.     componentSnapshot.createFromBuilder(()=>{this.pixelMapBuilder()},
  53.       (error: Error, pixmap: image.PixelMap) => {
  54.         if(error){
  55.           console.log("error: " + JSON.stringify(error))
  56.           return;
  57.         }
  58.         this.pixmap = pixmap;
  59.       })
  60.   }
  61.   // 长按50ms时提前准备自定义截图的pixmap
  62.   private PreDragChange(preDragStatus: PreDragStatus): void {
  63.     if (preDragStatus == PreDragStatus.ACTION_DETECTING_STATUS) {
  64.       this.getComponentSnapshot();
  65.     }
  66.   }
  67.   build() {
  68.     Row() {
  69.       Column() {
  70.         Text('start Drag')
  71.           .fontSize(18)
  72.           .width('100%')
  73.           .height(40)
  74.           .margin(10)
  75.           .backgroundColor('#008888')
  76.         Row() {
  77.           Image($r('app.media.app_icon'))
  78.             .width(100)
  79.             .height(100)
  80.             .draggable(true)
  81.             .margin({ left: 15 })
  82.             .visibility(this.imgState)
  83.             // 绑定平行手势,可同时触发应用自定义长按手势
  84.             .parallelGesture(LongPressGesture().onAction(() => {
  85.               promptAction.showToast({ duration: 100, message: 'Long press gesture trigger' });
  86.             }))
  87.             .onDragStart((event) => {
  88.               let data: UDC.Image = new UDC.Image();
  89.               data.imageUri = 'common/pic/img.png';
  90.               let unifiedData = new UDC.UnifiedData(data);
  91.               event.setData(unifiedData);
  92.               let dragItemInfo: DragItemInfo = {
  93.                 pixelMap: this.pixmap,
  94.                 extraInfo: "this is extraInfo",
  95.               };
  96.               return dragItemInfo;
  97.             })
  98.               // 提前准备拖拽自定义背板图
  99.             .onPreDrag((status: PreDragStatus) => {
  100.               this.PreDragChange(status);
  101.             })
  102.             .onDragEnd((event) => {
  103.               // onDragEnd里取到的result值在接收方onDrop设置
  104.               if (event.getResult() === DragResult.DRAG_SUCCESSFUL) {
  105.                 promptAction.showToast({ duration: 100, message: 'Drag Success' });
  106.               } else if (event.getResult() === DragResult.DRAG_FAILED) {
  107.                 promptAction.showToast({ duration: 100, message: 'Drag failed' });
  108.               }
  109.             })
  110.         }
  111.         Text('Drag Target Area')
  112.           .fontSize(20)
  113.           .width('100%')
  114.           .height(40)
  115.           .margin(10)
  116.           .backgroundColor('#008888')
  117.         Row() {
  118.           Image(this.targetImage)
  119.             .width(this.imageWidth)
  120.             .height(this.imageHeight)
  121.             .draggable(true)
  122.             .margin({ left: 15 })
  123.             .border({ color: Color.Black, width: 1 })
  124.             // 控制角标显示类型为MOVE,即不显示角标
  125.             .onDragMove((event) => {
  126.               event.setResult(DragResult.DROP_ENABLED)
  127.               event.dragBehavior = DragBehavior.MOVE
  128.             })
  129.             .allowDrop([UTD.UniformDataType.IMAGE])
  130.             .onDrop((dragEvent?: DragEvent) => {
  131.               // 获取拖拽数据
  132.               this.getDataFromUdmf((dragEvent as DragEvent), (event: DragEvent) => {
  133.                 let records: Array<UDC.UnifiedRecord> = event.getData().getRecords();
  134.                 let rect: Rectangle = event.getPreviewRect();
  135.                 this.imageWidth = Number(rect.width);
  136.                 this.imageHeight = Number(rect.height);
  137.                 this.targetImage = (records[0] as UDC.Image).imageUri;
  138.                 this.imgState = Visibility.None;
  139.                 // 显式设置result为successful,则将该值传递给拖出方的onDragEnd
  140.                 event.setResult(DragResult.DRAG_SUCCESSFUL);
  141.               })
  142.             })
  143.         }
  144.       }
  145.       .width('100%')
  146.       .height('100%')
  147.     }
  148.     .height('100%')
  149.   }
  150. }
  151. <strong>ts</strong>
复制代码
多选拖拽适配案例

  1. import componentSnapshot from "@ohos.arkui.componentSnapshot";
  2. import image from '@ohos.multimedia.image'
  3. @Entry
  4. @Component
  5. struct GridEts {
  6.   @State pixmap: image.PixelMap|undefined = undefined
  7.   @State numbers: number[] = []
  8.   @State isSelectedGrid: boolean[] = []
  9.   @State previewData: DragItemInfo[] = []
  10.   @State colors: Color[] = [Color.Red, Color.Blue, Color.Brown, Color.Gray, Color.Green, Color.Grey, Color.Orange,Color.Pink ,Color.Yellow]
  11.   @State numberBadge: number = 0;
  12.   @Styles
  13.   normalStyles(): void{
  14.     .opacity(1.0)
  15.   }
  16.   @Styles
  17.   selectStyles(): void{
  18.     .opacity(0.4)
  19.   }
  20.   onPageShow(): void {
  21.     let i: number = 0
  22.     for(i=0;i<100;i++){
  23.       this.numbers.push(i)
  24.       this.isSelectedGrid.push(false)
  25.       this.previewData.push({})
  26.     }
  27.   }
  28.   @Builder
  29.   RandomBuilder(idx: number) {
  30.     Column()
  31.       .backgroundColor(this.colors[idx % 9])
  32.       .width(50)
  33.       .height(50)
  34.       .opacity(1.0)
  35.   }
  36.   build() {
  37.     Column({ space: 5 }) {
  38.       Grid() {
  39.         ForEach(this.numbers, (idx: number) => {
  40.           GridItem() {
  41.             Column()
  42.               .backgroundColor(this.colors[idx % 9])
  43.               .width(50)
  44.               .height(50)
  45.               .opacity(1.0)
  46.               .id('grid'+idx)
  47.           }
  48.           .dragPreview(this.previewData[idx])
  49.           .selectable(true)
  50.           .selected(this.isSelectedGrid[idx])
  51.           // 设置多选显示效果
  52.           .stateStyles({
  53.             normal : this.normalStyles,
  54.             selected: this.selectStyles
  55.           })
  56.           .onClick(()=>{
  57.             this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
  58.             if (this.isSelectedGrid[idx]) {
  59.               this.numberBadge++;
  60.               let gridItemName = 'grid' + idx
  61.               // 选中状态下提前调用componentSnapshot中的get接口获取pixmap
  62.               componentSnapshot.get(gridItemName, (error: Error, pixmap: image.PixelMap)=>{
  63.                 this.pixmap = pixmap
  64.                 this.previewData[idx] = {
  65.                   pixelMap:this.pixmap
  66.                 }
  67.               })
  68.             } else {
  69.               this.numberBadge--;
  70.             }
  71.           })
  72.           // 使能多选拖拽,右上角数量角标需要应用设置numberBadge参数
  73.           .dragPreviewOptions({numberBadge: this.numberBadge},{isMultiSelectionEnabled:true,defaultAnimationBeforeLifting:true})
  74.           .onDragStart(()=>{
  75.           })
  76.         }, (idx: string) => idx)
  77.       }
  78.       .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
  79.       .columnsGap(5)
  80.       .rowsGap(10)
  81.       .backgroundColor(0xFAEEE0)
  82.     }.width('100%').margin({ top: 5 })
  83.   }
  84. }
复制代码



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4