0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

HarmonyOS应用统一拖拽解决方案

HarmonyOS开发者 来源:HarmonyOS开发者 2025-07-31 09:31 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

概述

拖拽操作是一种直观且高效的数据传输方式,它允许用户通过标准手势(包括用手指、鼠标或触控笔按住并移动)在应用程序之间及内部进行数据传输。

拖拽功能不仅操作便捷,还能与多种系统能力深度融合,拓展出更为广泛的应用场景。例如,跨设备拖拽让用户能在不同设备间无缝传输数据,跨窗口拖拽提升了多任务处理的灵活性。此外,基于拖拽操作还可以开发出更多创新性的应用场景,如AI智能识别、水印添加等,这些创新性的功能接入统称为“统一拖拽”。

本文将介绍几种典型拖拽场景及其具体实现方案,帮助开发者更好地理解和应用拖拽技术。

通过设置组件的拖拽响应,可以自定义拖出数据、拖入数据和拖拽背板图,实现如下场景:

拖拽图像增加水印:为拖拽的图像添加水印,水印内容为图像的拖拽时间。开发者可以在应用时根据需求自定义水印内容,例如标记拖拽图片的来源信息,为图像管理与溯源提供便利。

自定义拖拽背板图:将拖拽中的背板图设置为自定义数据内容。开发者可根据个性化需求打造独特的拖拽视觉效果。

AI识别拖拽内容:通过在接收拖拽内容时增加AI识别功能,使得只能显示文字的组件可以接收图片拖拽并显示图片中的文字信息。开发者可以将此能力应用于拖拽识图搜索。

将拖拽框架与系统的分屏能力、键鼠穿越能力、小艺及中转站结合,可以实现如下场景:

分屏拖拽:演示了分屏拖拽的功能,可以在分屏中打开两个不同的应用,实现跨应用拖拽。

跨设备拖拽:演示了基于键鼠穿越能力的跨设备拖拽,可以在平板和2in1设备中使用此功能以直观便捷地交换数据。

拖入小艺和中转站:演示了小艺和中转站与拖拽框架结合的能力,可以利用中转站暂存拖拽内容或进行跨设备拖拽,也可以利用小艺的AI对话式分析能力处理拖拽内容。

实现原理

拖拽流程可以分为三部分:发起拖拽、拖拽中和释放拖拽。其中,拖出方通过draggable()和onDragStart()等接口处理拖出数据,拖入方通过allowDrop()和onDrop()等接口处理拖入数据,拖拽数据使用UDMF统一数据对象UnifiedData 进行封装。下面,将按照这三个部分依次介绍拖拽的基础实现。

发起拖拽 拖拽中 释放拖拽
9473a2ee-6938-11f0-a6aa-92fbcf53809c.gif 948f3fc2-6938-11f0-a6aa-92fbcf53809c.gif 94b267b8-6938-11f0-a6aa-92fbcf53809c.gif

表1 拖拽流程展示

发起拖拽

默认支持拖出能力的组件,如Search、Hyperlink等,在拖出时会使用组件的默认拖出响应。其中Search组件默认拖拽内容为选中的文字,Hyperlink组件默认拖拽内容为超链接地址。如果想自定义组件的拖拽内容,需要在组件的onDragStart()接口中将自定义数据封装成UnifiedData数据对象,通过DragEvent的setData()接口设置拖出数据。对于其他非默认组件或自定义组件,如果想实现其拖出功能,需要将组件的draggable()属性设置为true,并自定义组件的拖拽内容。以Text组件为例,示例代码如下:

Text('自定义拖出响应,拖拽video')
 .draggable(true)
 .onDragStart((event) =>{
 // 处理拖出数据
 letvideo: unifiedDataChannel.Video=newunifiedDataChannel.Video();
  video.videoUri='/resources/rawfile/01.mp4';
 letdata: unifiedDataChannel.UnifiedData=newunifiedDataChannel.UnifiedData(video);
  (eventasDragEvent).setData(data);
 })

可以在onDragStart()中自由地处理拖拽信息,例如为图片添加水印,详情见拖拽图像增加水印。

