概述
拖拽操作是一種直觀且高效的數據傳輸方式,它允許用戶通過標準手勢(包括用手指、鼠標或觸控筆按住并移動)在應用程序之間及內部進行數據傳輸。
拖拽功能不僅操作便捷,還能與多種系統能力深度融合,拓展出更為廣泛的應用場景。例如,跨設備拖拽讓用戶能在不同設備間無縫傳輸數據,跨窗口拖拽提升了多任務處理的靈活性。此外,基于拖拽操作還可以開發出更多創新性的應用場景,如AI智能識別、水印添加等,這些創新性的功能接入統稱為“統一拖拽”。
本文將介紹幾種典型拖拽場景及其具體實現方案,幫助開發者更好地理解和應用拖拽技術。
通過設置組件的拖拽響應,可以自定義拖出數據、拖入數據和拖拽背板圖,實現如下場景:
拖拽圖像增加水印:為拖拽的圖像添加水印,水印內容為圖像的拖拽時間。開發者可以在應用時根據需求自定義水印內容,例如標記拖拽圖片的來源信息,為圖像管理與溯源提供便利。
自定義拖拽背板圖:將拖拽中的背板圖設置為自定義數據內容。開發者可根據個性化需求打造獨特的拖拽視覺效果。
AI識別拖拽內容:通過在接收拖拽內容時增加AI識別功能,使得只能顯示文字的組件可以接收圖片拖拽并顯示圖片中的文字信息。開發者可以將此能力應用于拖拽識圖搜索。
將拖拽框架與系統的分屏能力、鍵鼠穿越能力、小藝及中轉站結合,可以實現如下場景:
分屏拖拽:演示了分屏拖拽的功能,可以在分屏中打開兩個不同的應用,實現跨應用拖拽。
跨設備拖拽:演示了基于鍵鼠穿越能力的跨設備拖拽,可以在平板和2in1設備中使用此功能以直觀便捷地交換數據。
拖入小藝和中轉站:演示了小藝和中轉站與拖拽框架結合的能力,可以利用中轉站暫存拖拽內容或進行跨設備拖拽,也可以利用小藝的AI對話式分析能力處理拖拽內容。
實現原理
拖拽流程可以分為三部分:發起拖拽、拖拽中和釋放拖拽。其中,拖出方通過draggable()和onDragStart()等接口處理拖出數據,拖入方通過allowDrop()和onDrop()等接口處理拖入數據,拖拽數據使用UDMF統一數據對象UnifiedData 進行封裝。下面,將按照這三個部分依次介紹拖拽的基礎實現。
| 發起拖拽 | 拖拽中 | 釋放拖拽 |
![]() |
![]() |
![]() |
表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
+關注
關注
91文章
39747瀏覽量
301338 -
應用程序
+關注
關注
38文章
3344瀏覽量
60247 -
HarmonyOS
+關注
關注
80文章
2153瀏覽量
36037
原文標題:HarmonyOS應用統一拖拽解決方案
文章出處:【微信號:HarmonyOS_Dev,微信公眾號:HarmonyOS開發者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
基于Linux的LDAP統一認證解決方案
中央空調一拖一和一拖多的意思是什么 有什么區別
3CX統一通信解決方案,解決企業通信問題
HarmonyOS開發文檔(一)
意法半導體與中國一拖設立聯合實驗室,專注于農業電子解決方案
日立統一計算平臺選擇SAP HANA:融合橫向擴展解決方案
Hitachi統一計算平臺(UCP)解決方案與Brocade網絡
HarmonyOS應用統一拖拽解決方案



評論