1# Drag and Drop Control 2 3The drag and drop control attributes set whether a component can respond to drag events. 4 5> **NOTE** 6> 7> The APIs of this module are supported since API version 10. Updates will be marked with a superscript to indicate their earliest API version. 8 9The ArkUI framework provides default drag and drop capabilities for the following components, allowing them to serve as the drag source (from which data can be dragged) or drop target (to which data can be dropped). You can also define drag responses by implementing common drag events. 10 11- The following component supports drag actions by default: [Search](ts-basic-components-search.md), [TextInput](ts-basic-components-textinput.md), [TextArea](ts-basic-components-textarea.md), [RichEditor](ts-basic-components-richeditor.md), [Text](ts-basic-components-text.md), [Image](ts-basic-components-image.md), <!--Del-->[FormComponent](ts-basic-components-formcomponent-sys.md), <!--DelEnd-->[Hyperlink](ts-container-hyperlink.md). You can control the default drag behavior by setting the [draggable](ts-universal-attributes-drag-drop.md#draggable) attribute. 12 13- The following component supports drop actions by default: [Search](ts-basic-components-search.md), [TextInput](ts-basic-components-textinput.md), [TextArea](ts-basic-components-textarea.md), [RichEditor](ts-basic-components-richeditor.md). You can disable the default drag behavior by setting the [allowDrop](ts-universal-attributes-drag-drop.md#allowdrop) attribute to **null**. 14 15<!--RP1--><!--RP1End-->To enable drag and drop for other components, you need to set the **draggable** attribute to **true** and implement data transmission in APIs such as **onDragStart**. 16 17> **NOTE** 18> 19> When using the **Text** component, set [copyOption](ts-basic-components-text.md#copyoption9) to **CopyOptions.InApp** or **CopyOptions.LocalDevice**. 20 21## allowDrop 22 23allowDrop(value: Array<UniformDataType> | null) 24 25Sets the type of data that can be dropped to the component. 26 27**Atomic service API**: This API can be used in atomic services since API version 11. 28 29**System capability**: SystemCapability.ArkUI.ArkUI.Full 30 31**Parameters** 32 33| Name| Type | Mandatory| Description | 34| ------ | ------------------------------------------------------------ | ---- | ----------------------------------------------- | 35| value | Array\<[UniformDataType](../../apis-arkdata/js-apis-data-uniformTypeDescriptor.md#uniformdatatype)> \| null<sup>12+</sup> | Yes | Type of data that can be dropped to the component. Since API version 12, this parameter can be set to **null** to make the component reject all data types.<br>Default value: empty| 36 37## draggable 38 39draggable(value: boolean) 40 41Sets whether the component is draggable. 42 43**Atomic service API**: This API can be used in atomic services since API version 11. 44 45**System capability**: SystemCapability.ArkUI.ArkUI.Full 46 47**Parameters** 48 49| Name| Type | Mandatory| Description | 50| ------ | ------- | ---- | ---------------------------------------------- | 51| value | boolean | Yes | Whether the component is draggable. <br>**true**: The component is draggable.<br>**false**: The component is not draggable.<br>Default value: **false**| 52 53## dragPreview<sup>11+</sup> 54 55dragPreview(value: CustomBuilder | DragItemInfo | string) 56 57Sets the preview displayed when the component is dragged. 58 59**Atomic service API**: This API can be used in atomic services since API version 12. 60 61**System capability**: SystemCapability.ArkUI.ArkUI.Full 62 63**Parameters** 64 65| Name| Type | Mandatory| Description | 66| ------ | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 67| value | [CustomBuilder](ts-types.md#custombuilder8) \| [DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo) \| string<sup>12+</sup> | Yes | Preview displayed when the component is dragged. This attribute has effect for **onDragStart** only.<br>If the component supports drag and drop and a preview is specified through [bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8), that specified preview is displayed when the component is dragged. The priority of the background image returned in [onDragStart](ts-universal-events-drag-drop.md#onDragStart) is lower than that of the preview set in [dragPreview](ts-universal-attributes-drag-drop.md#dragPreview11). This means that, once set, the latter will be used in place of the former. Because [CustomBuilder](ts-types.md#custombuilder8) can be used only after offline rendering, it may increase performance overhead and latency. In light of this, you are advised to use [PixelMap](../../apis-image-kit/js-apis-image.md#pixelmap7) in [DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo) to set the preview.<br> When an ID of the string type is passed in, the snapshot of the component assigned the ID is used as the preview image. If the component assigned the ID cannot be found or its **Visibility** attribute is set to **none** or **hidden**, a snapshot of the current component is used as the preview image. Currently, snapshots do not support visual effects, such as brightness, shadow, blur, and rotation.<br>Default value: empty<br>| 68 69## dragPreview<sup>15+</sup> 70 71dragPreview(preview: CustomBuilder | DragItemInfo | string, config?: PreviewConfiguration):T 72 73Sets the preview displayed when the component is dragged. It is used only for setting or disabling the lifting effect. 74 75**Atomic service API**: This API can be used in atomic services since API version 15. 76 77**System capability**: SystemCapability.ArkUI.ArkUI.Full 78 79**Parameters** 80 81| Name| Type | Mandatory| Description | 82| ------ | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 83| preview | [CustomBuilder](ts-types.md#custombuilder8) \| [DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo) \| string | Yes | Preview displayed when the component is dragged. This attribute has effect for **onDragStart** only.<br>If the component supports drag and drop and a preview is specified through [bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8), that specified preview is displayed when the component is dragged. The priority of the background image returned in [onDragStart](ts-universal-events-drag-drop.md#ondragstart) is lower than that of the preview set in [dragPreview](ts-universal-attributes-drag-drop.md#dragpreview11). This means that, once set, the latter will be used in place of the former. Because [CustomBuilder](ts-types.md#custombuilder8) can be used only after offline rendering, it may increase performance overhead and latency. In light of this, you are advised to use [PixelMap](../../apis-image-kit/js-apis-image.md#pixelmap7) in [DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo) to set the preview.<br> When an ID of the string type is passed in, the snapshot of the component assigned the ID is used as the preview image. If the component assigned the ID cannot be found or its **Visibility** attribute is set to **none** or **hidden**, a snapshot of the current component is used as the preview image. Currently, snapshots do not support visual effects, such as brightness, shadow, blur, and rotation.<br>Default value: empty| 84| config | [PreviewConfiguration](ts-universal-events-drag-drop.md#previewconfiguration15) | Yes| Additional settings for the drag preview.<br>This parameter is effective only for previews set using [dragPreview](#dragpreview11).<br>Default value: empty| 85 86## dragPreviewOptions<sup>11+</sup> 87 88dragPreviewOptions(value: DragPreviewOptions, options?: DragInteractionOptions) 89 90Sets the processing mode of the drag preview and the display of the number badge during dragging. The **onItemDragStart** dragging mode is not supported. 91 92**Atomic service API**: This API can be used in atomic services since API version 12. 93 94**System capability**: SystemCapability.ArkUI.ArkUI.Full 95 96**Parameters** 97 98| Name| Type | Mandatory| Description | 99| ------ | -------------------------------------------------------------- | ---- | ------------------------------------------------------------ | 100| value | [DragPreviewOptions](#dragpreviewoptions11)<sup>11+</sup> | Yes | Processing mode of the drag preview and the display of the number badge during dragging.<br>Default value: empty| 101| options<sup>12+</sup>| [DragInteractionOptions](#draginteractionoptions12)<sup>12+</sup>| No | Interaction mode of the drag preview.<br>Default value: empty| 102 103## DragPreviewOptions<sup>11+</sup> 104 105**Atomic service API**: This API can be used in atomic services since API version 12. 106 107| Name| Type| Mandatory| Description| 108| -------- | -------- | -------- | -------- | 109| mode | [DragPreviewMode](#dragpreviewmode11) \| Array<[DragPreviewMode](#dragpreviewmode11)><sup>12+</sup>| No| How the background image is processed when the component is dragged.<br>Default value: **DragPreviewMode.AUTO**<br>If **DragPreviewMode.AUTO** is along with other enum values, the setting takes precedence with **DragPreviewMode.AUTO**, and other enum values do not take effect.| 110| numberBadge<sup>12+</sup> | boolean \| number | No| Whether to display the number badge or the number displayed on the badge. For a number badge, the value range is [0, 2<sup>31</sup>-1]. Values outside this range will be processed as the default state. If the value specified is a floating-point number, only the integer part is displayed.<br>**NOTE**<br>When multiple items are dragged, use this API to set the number of items dragged.<br>Default value: **true**| 111| modifier<sup>12+</sup> | [ImageModifier](ts-universal-attributes-attribute-modifier.md)| No| Style modifier to apply to the drag preview. You can use the attributes and styles supported by the image component to configure the drag preview style (see example 6). Currently, opacity, shadow, background blur, and rounded corners are supported. This parameter does not work for text dragging, which only supports the default effect.<br>1. Opacity<br>Use the [opacity](ts-universal-attributes-opacity.md#opacity) attribute to set the opacity. The value ranges from 0 to 1. If the value is set to **0** or left unspecified, it reverts to the default value **0.95**. Setting it to **1** or an invalid value makes the object completely opaque.<br>2. Shadow<br>Use the [shadow](ts-universal-attributes-image-effect.md#shadow) attribute to set the shadow.<br>3. Background blur<br>Use the [backgroundEffect](ts-universal-attributes-background.md#backgroundeffect11) or [backgroundBlurStyle](ts-universal-attributes-background.md#backgroundblurstyle18) attribute to set the background blur. If both are used, **backgroundEffect** takes precedence.<br>4. Rounded corner<br>Use the [border](ts-universal-attributes-border.md#border) or [borderRadius](ts-universal-attributes-border.md#borderRadius) attribute to set rounded corners. If you set rounded corners in both **mode** and **modifier**, the settings in **modifier** prevail.<br>Default value: empty. The attribute cannot be modified.| 112| sizeChangeEffect<sup>18+</sup> | [DraggingSizeChangeEffect](#draggingsizechangeeffect18)<sup>18+</sup> | No| Transition effect for switching between the long-press preview and the drag preview.<br>Default value: **DraggingSizeChangeEffect.DEFAULT**| 113 114## DragPreviewMode<sup>11+</sup> 115 116**Atomic service API**: This API can be used in atomic services since API version 12. 117 118| Name| Value| Description| 119| -------- | ------- | -------- | 120| AUTO | 1 | Enables the system to automatically change the position of the dragged point based on the scenario and apply scaling transformations to the drag preview based on set rules.| 121| DISABLE_SCALE | 2 | Disables the system's scaling behavior for the drag preview.| 122| ENABLE_DEFAULT_SHADOW<sup>12+</sup> | 3 | Enables the default shadow effect for non-text components.| 123| ENABLE_DEFAULT_RADIUS<sup>12+</sup> | 4 | Enables a unified rounded corner effect for non-text components, with the default value of 12 vp. If the custom rounded corner value set by the application is greater than the default value or the value set by **modifier**, the custom value is used.| 124| ENABLE_DRAG_ITEM_GRAY_EFFECT<sup>18+</sup> | 5 | Enables the gray (transparency) effect for the original drag item, which does not apply to text content dragging. When the user starts dragging, the original item displays a gray effect. When released, the original item returns to its original appearance. After enabling the default gray effect, avoid manually modifying the opacity after dragging starts. Otherwise, the gray effect will be overridden, and the original opacity will not be correctly restored when dragging ends.| 125| ENABLE_MULTI_TILE_EFFECT<sup>18+</sup> | 6 | Enables the effect where multiple selected objects do not cluster when dragged with the mouse. This parameter takes effect only when** isMultiSelectionEnabled** is **true** and effective under multi-selection conditions. The non-clustering effect has a higher priority than [dragPreview](#dragpreview11). This setting does not support secondary dragging, rounded corners, or scaling.| 126| ENABLE_TOUCH_POINT_CALCULATION_BASED_ON_FINAL_PREVIEW<sup>18+</sup> | 7 | Whether to enable the calculation of touch point positions based on the initial size of the drag preview. This option has no effect when **DragPreviewMode.ENABLE_MULTI_TILE_EFFECT** is set.| 127 128## DraggingSizeChangeEffect<sup>18+</sup> 129 130Enumerates the transition effects for switching between the long-press preview (set through [bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu12)) and the drag preview when both are configured on a component. 131 132**Atomic service API**: This API can be used in atomic services since API version 18. 133 134**System capability**: SystemCapability.ArkUI.ArkUI.Full 135 136| Name| Value| Description| 137| -------- | ------- | -------- | 138| DEFAULT | 0 | Directly switches from the long-press preview to the final drag preview at its full size when dragging begins.| 139| SIZE_TRANSITION | 1 | Switches from the long-press preview to the drag preview directly, but transitions the size from the long-press preview size to the final drag preview size. This is used when the long-press preview and drag preview are the same.| 140| SIZE_CONTENT_TRANSITION | 2 | Gradually transitions from the long-press preview to the final drag preview, including changes in content opacity and size. This is used when the long-press preview and drag preview have significant differences.| 141 142 143## DragInteractionOptions<sup>12+</sup> 144 145**Atomic service API**: This API can be used in atomic services since API version 12. 146 147| Name| Type| Mandatory| Description| 148| -------- | -------- | -------- | -------- | 149| isMultiSelectionEnabled | boolean | No| Whether to enable multiselect for the drag preview. <br>**true**: Enable multiselect for the drag preview.<br>**false**: Disable multiselect for the drag preview.<br> This parameter takes effect only for the [grid items](ts-container-griditem.md) and [list items](ts-container-listitem.md) in the [Grid](ts-container-grid.md) and [List](ts-container-list.md) containers.<br>When multiselect is enabled for an item, the child components of the item cannot be dragged. The precendence levels of drag previews for multiselect, from high to low, are as follows: preview specified through a string value in [dragPreview](#dragpreview11), preview specified through **PixelMap** in **dragPreview**, and component snapshot. The Builder format in **dragPreview** is not supported.<br>The context menu bound to the component through [bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu12) cannot contain the **isShown** parameter.<br>Default value: **false**<br>| 150| defaultAnimationBeforeLifting | boolean | No| Whether to enable the default pressed state animation (compressing in size) of the component before a lift animation starts. <br>**true**: Enable the default pressed state animation.<br>**false**: Disable the default pressed state animation.<br>Default value: **false**<br>| 151| isLiftingDisabled<sup>15+</sup> | boolean | No| Whether to disable the lifting effect during dragging. <br>**true**: Disable the lifting effect during dragging.<br>**false**: Enable the lifting effect during dragging.<br>With the value **true**, only the custom menu preview (set using [bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8)), also known as the long-press preview, is displayed if both the long-press preview and drag preview are configured.<br>Default value: **false**| 152| enableEdgeAutoScroll<sup>18+</sup> | boolean | No| Whether to trigger automatic scrolling for dragging to the edge of a scrollable component. <br>**true**: Trigger automatic scrolling.<br>**false**: Do not trigger automatic scrolling.<br>Default value: **true**| 153| enableHapticFeedback<sup>18+</sup> | boolean | No| Whether to enable haptic feedback during dragging. <br>**true**: Enable haptic feedback during dragging.<br>**false**: Disable haptic feedback during dragging. This parameter takes effect only in preview scenarios with a mask (using [bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu12)).<br>Default value: **false**| 154 155## Example 156### Example 1: Allowing Drag and Drop 157 158This example demonstrates how to configure whether a component can be dragged and dropped into by setting **allowDrop** and **draggable**. 159 160```ts 161// xxx.ets 162import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData'; 163 164@Entry 165@Component 166struct ImageExample { 167 @State uri: string = "" 168 @State AblockArr: string[] = [] 169 @State BblockArr: string[] = [] 170 @State AVisible: Visibility = Visibility.Visible 171 @State dragSuccess :Boolean = false 172 173 build() { 174 Column() { 175 Text('Image drag and drop') 176 .fontSize('30dp') 177 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceAround }) { 178 Image($r('app.media.icon')) 179 .width(100) 180 .height(100) 181 .border({ width: 1 }) 182 .visibility(this.AVisible) 183 .draggable(true) 184 .onDragEnd((event: DragEvent) => { 185 let ret = event.getResult(); 186 if(ret == 0) { 187 console.log("enter ret == 0") 188 this.AVisible = Visibility.Hidden; 189 } else { 190 console.log("enter ret != 0") 191 this.AVisible = Visibility.Visible; 192 } 193 }) 194 } 195 .margin({ bottom: 20 }) 196 Row() { 197 Column(){ 198 Text('Invalid drop target') 199 .fontSize('15dp') 200 .height('10%') 201 List(){ 202 ForEach(this.AblockArr, (item:string, index) => { 203 ListItem() { 204 Image(item) 205 .width(100) 206 .height(100) 207 .border({width: 1}) 208 } 209 .margin({ left: 30 , top : 30}) 210 }, (item:string) => item) 211 } 212 .height('90%') 213 .width('100%') 214 .allowDrop([uniformTypeDescriptor.UniformDataType.TEXT]) 215 .onDrop((event?: DragEvent, extraParams?: string) => { 216 this.uri = JSON.parse(extraParams as string).extraInfo; 217 this.AblockArr.splice(JSON.parse(extraParams as string).insertIndex, 0, this.uri); 218 console.log("ondrop not udmf data"); 219 }) 220 .border({width: 1}) 221 } 222 .height("50%") 223 .width("45%") 224 .border({ width: 1 }) 225 .margin({ left: 12 }) 226 Column(){ 227 Text('Valid drop target') 228 .fontSize('15dp') 229 .height('10%') 230 List(){ 231 ForEach(this.BblockArr, (item:string, index) => { 232 ListItem() { 233 Image(item) 234 .width(100) 235 .height(100) 236 .border({width: 1}) 237 } 238 .margin({ left: 30 , top : 30}) 239 }, (item:string) => item) 240 } 241 .border({width: 1}) 242 .height('90%') 243 .width('100%') 244 .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE]) 245 .onDrop((event?: DragEvent, extraParams?: string) => { 246 console.log("enter onDrop") 247 let dragData:UnifiedData = (event as DragEvent).getData() as UnifiedData; 248 if(dragData != undefined) { 249 let arr:Array<unifiedDataChannel.UnifiedRecord> = dragData.getRecords(); 250 if(arr.length > 0) { 251 let image = arr[0] as unifiedDataChannel.Image; 252 this.uri = image.imageUri; 253 this.BblockArr.splice(JSON.parse(extraParams as string).insertIndex, 0, this.uri); 254 } else { 255 console.log(`dragData arr is null`) 256 } 257 } else { 258 console.log(`dragData is undefined`) 259 } 260 console.log("ondrop udmf data"); 261 this.dragSuccess = true 262 }) 263 } 264 .height("50%") 265 .width("45%") 266 .border({ width: 1 }) 267 .margin({ left: 12 }) 268 } 269 }.width('100%') 270 } 271} 272``` 273 274 275 276### Example 2: Setting the Drag Preview 277 278This example demonstrates how to configure the preview displayed during the drag process using **dragPreview**. 279 280```ts 281// xxx.ets 282@Entry 283@Component 284struct DragPreviewDemo{ 285 @Builder dragPreviewBuilder() { 286 Column() { 287 Text("dragPreview") 288 .width(150) 289 .height(50) 290 .fontSize(20) 291 .borderRadius(10) 292 .textAlign(TextAlign.Center) 293 .fontColor(Color.Black) 294 .backgroundColor(Color.Pink) 295 } 296 } 297 298 @Builder MenuBuilder() { 299 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 300 Text("menu item 1") 301 .fontSize(15) 302 .width(100) 303 .height(40) 304 .textAlign(TextAlign.Center) 305 .fontColor(Color.Black) 306 .backgroundColor(Color.Pink) 307 Divider() 308 .height(5) 309 Text("menu item 2") 310 .fontSize(15) 311 .width(100) 312 .height(40) 313 .textAlign(TextAlign.Center) 314 .fontColor(Color.Black) 315 .backgroundColor(Color.Pink) 316 } 317 .width(100) 318 } 319 320 build() { 321 Row() { 322 Column() { 323 Image('/resource/image.jpeg') 324 .width("30%") 325 .draggable(true) 326 .bindContextMenu(this.MenuBuilder, ResponseType.LongPress) 327 .onDragStart(() => { 328 console.log("Image onDragStart") 329 }) 330 .dragPreview(this.dragPreviewBuilder) 331 } 332 .width("100%") 333 } 334 .height("100%") 335 } 336} 337``` 338 339 340 341### Example 3: Setting the Drag Preview Style 342 343This example demonstrates how to set default shadows, unified rounded corners, and gray effects by configuring **dragPreviewOptions** with **ENABLE_DEFAULT_SHADOW**, **ENABLE_DEFAULT_RADIUS**, and **ENABLE_DRAG_ITEM_GRAY_EFFECT**. 344 345```ts 346// xxx.ets 347@Entry 348@Component 349struct dragPreviewOptionsDemo{ 350 build() { 351 Row() { 352 Column() { 353 Image('/resource/image.jpeg') 354 .margin({ top: 10 }) 355 .width("30%") 356 .draggable(true) 357 .dragPreviewOptions({ mode: DragPreviewMode.AUTO }) 358 Image('/resource/image.jpeg') 359 .margin({ top: 10 }) 360 .width("30%") 361 .border({ radius: { topLeft: 1, topRight: 2, bottomLeft: 4, bottomRight: 8 } }) 362 .draggable(true) 363 .onDragStart(() => { 364 console.log("Image onDragStart") 365 }) 366 .dragPreviewOptions({ mode: [ DragPreviewMode.ENABLE_DEFAULT_SHADOW, DragPreviewMode.ENABLE_DEFAULT_RADIUS, DragPreviewMode.ENABLE_DRAG_ITEM_GRAY_EFFECT ] }) 367 } 368 .width("100%") 369 .height("100%") 370 } 371 } 372} 373``` 374 375 376 377 378### Example 4: Enabling Multiselect for Dragging 379 380This example demonstrates how to enable multiselect for dragging in a **Grid** component by configuring **isMultiSelectionEnabled**. 381 382```ts 383@Entry 384@Component 385struct Example { 386 @State numbers: number[] = [0, 1, 2, 3, 4 , 5, 6, 7, 8] 387 build() { 388 Column({ space: 5}) { 389 Grid() { 390 ForEach(this.numbers, (item: number) => { 391 GridItem() { 392 Column() 393 .backgroundColor(Color.Blue) 394 .width('100%') 395 .height('100%') 396 } 397 .width(90) 398 .height(90) 399 .selectable(true) 400 .selected(true) 401 .dragPreviewOptions({}, {isMultiSelectionEnabled:true}) 402 .onDragStart(()=>{ 403 404 }) 405 }, (item: string) => item) 406 } 407 .columnsTemplate('1fr 1fr 1fr') 408 .rowsTemplate('1fr 1fr 1fr') 409 .height(300) 410 } 411 .width('100%') 412 } 413} 414``` 415 416 417 418### Example 5: Enabling the Default Pressed State Animation 419 420This example demonstrates how to enable the default pressed state animation for a **Grid** component by configuring **defaultAnimationBeforeLifting**. 421 422```ts 423@Entry 424@Component 425struct Example { 426 @State numbers: number[] = [0, 1, 2, 3, 4 , 5, 6, 7, 8] 427 build() { 428 Column({ space: 5}) { 429 Grid() { 430 ForEach(this.numbers, (item: number) => { 431 GridItem() { 432 Column() 433 .backgroundColor(Color.Blue) 434 .width('100%') 435 .height('100%') 436 } 437 .width(90) 438 .height(90) 439 .selectable(true) 440 .selected(true) 441 .dragPreviewOptions({}, {isMultiSelectionEnabled:true, defaultAnimationBeforeLifting:true}) 442 .onDragStart(()=>{ 443 444 }) 445 }, (item: string) => item) 446 } 447 .columnsTemplate('1fr 1fr 1fr') 448 .rowsTemplate('1fr 1fr 1fr') 449 .height(300) 450 } 451 .width('100%') 452 } 453} 454``` 455 456 457 458### Example 6: Customizing the Preview Style 459 460This example demonstrates how to customize the preview style for an **Image** component by configuring **ImageModifier**. 461 462```ts 463// xxx.ets 464import { ImageModifier } from '@kit.ArkUI' 465 466@Entry 467@Component 468struct dragPreviewOptionsDemo{ 469 @State myModifier: ImageAttribute = new ImageModifier().opacity(0.5) 470 @State vis: boolean = true 471 @State changeValue: string = '' 472 @State submitValue: string = '' 473 @State positionInfo: CaretOffset = { index: 0, x: 0, y: 0 } 474 controller: SearchController = new SearchController() 475 @State OpacityIndex: number = 0 476 @State OpacityList:(number | undefined | null)[]=[ 477 0.3,0.5,0.7,1,-50,0,10,undefined,null 478 ] 479 build() { 480 Row() { 481 Column() { 482 Text(this.OpacityList[this.OpacityIndex] + "") 483 Button("Opacity") 484 .onClick(()=> { 485 this.OpacityIndex++ 486 if(this.OpacityIndex > this.OpacityList.length - 1){ 487 this.OpacityIndex = 0 488 } 489 }) 490 Image($r('app.media.image')) 491 .margin({ top: 10 }) 492 .width("100%") 493 .draggable(true) 494 .dragPreviewOptions({modifier: this.myModifier.opacity(this.OpacityList[this.OpacityIndex]) as ImageModifier}) 495 } 496 .width("50%") 497 .height("50%") 498 } 499 } 500} 501``` 502 503 504 505### Example 7: Configuring Image Dragging Settings 506 507This example shows the settings for different types of images (online image resources, local image resources, and PixelMap) during drag operations. 508The **ohos.permission.INTERNET** permission is required for using online images. For details about how to apply for a permission, see [Declaring Permissions](../../../security/AccessToken/declare-permissions.md). 509 510```ts 511// xxx.ets 512import { uniformTypeDescriptor, unifiedDataChannel } from '@kit.ArkData'; 513import { image } from '@kit.ImageKit'; 514import { request } from '@kit.BasicServicesKit'; 515import { common } from '@kit.AbilityKit'; 516import { fileIo } from '@kit.CoreFileKit'; 517import { buffer } from '@kit.ArkTS'; 518import { BusinessError } from '@kit.BasicServicesKit'; 519 520@Entry 521@Component 522struct ImageDrag { 523 @State targetImage1: string | PixelMap | null = null; 524 @State targetImage2: string | PixelMap | null = null; 525 @State targetImage3: string | PixelMap | null = null; 526 context = getContext(this) as common.UIAbilityContext; 527 filesDir = this.context.filesDir; 528 529 public async createPixelMap(pixelMap: unifiedDataChannel.SystemDefinedPixelMap): Promise<image.PixelMap | null> { 530 let mWidth: number = (pixelMap.details?.width ?? -1) as number; 531 let mHeight: number = (pixelMap.details?.width ?? -1) as number; 532 let mPixelFormat: image.PixelMapFormat = 533 (pixelMap.details?.['pixel-format'] ?? image.PixelMapFormat.UNKNOWN) as image.PixelMapFormat; 534 let mItemPixelMapData: Uint8Array = pixelMap.rawData; 535 const opts: image.InitializationOptions = { 536 editable: false, pixelFormat: mPixelFormat, size: { 537 height: mHeight, 538 width: mWidth 539 } 540 }; 541 const buffer: ArrayBuffer = mItemPixelMapData.buffer.slice(mItemPixelMapData.byteOffset, 542 mItemPixelMapData.byteLength + mItemPixelMapData.byteOffset); 543 try { 544 let pixelMap: image.PixelMap = await image.createPixelMap(buffer, opts); 545 return pixelMap; 546 } catch (err) { 547 console.error('dragtest--> getPixelMap', err); 548 return null; 549 } 550 } 551 552 build() { 553 Column() { 554 Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center }) { 555 // Drag an online image. 556 Column() { 557 Text('Online Image').fontSize(14) 558 Image('https://www.example.com/xxx.png') // Enter a specific online image URL. 559 .objectFit(ImageFit.Contain).draggable(true) 560 .onDragStart(() => {}) 561 .width(100).height(100) 562 } 563 .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted }) 564 .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center) 565 566 // Drag a local image. 567 Column() { 568 Text('Local Image').fontSize(14) 569 Image($r('app.media.example')) 570 .objectFit(ImageFit.Contain).draggable(true) 571 .onDragStart(() => {}) 572 .width(100).height(100) 573 } 574 .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted }) 575 .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center) 576 577 // Drag a PixelMap object. 578 Column() { 579 Text('PixelMap').fontSize(14) 580 Image(this.context.resourceManager.getDrawableDescriptor($r('app.media.example').id).getPixelMap()) 581 .objectFit(ImageFit.Contain).draggable(true) 582 .onDragStart(() => {}) 583 .width(100).height(100) 584 } 585 .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted }) 586 .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center) 587 } 588 589 // Set the drop data type to Image. 590 Text('Data type is Image').fontSize(14).margin({ top: 10 }) 591 Column() { 592 Image(this.targetImage1) 593 .objectFit(ImageFit.Contain) 594 .width('70%').height('70%') 595 .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE]) 596 .onDrop((event: DragEvent, extraParams: string) => { 597 // Obtain the image through extraParams. 598 let arr: Record<string, object> = JSON.parse(extraParams) as Record<string, object>; 599 let uri = arr['extraInfo']; 600 if (typeof uri == 'string') { 601 this.targetImage1 = uri; 602 603 try { 604 request.downloadFile(this.context, { 605 url: uri, 606 filePath: this.filesDir + '/example.png' 607 }).then((downloadTask: request.DownloadTask) => { 608 let file = fileIo.openSync(this.filesDir + '/example.png', fileIo.OpenMode.READ_WRITE); 609 let arrayBuffer = new ArrayBuffer(1024); 610 let readLen = fileIo.readSync(file.fd, arrayBuffer); 611 let buf = buffer.from(arrayBuffer, 0, readLen); 612 console.info(`The content of file: ${buf.toString()}`); 613 fileIo.closeSync(file); 614 }) 615 } catch (error) {} 616 } 617 }) 618 } 619 .width('70%').height('25%') 620 .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted }) 621 .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center) 622 623 Column() { 624 Image(this.targetImage2) 625 .objectFit(ImageFit.Contain) 626 .width('70%').height('70%') 627 .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE]) 628 .onDrop((event: DragEvent, extraParams: string) => { 629 // Obtain the image through uniformTypeDescriptor. 630 let data: UnifiedData = event.getData(); 631 let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords(); 632 if (records[0].getType() ===uniformTypeDescriptor.UniformDataType.IMAGE) { 633 let image: unifiedDataChannel.Image = records[0] as unifiedDataChannel.Image; 634 this.targetImage2 = image.imageUri; 635 } 636 }) 637 } 638 .width('70%').height('25%') 639 .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted }) 640 .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center) 641 642 // Set the drop data type to PixelMap. 643 Text('Data type is PixelMap').fontSize(14).margin({ top: 10 }) 644 Column() { 645 Image(this.targetImage3) 646 .objectFit(ImageFit.Contain) 647 .width('70%').height('70%') 648 .allowDrop([uniformTypeDescriptor.UniformDataType.OPENHARMONY_PIXEL_MAP]) 649 .onDrop(async (event: DragEvent, extraParams: string) => { 650 // Obtain the image through uniformTypeDescriptor. 651 let data: UnifiedData = event.getData(); 652 let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords(); 653 if (records[0].getType() ===uniformTypeDescriptor.UniformDataType.OPENHARMONY_PIXEL_MAP) { 654 let record: unifiedDataChannel.SystemDefinedPixelMap = records[0] as unifiedDataChannel.SystemDefinedPixelMap; 655 this.targetImage3 = await this.createPixelMap(record); 656 657 // Save data to local storage. 658 const imagePackerApi = image.createImagePacker(); 659 let packOpts : image.PackingOption = { format: "image/jpeg", quality:98 }; 660 const path : string = this.context.cacheDir + "/pixel_map.jpg"; 661 let file = fileIo.openSync(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE); 662 imagePackerApi.packToFile(this.targetImage3, file.fd, packOpts).then(() => { 663 // Pack the image into the file. 664 }).catch((error : BusinessError) => { 665 console.error('Failed to pack the image. And the error is: ' + error); 666 }) 667 } 668 }) 669 } 670 .width('70%').height('25%') 671 .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted }) 672 .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center) 673 674 }.width('100%').height('100%') 675 } 676} 677``` 678 679 680 681### Example 8: Enabling Haptic Feedback for Dragging 682This example demonstrates how to enable haptic feedback for image dragging by setting **enableHapticFeedback**. 683```ts 684// xxx.ets 685@Entry 686@Component 687struct DragPreviewDemo{ 688 @Builder MenuBuilder() { 689 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 690 Text("menu item 1") 691 .fontSize(15) 692 .width(100) 693 .height(40) 694 .textAlign(TextAlign.Center) 695 .fontColor(Color.Black) 696 .backgroundColor(Color.Pink) 697 Divider() 698 .height(5) 699 Text("menu item 2") 700 .fontSize(15) 701 .width(100) 702 .height(40) 703 .textAlign(TextAlign.Center) 704 .fontColor(Color.Black) 705 .backgroundColor(Color.Pink) 706 } 707 .width(100) 708 } 709 710 build() { 711 Row() { 712 Column() { 713 Image($r('app.media.app_icon')) 714 .width("30%") 715 .draggable(true) 716 .dragPreviewOptions({}, {isMultiSelectionEnabled:true, defaultAnimationBeforeLifting:true, enableHapticFeedback: true}) 717 .bindContextMenu(this.MenuBuilder, ResponseType.LongPress) 718 .onDragStart(() => { 719 console.log("Image onDragStart") 720 }) 721 } 722 .width("100%") 723 } 724 .height("100%") 725 } 726} 727``` 728 729### Example 9: Customizing the Drag Preview 730This example demonstrates how to customize the drag preview using **onlyForLifting** for lifting effects and **isLiftingDisabled** to disable the lifting effect. 731```ts 732// xxx.ets 733@Entry 734@Component 735struct LiftingExampleDemo { 736 @Builder 737 dragPreviewBuilder() { 738 Column() { 739 Text("dragPreview builder") 740 .width(150) 741 .height(50) 742 .fontSize(20) 743 .borderRadius(10) 744 .textAlign(TextAlign.Center) 745 .fontColor(Color.Black) 746 .backgroundColor(Color.Green) 747 } 748 } 749 @Builder 750 MenuBuilder() { 751 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 752 Text("menu 1") 753 .fontSize(25) 754 .width(200) 755 .height(60) 756 .textAlign(TextAlign.Center) 757 .fontColor(Color.Black) 758 .backgroundColor(Color.Green) 759 Divider() 760 .height(5) 761 Text("menu 2") 762 .fontSize(25) 763 .width(200) 764 .height(60) 765 .textAlign(TextAlign.Center) 766 .fontColor(Color.Black) 767 .backgroundColor(Color.Green) 768 } 769 .width(100) 770 } 771 build() { 772 Column() { 773 Column() { 774 Text("Lifting effect disabled") 775 .fontSize(30) 776 .height(30) 777 .backgroundColor('#FFFFFF') 778 .margin({ top: 30 }) 779 Image($r('app.media.startIcon')) 780 .width("40%") 781 .draggable(true) 782 .margin({ top: 15 }) 783 .bindContextMenu(this.MenuBuilder, ResponseType.LongPress) 784 .onDragStart(() => { 785 }) 786 .dragPreviewOptions({}, { 787 isLiftingDisabled: true 788 }) 789 .dragPreview(this.dragPreviewBuilder, { 790 onlyForLifting: true, 791 delayCreating: true 792 }) 793 }.width("%") 794 Column() { 795 Text("Lifting effect only") 796 .fontSize(30) 797 .height(30) 798 .backgroundColor('#FFFFFF') 799 .margin({ top: 80 }) 800 Image($r('app.media.startIcon')) 801 .width("40%") 802 .draggable(true) 803 .margin({ top: 15 }) 804 .onDragStart(() => { 805 }) 806 .dragPreviewOptions({}, { 807 isLiftingDisabled: false 808 }) 809 .dragPreview(this.dragPreviewBuilder, { 810 onlyForLifting: true, 811 delayCreating: true 812 }) 813 }.width("100%") 814 }.height("100%") 815 } 816} 817``` 818 819Custom preview for the lifting effect only 820 821 822 823Custom preview with the lifting effect disabled 824 825 826