拖拽中

通过标准手势发起拖拽后,系统会默认将组件本身的截图作为拖拽移动中的背板图。如果想自定义拖拽背板图,需要在组件的onDragStart()接口中通过回调的CustomBuilder或DragItemInfo进行设置。以Text组件为例,示例代码如下:

Text('自定义拖拽背板图')
 .draggable(true)
 .onDragStart(() =>{
 // 返回自定义背板图
 letdragItemInfo:DragItemInfo= {
  pixelMap:this.pixelMap,
  builder:() =>{this.pixelMapBuilder() },
  extraInfo:"this is extraInfo",
  };
 returndragItemInfo;
 })

可以将拖拽背板图设置为自定义的图片或者文字,详情见自定义拖拽背板图。

释放拖拽

默认支持拖入能力的组件,如Search等,将目标拖入组件区域内会使用默认拖入响应。如果想自定义组件的拖入响应,需要将组件的allowDrop()属性设置为允许拖入的数据类型,并在其onDrop()接口中通过DragEvent的getData()接口获取拖入数据后,对数据内容进行相应处理。

Text(this.targetText)
 .allowDrop([uniformTypeDescriptor.UniformDataType.PLAIN_TEXT])
 .onDrop((event: DragEvent) =>{
 // 处理拖入数据
 letrecords:Array = event.getData().getRecords();
 letplainText: unifiedDataChannel.PlainText= records[0]asunifiedDataChannel.PlainText;
 this.targetText= plainText.textContent;
 })

可以在onDrop()中处理接收到的数据,例如将图片识别为文字以显示在只支持文字的组件上,详情见AI识别拖拽内容。

拖拽图像增加水印

在拖拽过程中,可以自定义拖出响应,为拖拽图像增加水印,以标识图像的相关信息。下面以在图像中增加拖拽时间水印为例,介绍实现原理。

实现原理

在拖出对象的onDragStart()接口中获取图像信息,调用系统绘制能力drawing在图像上绘制水印,通过DragEvent的setData()接口将水印图像设置为拖拽数据。

开发步骤

1. 将Image的draggable()属性设置为true。

// src/main/ets/pages/watermark/Watermark.ets
Image($rawfile('river.png'))
// ...
 .draggable(true)

2. 在拖出对象的onDragStart()接口中,获取图像信息并将其转换成PixelMap。

.onDragStart((event: DragEvent) =>{
constresourceMgr: resourceManager.ResourceManager=this.context.resourceManager;
letrawFileDescriptor = resourceMgr.getRawFdSync('river.png');
constimageSourceApi: image.ImageSource= image.createImageSource(rawFileDescriptor);
letpixelMap = imageSourceApi.createPixelMapSync();
// ...
})

3. 将图片绘制到Canvas画布上,并获取拖拽时间作为水印绘制到画布上的指定位置,得到添加水印的图像。

// 获取拖拽时间
this.time=this.getTimeWatermark(systemDateTime.getTime(false));
letmarkPixelMap =this.addWaterMark(this.time, pixelMap);
// 绘制水印
addWaterMark(watermark:string, pixelMap: image.PixelMap) {
if(canIUse('SystemCapability.Graphics.Drawing')) {
  watermark =this.context.resourceManager.getStringSync($r('app.string.drag_time')) + watermark;
 letimageWidth = pixelMap.getImageInfoSync().size.width;
 letimageHeight = pixelMap.getImageInfoSync().size.height;
 letimageScale = imageWidth / display.getDefaultDisplaySync().width;
 constcanvas =newdrawing.Canvas(pixelMap);
 constpen =newdrawing.Pen();
 constbrush =newdrawing.Brush();
  pen.setColor({
  alpha:102,
  red:255,
  green:255,
  blue:255
  })
  brush.setColor({
  alpha:102,
  red:255,
  green:255,
  blue:255
  })
 constfont =newdrawing.Font();
  font.setSize(48* imageScale);
 lettextWidth = font.measureText(watermark, drawing.TextEncoding.TEXT_ENCODING_UTF8);
 consttextBlob = drawing.TextBlob.makeFromString(watermark, font, drawing.TextEncoding.TEXT_ENCODING_UTF8);
  canvas.attachBrush(brush);
  canvas.attachPen(pen);
  canvas.drawTextBlob(textBlob, imageWidth -24* imageScale - textWidth, imageHeight -32* imageScale);
  canvas.detachBrush();
  canvas.detachPen();
 }else{
  hilog.info(0x0000,TAG,'watermark is not supported');
 }
returnpixelMap;
}

