1# Class (DragController) 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @jiangtao92--> 5<!--Designer: @piggyguy--> 6<!--Tester: @songyanhong--> 7<!--Adviser: @HelloCrease--> 8 9Provides APIs for initiating drag actions. When receiving a gesture event, such as a touch or long-press event, an application can initiate a drag action and carry drag information therein. 10 11> **NOTE** 12> 13> - The initial APIs of this module are supported since API version 10. Updates will be marked with a superscript to indicate their earliest API version. 14> 15> - The initial APIs of this class are supported since API version 11. 16> 17> - In the following API examples, you must first use [getDragController()](arkts-apis-uicontext-uicontext.md#getdragcontroller11) in **UIContext** to obtain a **DragController** instance, and then call the APIs using the obtained instance. 18 19## executeDrag<sup>11+</sup> 20 21executeDrag(custom: CustomBuilder | DragItemInfo, dragInfo: dragController.DragInfo, callback: AsyncCallback<dragController.DragEventParam>): void 22 23Initiates a drag action, with the object to be dragged and the drag information passed in. This API uses a callback to return the drag event result. 24 25**Atomic service API**: This API can be used in atomic services since API version 12. 26 27**System capability**: SystemCapability.ArkUI.ArkUI.Full 28 29**Parameters** 30 31| Name | Type | Mandatory| Description | 32| -------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 33| custom | [CustomBuilder](arkui-ts/ts-types.md#custombuilder8) \| [DragItemInfo](arkui-ts/ts-universal-events-drag-drop.md#dragiteminfo) | Yes | Object to be dragged.<br> **NOTE**<br>The global builder is not supported. If the [Image](arkui-ts/ts-basic-components-image.md) component is used in the builder, enable synchronous loading, that is, set the [syncLoad](arkui-ts/ts-basic-components-image.md#syncload8) attribute of the component to **true**. The builder is used only to generate the image displayed during the current dragging. If the root component of the builder has zero width or height, it will cause failure in drag image generation, which in turn breaks the entire drag operation. Changes to the builder, if any, apply to the next dragging, but not to the current dragging.| 34| dragInfo | [dragController.DragInfo](js-apis-arkui-dragController.md#draginfo) | Yes | Drag information. | 35| callback | [AsyncCallback](../apis-basic-services-kit/js-apis-base.md#asynccallback)<[dragController.DragEventParam](js-apis-arkui-dragController.md#drageventparam12)> | Yes | Callback used to return the result.<br>- **event**: drag event information that includes only the drag result.<br>- **extraParams**: extra information about the drag event.| 36 37**Error codes** 38 39For details about the error codes, see [Universal Error Codes](../errorcode-universal.md). 40 41| ID| Error Message | 42| -------- | ------------- | 43| 401 | Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2.Incorrect parameters types; 3. Parameter verification failed. | 44| 100001 | Internal handling failed. | 45 46**Example** 47 48```ts 49import { dragController } from '@kit.ArkUI'; 50import { unifiedDataChannel } from '@kit.ArkData'; 51 52@Entry 53@Component 54struct DragControllerPage { 55 @Builder DraggingBuilder() { 56 Column() { 57 Text("DraggingBuilder") 58 } 59 .width(100) 60 .height(100) 61 .backgroundColor(Color.Blue) 62 } 63 64 build() { 65 Column() { 66 Button('touch to execute drag') 67 .onTouch((event?: TouchEvent) => { 68 if (event) { 69 if (event.type == TouchType.Down) { 70 let text = new unifiedDataChannel.Text(); 71 let unifiedData = new unifiedDataChannel.UnifiedData(text); 72 73 let dragInfo: dragController.DragInfo = { 74 pointerId: 0, 75 data: unifiedData, 76 extraParams: '' 77 }; 78 class tmp { 79 event: DragEvent | undefined = undefined; 80 extraParams: string = ''; 81 } 82 let eve: tmp = new tmp(); 83 this.getUIContext().getDragController().executeDrag(() => { this.DraggingBuilder() }, dragInfo, (err, eve) => { 84 if (eve.event) { 85 if (eve.event.getResult() == DragResult.DRAG_SUCCESSFUL) { 86 // ... 87 } else if (eve.event.getResult() == DragResult.DRAG_FAILED) { 88 // ... 89 } 90 } 91 }) 92 } 93 } 94 }) 95 } 96 } 97} 98``` 99 100## executeDrag<sup>11+</sup> 101 102executeDrag(custom: CustomBuilder | DragItemInfo, dragInfo: dragController.DragInfo): Promise<dragController.DragEventParam> 103 104Initiates a drag action, with the object to be dragged and the drag information passed in. This API uses a promise to return the drag event result. 105 106**Atomic service API**: This API can be used in atomic services since API version 12. 107 108**System capability**: SystemCapability.ArkUI.ArkUI.Full 109 110**Parameters** 111 112| Name | Type | Mandatory| Description | 113| -------- | ------------------------------------------------------------ | ---- | -------------------------------- | 114| custom | [CustomBuilder](arkui-ts/ts-types.md#custombuilder8) \| [DragItemInfo](arkui-ts/ts-universal-events-drag-drop.md#dragiteminfo) | Yes | Object to be dragged.| 115| dragInfo | [dragController.DragInfo](js-apis-arkui-dragController.md#draginfo) | Yes | Drag information. | 116 117**Return value** 118 119| Type | Description | 120| ------------------------------------------------------------ | ------------------------------------------------------------ | 121| Promise<[dragController.DragEventParam](js-apis-arkui-dragController.md#drageventparam12)> | Promise used to return the result.<br>- **event**: drag event information that includes only the drag result.<br>- **extraParams**: extra information about the drag event.| 122 123**Error codes** 124 125For details about the error codes, see [Universal Error Codes](../errorcode-universal.md). 126 127| ID| Error Message | 128| -------- | ------------- | 129| 401 | Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2.Incorrect parameters types; 3. Parameter verification failed. | 130| 100001 | Internal handling failed. | 131 132**Example** 133 134```ts 135import { dragController } from '@kit.ArkUI'; 136import { image } from '@kit.ImageKit'; 137import { unifiedDataChannel } from '@kit.ArkData'; 138 139@Entry 140@Component 141struct DragControllerPage { 142 @State pixmap: image.PixelMap | null = null; 143 144 @Builder DraggingBuilder() { 145 Column() { 146 Text("DraggingBuilder") 147 } 148 .width(100) 149 .height(100) 150 .backgroundColor(Color.Blue) 151 } 152 153 @Builder PixmapBuilder() { 154 Column() { 155 Text("PixmapBuilder") 156 } 157 .width(100) 158 .height(100) 159 .backgroundColor(Color.Blue) 160 } 161 162 build() { 163 Column() { 164 Button('touch to execute drag') 165 .onTouch((event?: TouchEvent) => { 166 if (event) { 167 if (event.type == TouchType.Down) { 168 let text = new unifiedDataChannel.Text(); 169 let unifiedData = new unifiedDataChannel.UnifiedData(text); 170 171 let dragInfo: dragController.DragInfo = { 172 pointerId: 0, 173 data: unifiedData, 174 extraParams: '' 175 }; 176 let pb: CustomBuilder = (): void => { this.PixmapBuilder() }; 177 this.getUIContext().getComponentSnapshot().createFromBuilder(pb).then((pix: image.PixelMap) => { 178 this.pixmap = pix; 179 let dragItemInfo: DragItemInfo = { 180 pixelMap: this.pixmap, 181 builder: () => { this.DraggingBuilder() }, 182 extraInfo: "DragItemInfoTest" 183 }; 184 185 class tmp { 186 event: DragResult | undefined = undefined; 187 extraParams: string = ''; 188 } 189 let eve: tmp = new tmp(); 190 this.getUIContext().getDragController().executeDrag(dragItemInfo, dragInfo) 191 .then((eve) => { 192 if (eve.event.getResult() == DragResult.DRAG_SUCCESSFUL) { 193 // ... 194 } else if (eve.event.getResult() == DragResult.DRAG_FAILED) { 195 // ... 196 } 197 }) 198 .catch((err: Error) => { 199 }) 200 }) 201 } 202 } 203 }) 204 } 205 .width('100%') 206 .height('100%') 207 } 208} 209``` 210 211## createDragAction<sup>11+</sup> 212 213createDragAction(customArray: Array<CustomBuilder \| DragItemInfo>, dragInfo: dragController.DragInfo): dragController.DragAction 214 215Creates a drag action object for initiating drag and drop operations. You need to explicitly specify one or more drag previews, the drag data, and the drag handle point. If a drag operation initiated by an existing drag action object is not completed, no new object can be created, and calling the API will throw an exception. After the lifecycle of the drag action object ends, the callback functions registered on this object become invalid. Therefore, it is necessary to hold this object within a longer scope and replace the old value with a new object returned by **createDragAction** before each drag initiation. 216 217Note: You are advised to control the number of drag previews. If too many previews are passed in, the drag efficiency may be affected. 218 219**Atomic service API**: This API can be used in atomic services since API version 12. 220 221**System capability**: SystemCapability.ArkUI.ArkUI.Full 222 223**Parameters** 224 225| Name | Type | Mandatory| Description | 226| -------- | ------------------------------------------------------------ | ---- | -------------------------------- | 227| customArray | Array<[CustomBuilder](arkui-ts/ts-types.md#custombuilder8) \| [DragItemInfo](arkui-ts/ts-universal-events-drag-drop.md#dragiteminfo)> | Yes | Object to be dragged.| 228| dragInfo | [dragController.DragInfo](js-apis-arkui-dragController.md#draginfo) | Yes | Drag information. | 229 230**Return value** 231 232| Type | Description | 233| ------------------------------------------------------ | ------------------ | 234| [dragController.DragAction](js-apis-arkui-dragController.md#dragaction11)| **DragAction** object, which is used to subscribe to drag state changes and start the drag service.| 235 236**Error codes** 237 238For details about the error codes, see [Universal Error Codes](../errorcode-universal.md). 239 240| ID| Error Message | 241| -------- | ------------- | 242| 401 | Parameter error. Possible causes: 1. Mandatory parameters are left unspecified; 2.Incorrect parameters types; 3. Parameter verification failed. | 243| 100001 | Internal handling failed. | 244 245**Example** 246 2471. In the **EntryAbility.ets** file, obtain the UI context and save it to LocalStorage. 248 249```ts 250import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; 251import { hilog } from '@kit.PerformanceAnalysisKit'; 252import { window, UIContext } from '@kit.ArkUI'; 253 254let uiContext: UIContext; 255let localStorage: LocalStorage = new LocalStorage('uiContext'); 256 257export default class EntryAbility extends UIAbility { 258 storage: LocalStorage = localStorage; 259 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { 260 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 261 } 262 263 onDestroy(): void { 264 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 265 } 266 267 onWindowStageCreate(windowStage: window.WindowStage): void { 268 // Main window is created, set main page for this ability 269 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 270 271 windowStage.loadContent('pages/Index', this.storage, (err, data) => { 272 if (err.code) { 273 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 274 return; 275 } 276 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); 277 windowStage.getMainWindow((err, data) => 278 { 279 if (err.code) { 280 console.error('Failed to abtain the main window. Cause:' + err.message); 281 return; 282 } 283 let windowClass: window.Window = data; 284 uiContext = windowClass.getUIContext(); 285 this.storage.setOrCreate<UIContext>('uiContext', uiContext); 286 // Obtain a UIContext instance. 287 }); 288 }); 289 } 290 291 onWindowStageDestroy(): void { 292 // Main window is destroyed, release UI related resources 293 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); 294 } 295 296 onForeground(): void { 297 // Ability has brought to foreground 298 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 299 } 300 301 onBackground(): void { 302 // Ability has back to background 303 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 304 } 305} 306``` 3072. Call **this.getUIContext().getSharedLocalStorage()** to obtain the UI context and then use the **DragController** object obtained to perform subsequent operations. 308```ts 309import { dragController, componentSnapshot, UIContext, DragController } from '@kit.ArkUI'; 310import { image } from '@kit.ImageKit'; 311import { unifiedDataChannel } from '@kit.ArkData'; 312 313@Entry() 314@Component 315struct DragControllerPage { 316 @State pixmap: image.PixelMap | null = null; 317 private dragAction: dragController.DragAction | null = null; 318 customBuilders: Array<CustomBuilder | DragItemInfo> = new Array<CustomBuilder | DragItemInfo>(); 319 storages = this.getUIContext().getSharedLocalStorage(); 320 321 @Builder DraggingBuilder() { 322 Column() { 323 Text("DraggingBuilder") 324 } 325 .width(100) 326 .height(100) 327 .backgroundColor(Color.Blue) 328 } 329 330 build() { 331 Column() { 332 333 Column() { 334 Text("Test") 335 } 336 .width(100) 337 .height(100) 338 .backgroundColor(Color.Red) 339 340 Button('Drag Multiple Objects').onTouch((event?: TouchEvent) => { 341 if (event) { 342 if (event.type == TouchType.Down) { 343 console.info("muti drag Down by listener"); 344 this.customBuilders.push(() => { this.DraggingBuilder() }); 345 this.customBuilders.push(() => { this.DraggingBuilder() }); 346 this.customBuilders.push(() => { this.DraggingBuilder() }); 347 let text = new unifiedDataChannel.Text(); 348 let unifiedData = new unifiedDataChannel.UnifiedData(text); 349 let dragInfo: dragController.DragInfo = { 350 pointerId: 0, 351 data: unifiedData, 352 extraParams: '' 353 }; 354 try{ 355 let uiContext: UIContext = this.storages?.get<UIContext>('uiContext') as UIContext; 356 this.dragAction = uiContext.getDragController().createDragAction(this.customBuilders, dragInfo); 357 if (!this.dragAction) { 358 console.info("listener dragAction is null"); 359 return; 360 } 361 this.dragAction.on('statusChange', (dragAndDropInfo) => { 362 if (dragAndDropInfo.status == dragController.DragStatus.STARTED) { 363 console.info("drag has start"); 364 } else if (dragAndDropInfo.status == dragController.DragStatus.ENDED) { 365 console.info("drag has end"); 366 if (!this.dragAction) { 367 return; 368 } 369 this.customBuilders.splice(0, this.customBuilders.length); 370 this.dragAction.off('statusChange'); 371 } 372 }) 373 this.dragAction.startDrag().then(() => {}).catch((err: Error) => { 374 console.error("start drag Error:" + err.message); 375 }) 376 } catch(err) { 377 console.error("create dragAction Error:" + err.message); 378 } 379 } 380 } 381 }).margin({ top: 20 }) 382 } 383 } 384} 385``` 386 387## getDragPreview<sup>11+</sup> 388 389getDragPreview(): dragController.DragPreview 390 391Obtains the **DragPreview** object, which represents the preview displayed during a drag operation. 392 393**Atomic service API**: This API can be used in atomic services since API version 12. 394 395**System capability**: SystemCapability.ArkUI.ArkUI.Full 396 397**Return value** 398 399| Type | Description | 400| ------------------------------------------------------------ | ------------------------------------------------------------ | 401| [dragController.DragPreview](js-apis-arkui-dragController.md#dragpreview11) | **DragPreview** object. It provides the API for setting the preview style. It does not work in the **OnDrop** and **OnDragEnd** callbacks.| 402 403**Error codes**: For details about universal error codes, see [Universal Error Codes](../errorcode-universal.md). 404 405**Example** 406 407See the example for [animate](js-apis-arkui-dragController.md#animate11). 408 409## setDragEventStrictReportingEnabled<sup>12+</sup> 410 411setDragEventStrictReportingEnabled(enable: boolean): void 412 413Sets whether the **onDragLeave** callback of the parent component is triggered when an item is dragged from the parent to the child component. 414 415**Atomic service API**: This API can be used in atomic services since API version 12. 416 417**System capability**: SystemCapability.ArkUI.ArkUI.Full 418 419**Parameters** 420 421| Name| Type | Mandatory| Description | 422| ------ | ------- | ---- | ------------------------------------------------------------ | 423| enable | boolean | Yes | Whether the **onDragLeave** callback of the parent component is triggered when an item is dragged from the parent to the child component. The value **true** means the **onDragLeave** callback of the parent component is triggered, and **false** means the opposite.| 424 425**Example** 426 427```ts 428import { UIAbility } from '@kit.AbilityKit'; 429import { window, UIContext } from '@kit.ArkUI'; 430 431 export default class EntryAbility extends UIAbility { 432 onWindowStageCreate(windowStage: window.WindowStage): void { 433 windowStage.loadContent('pages/Index', (err, data) => { 434 if (err.code) { 435 return; 436 } 437 windowStage.getMainWindow((err, data) => { 438 if (err.code) { 439 return; 440 } 441 let windowClass: window.Window = data; 442 let uiContext: UIContext = windowClass.getUIContext(); 443 uiContext.getDragController().setDragEventStrictReportingEnabled(true); 444 }); 445 }); 446 } 447} 448``` 449 450## cancelDataLoading<sup>15+</sup> 451 452cancelDataLoading(key: string): void 453 454Cancels the data loading initiated by the [startDataLoading](arkui-ts/ts-universal-events-drag-drop.md#dragevent7) API. 455 456**Atomic service API**: This API can be used in atomic services since API version 15. 457 458**System capability**: SystemCapability.ArkUI.ArkUI.Full 459 460**Parameters** 461 462| Name| Type | Mandatory| Description | 463| ------ | ------- | ---- | ------------------------------------------------------------ | 464| key | string | Yes | Identifier for the drag data. It is used to distinguish between different drag operations. The key can be obtained through the **startDataLoading** API.| 465 466**Error codes** 467 468For details about the error codes, see [Universal Error Codes](../errorcode-universal.md) and [Drag Event Error Codes](./errorcode-drag-event.md). 469 470| ID| Error Message | 471| -------- | ------------------------------------------------------------ | 472| 401 | Parameter error. | 473| 190004 | Operation failed. | 474 475## notifyDragStartRequest<sup>18+</sup> 476 477notifyDragStartRequest(requestStatus: dragController.DragStartRequestStatus): void 478 479Controls whether the application can initiate a drag operation. 480 481**Atomic service API**: This API can be used in atomic services since API version 18. 482 483**System capability**: SystemCapability.ArkUI.ArkUI.Full 484 485**Parameters** 486 487| Name| Type | Mandatory| Description | 488| ------ | ------- | ---- | ------------------------------------------------------------ | 489| requestStatus | [dragController.DragStartRequestStatus](js-apis-arkui-dragController.md#dragstartrequeststatus18) | Yes |Whether the application can initiate a drag operation.| 490 491**Example** 492 493```ts 494import { unifiedDataChannel } from '@kit.ArkData'; 495import { image } from '@kit.ImageKit'; 496import { dragController } from '@kit.ArkUI'; 497 498// xxx.ets 499@Entry 500@Component 501struct NormalEts { 502 @State finished: boolean = false; 503 @State timeout1: number = 1; 504 @State pixmap: image.PixelMap | undefined = undefined; 505 @State unifiedData1: unifiedDataChannel.UnifiedData | undefined = undefined; 506 @State previewData: DragItemInfo | undefined = undefined; 507 508 loadData() { 509 let timeout = setTimeout(() => { 510 this.getUIContext().getComponentSnapshot().get("image1", (error: Error, pixmap: image.PixelMap) => { 511 this.pixmap = pixmap; 512 this.previewData = { 513 pixelMap: this.pixmap 514 }; 515 }); 516 517 let data: unifiedDataChannel.Image = new unifiedDataChannel.Image(); 518 data.imageUri = "app.media.startIcon"; 519 let unifiedData = new unifiedDataChannel.UnifiedData(data); 520 this.unifiedData1 = unifiedData; 521 522 this.getUIContext().getDragController().notifyDragStartRequest(dragController.DragStartRequestStatus.READY); 523 }, 4000); 524 this.timeout1 = timeout; 525 } 526 527 528 build() { 529 Column({space: 20}) { 530 Image($r("app.media.startIcon")) 531 .width(300) 532 .height(200) 533 .id("image1") 534 .draggable(true) 535 .dragPreview(this.previewData) 536 .onPreDrag((status: PreDragStatus) => { 537 if (status == PreDragStatus.PREPARING_FOR_DRAG_DETECTION) { 538 this.loadData(); 539 } else { 540 clearTimeout(this.timeout1); 541 } 542 }) 543 .onDragStart((event: DragEvent) => { 544 if (this.finished == false) { 545 this.getUIContext().getDragController().notifyDragStartRequest(dragController.DragStartRequestStatus.WAITING); 546 } else { 547 event.setData(this.unifiedData1); 548 } 549 }) 550 .onDragEnd(() => { 551 this.finished = false; 552 }) 553 } 554 .height(400) 555 .backgroundColor(Color.Pink) 556 } 557} 558``` 559## enableDropDisallowedBadge<sup>20+</sup> 560 561enableDropDisallowedBadge(enabled: boolean): void 562 563Specifies whether to enable the display of a disallowed badge when dragged content is incompatible with a component's configured [allowDrop](../apis-arkui/arkui-ts/ts-universal-attributes-drag-drop.md#allowdrop) types. With the display enabled, the system automatically shows a disallowed badge during drag operations when the dragged data types have no intersection with the target component's allowed drop types. This API currently does not support [UIExtension](../apis-arkui/js-apis-arkui-uiExtension.md). 564 565**Atomic service API**: This API can be used in atomic services since API version 20. 566 567**System capability**: SystemCapability.ArkUI.ArkUI.Full 568 569**Parameters** 570 571| Name| Type | Mandatory| Description | 572| ------ | ------- | ---- | ------------------------------------------------------------ | 573| enabled | boolean | Yes | Whether to enable the display of a disallowed badge when dragged content is incompatible with a component's configured [allowDrop](../apis-arkui/arkui-ts/ts-universal-attributes-drag-drop.md#allowdrop) types. The value **true** means to enable the display of a disallowed badge, and **false** means the opposite. The default value is **false**.| 574 575**Example** 576 577This example demonstrates how to use the **enableDropDisallowedBadge** API to enable the display of a disallowed badge when dragged content is incompatible with the target component's allowed drop types. 578 579```ts 580import { UIAbility } from '@kit.AbilityKit'; 581import { window, UIContext } from '@kit.ArkUI'; 582 583 export default class EntryAbility extends UIAbility { 584 onWindowStageCreate(windowStage: window.WindowStage): void { 585 windowStage.loadContent('pages/Index', (err, data) => { 586 if (err.code) { 587 return; 588 } 589 windowStage.getMainWindow((err, data) => { 590 if (err.code) { 591 return; 592 } 593 let windowClass: window.Window = data; 594 let uiContext: UIContext = windowClass.getUIContext(); 595 uiContext.getDragController().enableDropDisallowedBadge(true); 596 }); 597 }); 598 } 599} 600``` 601 602