4. 将图像打包保存在文件中,调用DragEvent的setData()接口将水印图像设置为拖拽数据。

letpackOpts: image.PackingOption= {format:'image/png',quality:20};
letfile =
 fs.openSync(`${this.context.filesDir}/watermark.png`, fs.OpenMode.CREATE| fs.OpenMode.READ_WRITE);
constimagePackerApi: image.ImagePacker= image.createImagePacker();
imagePackerApi.packToFile(markPixelMap, file.fd, packOpts);
letimg: unifiedDataChannel.Image=newunifiedDataChannel.Image();
img.imageUri= fileUri.getUriFromPath(`${this.context.filesDir}/watermark.png`);
letdata: unifiedDataChannel.UnifiedData=newunifiedDataChannel.UnifiedData(img);
(eventasDragEvent).setData(data);
fs.closeSync(file.fd);

自定义拖拽背板图

在拖拽过程中,可以自定义拖拽背板图,展示拖拽数据的相关信息。

实现原理

在拖出对象的onDragStart()接口中,回调自定义的PixelMap作为拖拽中的背板图。

开发步骤

1. 创建自定义组件。

// src/main/ets/pages/background/Background.ets
@Builder
pixelMapBuilder() {
Column() {
 Text($r('app.string.background_content'))
   .fontSize('16fp')
   .fontColor(Color.Black)
   .margin({
   left:'16vp',
   right:'16vp',
   top:'8vp',
   bottom:'8vp'
   })
 }
 .backgroundColor(Color.White)
 .borderRadius(18)
}

2. 将自定义组件转换成PixelMap,作为拖拽过程中显示的图片。

说明:由于CustomBuilder需要离线渲染之后才能使用,存在一定的性能开销和时延,因此推荐开发者优先使用DragItemInfo中的PixelMap方式返回背板图。

privategetComponentSnapshot():void{
this.getUIContext().getComponentSnapshot().createFromBuilder(() =>{
 this.pixelMapBuilder()
 },
 (error:Error, pixmap: image.PixelMap) =>{
  if(error) {
    hilog.error(0x0000,TAG,JSON.stringify(error));
   return;
   }
  this.pixelMap= pixmap;
  })
}

3. 在拖出对象的onDragStart()接口中,将回调的PixelMap作为拖拽中的背板图。

Image($r('app.media.mount'))
// ...
 .onDragStart(() =>{
 letdragItemInfo:DragItemInfo= {
  pixelMap:this.pixelMap,
  builder:() =>{
   this.pixelMapBuilder()
   },
  extraInfo:"this is extraInfo"
  };
 returndragItemInfo;
 })

AI识别拖拽内容

在拖拽过程中,可以自定义拖入响应,以识别拖拽内容并将其输出在释放区内。下面以通过AI识别拖拽图像中的文字为例,介绍实现原理。

实现原理

在拖入对象的onDrop()接口中,通过DragEvent的getData()接口获取拖拽数据后,调用系统文字识别能力textRecognition得到图像中的文字信息。

开发步骤

1. 在拖拽释放区域的allowDrop()接口中设置允许拖入的数据类型为uniformTypeDescriptor.UniformDataType.IMAGE。

// src/main/ets/pages/airecognition/AIRecognition.ets
Column() {
 Text(this.textContent)
 // ...
}
.allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])

2. 在拖入对象的onDrop()接口中,调用DragEvent的getData()接口获取拖拽数据。

.onDrop(async(event?:DragEvent) => {
letdragData:UnifiedData= (eventasDragEvent).getData()asUnifiedData;
// ...
letrecord:Array = dragData.getRecords();
// ...
letimageSource = record[0]asunifiedDataChannel.Image;
// ...
})

3. 将拖拽数据转换成颜色数据格式为RGBA_8888的PixelMap类型的视觉信息。

constresourceReg =newRegExp('resource');
if(resourceReg.test(imageSource.uri)) {
constnumberReg =newRegExp('[0-9]+');
letidArray = imageSource.uri.match(numberReg);
if(idArray !==null) {
 letid = idArray[0];
 letdrawableDescriptor =this.context.resourceManager.getDrawableDescriptor(Number(id),0,1);
 letpixelMapInit = drawableDescriptor.getPixelMap()asimage.PixelMap;
 letimageHeight = pixelMapInit.getImageInfoSync().size.height;
 letimageWidth = pixelMapInit.getImageInfoSync().size.width;
 constreadBuffer:ArrayBuffer=newArrayBuffer(imageHeight * imageWidth *4);
  pixelMapInit.readPixelsToBufferSync(readBuffer);
 letopts: image.InitializationOptions= {
  editable:true,
  size: {height: imageHeight,width: imageWidth },
  srcPixelFormat: pixelMapInit.getImageInfoSync().pixelFormat,
  pixelFormat:3,
  alphaType: pixelMapInit.getImageInfoSync().alphaType,
  scaleMode:0
  };
 letpixelMap: image.PixelMap= image.createPixelMapSync(readBuffer, opts);
 // ...
 }
}

4. 调用系统文字识别能力textRecognition获取拖拽数据中的文字信息。

letvisionInfo: textRecognition.VisionInfo= {
pixelMap: pixelMap
};
letdata =awaittextRecognition.recognizeText(visionInfo);
letrecognitionString = data.value;
this.textContent= recognitionString;

分屏拖拽

将拖拽框架与系统的分屏能力结合,可以将数据从一个分屏页面拖拽到另一个分屏页面,实现跨应用拖拽或同应用跨页面拖拽。

使用说明

需要开启软件的分屏权限,并根据需求自定义拖拽响应。

跨设备拖拽

将拖拽框架与系统的键鼠穿越能力结合,可以接入跨设备拖拽,实现在平板或2in1类型的任意两台设备之间拖拽数据。

使用说明

需要满足跨设备拖拽开发指导中的使用限制条件,并根据需求自定义拖拽响应。

拖入小艺和中转站

将数据拖入系统的中转站,可以实现跨应用数据拖拽和跨设备数据流转;将数据拖入小艺,可以利用系统的AI能力处理拖拽数据。

使用限制

应用本身预置的资源文件(即应用在安装前的HAP包中已经存在的资源文件)不支持拖入小艺和中转站。

常见问题-在模拟器中无法实现AI识别拖拽内容

问题现象

将图像拖拽至释放区,无法识别图像中的文字并输出在释放区内。

解决措施

模拟器不支持textRecognition接口的调用,建议使用真机进行调试,详细请参见模拟器与真机的差异。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • AI
    AI
    +关注

    关注

    88

    文章

    35758

    浏览量

    282463
  • 应用程序
    +关注

    关注

    38

    文章

    3339

    浏览量

    59217
  • HarmonyOS
    +关注

    关注

    80

    文章

    2130

    浏览量

    33670

原文标题:HarmonyOS应用统一拖拽解决方案

文章出处:【微信号:HarmonyOS_Dev,微信公众号:HarmonyOS开发者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    立体智慧仓储解决方案.#云计算

    解决方案智能设备
    学习电子知识
    发布于 :2022年10月06日 19:45:47

    基于Linux的LDAP统一认证解决方案

    设置成了“888888”等弱密码,由于各种软件的认证机制之间没有使用统一的标准,员工无法次性修改所有服务的密码,这导致很多即使是入职很久的员工都还在使用这个“众所周知”的密码。
    发表于 07-09 06:43

    如何实现变频器一拖二甚至一拖多功能?

    如何实现变频器一拖二甚至一拖多功能?
    发表于 11-02 06:17

    求助,请问如何发布我的HarmonyOS组件或解决方案

    我要如何发布我的HarmonyOS组件或解决方案
    发表于 06-02 15:55

    ADMS统一标准化集成管理解决方案

    Altium Designer中利用adms统一标准话集成管理解决方案
    发表于 03-22 17:44 0次下载

    中央空调一拖一拖多的意思是什么 有什么区别

    当室内机整体全开时,“一拖多”结构会比较省电,当某个房间单独开启时传统空调或者“一拖”结构的中央空调较为省电。
    发表于 08-29 14:55 7.8w次阅读

    3CX统一通信解决方案,解决企业通信问题

    3CX是个完整的统一通讯解决方案。将语音、视频、移动办公和信息服务集成在个平台上,因而不需要多台服务器来运行每个应用程序。次投入,永久
    的头像 发表于 12-05 15:20 6574次阅读

    HarmonyOS开发文档(

    华为自有开源操作系统鸿蒙OS,鸿蒙OS凭借多终端开发IDE,多语言统一编译,分布式架构Kit提供屏幕布局控件以及交互的自动适配,支持控件拖拽,面向预览的可视化编程,从而使开发者可以基于同工程高效
    发表于 10-15 14:15 62次下载
    <b class='flag-5'>HarmonyOS</b>开发文档(<b class='flag-5'>一</b>)

    意法半导体与中国一拖设立联合实验室,专注于农业电子解决方案

    集团有限公司(中国一拖)共同宣布,双方将在位于河南省洛阳市的中国一拖技术中心智能信息化研究院设立家联合实验室,专注于研发拖拉机的发动机、整车和农具控制系统的电子解决方案。 随着自动化
    的头像 发表于 11-27 11:43 2140次阅读

    HarmonyOS测试技术与实战-分布式应用测试解决方案

    HDC 2021华为开发者大会HarmonyOS测试技术与实战-HarmonyOS分布式应用测试解决方案
    的头像 发表于 10-23 14:48 1846次阅读
    <b class='flag-5'>HarmonyOS</b>测试技术与实战-分布式应用测试<b class='flag-5'>解决方案</b>

    一拖二快充数据线方案(采用LDR6020芯片,最高可做一拖五同时快充)

    一拖二快充数据线方案(采用LDR6020芯片,最高可做一拖五同时快充)
    的头像 发表于 05-10 16:11 1530次阅读
    <b class='flag-5'>一拖</b>二快充数据线<b class='flag-5'>方案</b>(采用LDR6020芯片,最高可做<b class='flag-5'>一拖</b>五同时快充)

    日立统一计算平台选择SAP HANA:融合横向扩展解决方案

    电子发烧友网站提供《日立统一计算平台选择SAP HANA:融合横向扩展解决方案.pdf》资料免费下载
    发表于 08-29 11:46 0次下载
    日立<b class='flag-5'>统一</b>计算平台选择SAP HANA:融合横向扩展<b class='flag-5'>解决方案</b>

    Hitachi统一计算平台(UCP)解决方案与Brocade网络

    电子发烧友网站提供《Hitachi统一计算平台(UCP)解决方案与Brocade网络.pdf》资料免费下载
    发表于 08-30 10:31 0次下载
    Hitachi<b class='flag-5'>统一</b>计算平台(UCP)<b class='flag-5'>解决方案</b>与Brocade网络

    Type-C一拖二/一拖三快充数据线方案介绍

    一拖二同时快充数据线方案种创新的充电解决方案,旨在满足用户同时给两个设备充电的需求,并且保证充电速度和效率。以下是个典型的
    的头像 发表于 04-15 15:29 3961次阅读
    Type-C<b class='flag-5'>一拖</b>二/<b class='flag-5'>一拖</b>三快充数据线<b class='flag-5'>方案</b>介绍

    LDR6500:低成本一拖二快充线解决方案

    一拖二快充线应运而生,它不仅解决了充电效率问题,还大大简化了我们的充电流程。 LDR6500一拖二快充芯片是专利产品,侵权必究,专利号:201210007717.X 、LDR6500一拖
    的头像 发表于 10-12 14:32 821次阅读