• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;UniformDataType&gt; | 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#backgroundblurstyle9) 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| Name| Value| Description|
117| -------- | ------- | -------- |
118| 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.<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
119| DISABLE_SCALE  | 2 | Disables the system's scaling behavior for the drag preview.<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
120| ENABLE_DEFAULT_SHADOW<sup>12+</sup> | 3 | Enables the default shadow effect for non-text components.<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
121| 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.<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
122| 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.<br>**Atomic service API**: This API can be used in atomic services since API version 18.|
123| 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.<br>**Atomic service API**: This API can be used in atomic services since API version 18.|
124| 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.<br>**Atomic service API**: This API can be used in atomic services since API version 18.|
125
126## DraggingSizeChangeEffect<sup>18+</sup>
127
128Enumerates 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.
129
130**Atomic service API**: This API can be used in atomic services since API version 18.
131
132**System capability**: SystemCapability.ArkUI.ArkUI.Full
133
134| Name| Value| Description|
135| -------- | ------- | -------- |
136| DEFAULT | 0 | Directly switches from the long-press preview to the final drag preview at its full size when dragging begins.|
137| 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.|
138| 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.|
139
140
141## DragInteractionOptions<sup>12+</sup>
142
143| Name| Type| Mandatory| Description|
144| -------- | -------- | -------- | -------- |
145| 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>**Atomic service API**: This API can be used in atomic services since API version 12.|
146| 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>**Atomic service API**: This API can be used in atomic services since API version 12.|
147| 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**<br>**Atomic service API**: This API can be used in atomic services since API version 15.|
148| 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**<br>**Atomic service API**: This API can be used in atomic services since API version 18.|
149| 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>**NOTE**<br>The settings take effect only when the application has the ohos.permission.VIBRATE permission and the user has enabled haptic feedback.<br>Default value: **false**<br>**Atomic service API**: This API can be used in atomic services since API version 18.|
150
151## Example
152### Example 1: Allowing Drag and Drop
153
154This example demonstrates how to configure whether a component can be dragged and dropped into by setting **allowDrop** and **draggable**.
155
156```ts
157// xxx.ets
158import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';
159
160@Entry
161@Component
162struct ImageExample {
163  @State uri: string = ""
164  @State AblockArr: string[] = []
165  @State BblockArr: string[] = []
166  @State AVisible: Visibility = Visibility.Visible
167  @State dragSuccess :Boolean = false
168
169  build() {
170    Column() {
171      Text('Image drag and drop')
172        .fontSize('30dp')
173      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceAround }) {
174        Image($r('app.media.icon'))
175          .width(100)
176          .height(100)
177          .border({ width: 1 })
178          .visibility(this.AVisible)
179          .draggable(true)
180          .onDragEnd((event: DragEvent) => {
181            let ret = event.getResult();
182            if(ret == 0) {
183              console.log("enter ret == 0")
184              this.AVisible = Visibility.Hidden;
185            } else {
186              console.log("enter ret != 0")
187              this.AVisible = Visibility.Visible;
188            }
189          })
190      }
191      .margin({ bottom: 20 })
192      Row() {
193        Column(){
194          Text('Invalid drop target')
195            .fontSize('15dp')
196            .height('10%')
197          List(){
198            ForEach(this.AblockArr, (item:string, index) => {
199              ListItem() {
200                Image(item)
201                  .width(100)
202                  .height(100)
203                  .border({width: 1})
204              }
205              .margin({ left: 30 , top : 30})
206            }, (item:string) => item)
207          }
208          .height('90%')
209          .width('100%')
210          .allowDrop([uniformTypeDescriptor.UniformDataType.TEXT])
211          .onDrop((event?: DragEvent, extraParams?: string) => {
212            this.uri = JSON.parse(extraParams as string).extraInfo;
213            this.AblockArr.splice(JSON.parse(extraParams as string).insertIndex, 0, this.uri);
214            console.log("ondrop not udmf data");
215          })
216          .border({width: 1})
217        }
218        .height("50%")
219        .width("45%")
220        .border({ width: 1 })
221        .margin({ left: 12 })
222        Column(){
223          Text('Valid drop target')
224            .fontSize('15dp')
225            .height('10%')
226          List(){
227            ForEach(this.BblockArr, (item:string, index) => {
228              ListItem() {
229                Image(item)
230                  .width(100)
231                  .height(100)
232                  .border({width: 1})
233              }
234              .margin({ left: 30 , top : 30})
235            }, (item:string) => item)
236          }
237          .border({width: 1})
238          .height('90%')
239          .width('100%')
240          .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])
241          .onDrop((event?: DragEvent, extraParams?: string) => {
242            console.log("enter onDrop")
243            let dragData:UnifiedData = (event as DragEvent).getData() as UnifiedData;
244            if(dragData != undefined) {
245              let arr:Array<unifiedDataChannel.UnifiedRecord> = dragData.getRecords();
246              if(arr.length > 0) {
247                let image = arr[0] as unifiedDataChannel.Image;
248                this.uri = image.imageUri;
249                this.BblockArr.splice(JSON.parse(extraParams as string).insertIndex, 0, this.uri);
250              } else {
251                console.log(`dragData arr is null`)
252              }
253            } else {
254              console.log(`dragData  is undefined`)
255            }
256            console.log("ondrop udmf data");
257            this.dragSuccess = true
258          })
259        }
260        .height("50%")
261        .width("45%")
262        .border({ width: 1 })
263        .margin({ left: 12 })
264      }
265    }.width('100%')
266  }
267}
268```
269
270![dragImage.gif](figures/dragImage.gif)
271
272### Example 2: Setting the Drag Preview
273
274This example demonstrates how to configure the preview displayed during the drag process using **dragPreview**.
275
276```ts
277// xxx.ets
278@Entry
279@Component
280struct DragPreviewDemo{
281  @Builder dragPreviewBuilder() {
282    Column() {
283      Text("dragPreview")
284        .width(150)
285        .height(50)
286        .fontSize(20)
287        .borderRadius(10)
288        .textAlign(TextAlign.Center)
289        .fontColor(Color.Black)
290        .backgroundColor(Color.Pink)
291    }
292  }
293
294  @Builder MenuBuilder() {
295    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
296      Text("menu item 1")
297        .fontSize(15)
298        .width(100)
299        .height(40)
300        .textAlign(TextAlign.Center)
301        .fontColor(Color.Black)
302        .backgroundColor(Color.Pink)
303      Divider()
304        .height(5)
305      Text("menu item 2")
306        .fontSize(15)
307        .width(100)
308        .height(40)
309        .textAlign(TextAlign.Center)
310        .fontColor(Color.Black)
311        .backgroundColor(Color.Pink)
312    }
313    .width(100)
314  }
315
316  build() {
317    Row() {
318      Column() {
319        Image('/resource/image.jpeg')
320          .width("30%")
321          .draggable(true)
322          .bindContextMenu(this.MenuBuilder, ResponseType.LongPress)
323          .onDragStart(() => {
324            console.log("Image onDragStart")
325          })
326          .dragPreview(this.dragPreviewBuilder)
327      }
328      .width("100%")
329    }
330    .height("100%")
331  }
332}
333```
334
335![dragPreview.gif](figures/dragPreview.gif)
336
337### Example 3: Setting the Drag Preview Style
338
339This 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**.
340
341```ts
342// xxx.ets
343@Entry
344@Component
345struct dragPreviewOptionsDemo{
346  build() {
347    Row() {
348      Column() {
349        Image('/resource/image.jpeg')
350          .margin({ top: 10 })
351          .width("30%")
352          .draggable(true)
353          .dragPreviewOptions({ mode: DragPreviewMode.AUTO })
354        Image('/resource/image.jpeg')
355          .margin({ top: 10 })
356          .width("30%")
357          .border({ radius: { topLeft: 1, topRight: 2, bottomLeft: 4, bottomRight: 8 } })
358          .draggable(true)
359          .onDragStart(() => {
360            console.log("Image onDragStart")
361          })
362          .dragPreviewOptions({ mode: [ DragPreviewMode.ENABLE_DEFAULT_SHADOW, DragPreviewMode.ENABLE_DEFAULT_RADIUS, DragPreviewMode.ENABLE_DRAG_ITEM_GRAY_EFFECT ] })
363      }
364      .width("100%")
365      .height("100%")
366    }
367  }
368}
369```
370
371![dragPreviewMode.gif](figures/dragPreviewMode.gif)
372
373
374### Example 4: Enabling Multiselect for Dragging
375
376This example demonstrates how to enable multiselect for dragging in a **Grid** component by configuring **isMultiSelectionEnabled**.
377
378```ts
379@Entry
380@Component
381struct Example {
382  @State numbers: number[] = [0, 1, 2, 3, 4 , 5, 6, 7, 8]
383  build() {
384    Column({ space: 5}) {
385      Grid() {
386        ForEach(this.numbers, (item: number) => {
387          GridItem() {
388            Column()
389              .backgroundColor(Color.Blue)
390              .width('100%')
391              .height('100%')
392          }
393          .width(90)
394          .height(90)
395          .selectable(true)
396          .selected(true)
397          .dragPreviewOptions({}, {isMultiSelectionEnabled:true})
398          .onDragStart(()=>{
399
400          })
401    }, (item: string) => item)
402      }
403      .columnsTemplate('1fr 1fr 1fr')
404      .rowsTemplate('1fr 1fr 1fr')
405      .height(300)
406    }
407    .width('100%')
408  }
409}
410```
411
412![isMultiSelectionEnabled.gif](figures/isMultiSelectionEnabled.gif)
413
414### Example 5: Enabling the Default Pressed State Animation
415
416This example demonstrates how to enable the default pressed state animation for a **Grid** component by configuring **defaultAnimationBeforeLifting**.
417
418```ts
419@Entry
420@Component
421struct Example {
422  @State numbers: number[] = [0, 1, 2, 3, 4 , 5, 6, 7, 8]
423  build() {
424    Column({ space: 5}) {
425      Grid() {
426        ForEach(this.numbers, (item: number) => {
427          GridItem() {
428            Column()
429              .backgroundColor(Color.Blue)
430              .width('100%')
431              .height('100%')
432          }
433          .width(90)
434          .height(90)
435          .selectable(true)
436          .selected(true)
437          .dragPreviewOptions({}, {isMultiSelectionEnabled:true, defaultAnimationBeforeLifting:true})
438          .onDragStart(()=>{
439
440          })
441    }, (item: string) => item)
442      }
443      .columnsTemplate('1fr 1fr 1fr')
444      .rowsTemplate('1fr 1fr 1fr')
445      .height(300)
446    }
447    .width('100%')
448  }
449}
450```
451
452![defaultAnimationBeforeLifting.gif](figures/defaultAnimationBeforeLifting.gif)
453
454### Example 6: Customizing the Preview Style
455
456This example demonstrates how to customize the preview style for an **Image** component by configuring **ImageModifier**.
457
458```ts
459// xxx.ets
460import { ImageModifier } from '@kit.ArkUI'
461
462@Entry
463@Component
464struct dragPreviewOptionsDemo{
465  @State myModifier: ImageAttribute = new ImageModifier().opacity(0.5)
466  @State vis: boolean = true
467  @State changeValue: string = ''
468  @State submitValue: string = ''
469  @State positionInfo: CaretOffset = { index: 0, x: 0, y: 0 }
470  controller: SearchController = new SearchController()
471  @State OpacityIndex: number = 0
472  @State OpacityList:(number | undefined | null)[]=[
473    0.3,0.5,0.7,1,-50,0,10,undefined,null
474  ]
475  build() {
476    Row() {
477      Column() {
478        Text(this.OpacityList[this.OpacityIndex] + "")
479        Button("Opacity")
480          .onClick(()=> {
481            this.OpacityIndex++
482            if(this.OpacityIndex > this.OpacityList.length - 1){
483              this.OpacityIndex = 0
484            }
485          })
486        Image($r('app.media.image'))
487          .margin({ top: 10 })
488          .width("100%")
489          .draggable(true)
490          .dragPreviewOptions({modifier: this.myModifier.opacity(this.OpacityList[this.OpacityIndex]) as ImageModifier})
491      }
492      .width("50%")
493      .height("50%")
494    }
495  }
496}
497```
498
499![imageModifier.gif](figures/imageModifier.gif)
500
501### Example 7: Configuring Image Dragging Settings
502
503This example shows the settings for different types of images (online image resources, local image resources, and PixelMap) during drag operations.
504The **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).
505
506```ts
507// xxx.ets
508import { uniformTypeDescriptor, unifiedDataChannel } from '@kit.ArkData';
509import { image } from '@kit.ImageKit';
510import { request } from '@kit.BasicServicesKit';
511import { common } from '@kit.AbilityKit';
512import { fileIo } from '@kit.CoreFileKit';
513import { buffer } from '@kit.ArkTS';
514import { BusinessError } from '@kit.BasicServicesKit';
515
516@Entry
517@Component
518struct ImageDrag {
519  @State targetImage1: string | PixelMap | null = null;
520  @State targetImage2: string | PixelMap | null = null;
521  @State targetImage3: string | PixelMap | null = null;
522  context: Context|undefined = this.getUIContext().getHostContext();
523  filesDir = this.context?.filesDir;
524
525  public async createPixelMap(pixelMap: unifiedDataChannel.SystemDefinedPixelMap): Promise<image.PixelMap | null> {
526    let mWidth: number = (pixelMap.details?.width ?? -1) as number;
527    let mHeight: number = (pixelMap.details?.width ?? -1) as number;
528    let mPixelFormat: image.PixelMapFormat =
529      (pixelMap.details?.['pixel-format'] ?? image.PixelMapFormat.UNKNOWN) as image.PixelMapFormat;
530    let mItemPixelMapData: Uint8Array = pixelMap.rawData;
531    const opts: image.InitializationOptions = {
532      editable: false, pixelFormat: mPixelFormat, size: {
533        height: mHeight,
534        width: mWidth
535      }
536    };
537    const buffer: ArrayBuffer = mItemPixelMapData.buffer.slice(mItemPixelMapData.byteOffset,
538      mItemPixelMapData.byteLength + mItemPixelMapData.byteOffset);
539    try {
540      let pixelMap: image.PixelMap = await image.createPixelMap(buffer, opts);
541      return pixelMap;
542    } catch (err) {
543      console.error('dragtest--> getPixelMap', err);
544      return null;
545    }
546  }
547
548  build() {
549    Column() {
550      Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center }) {
551        // Drag an online image.
552        Column() {
553          Text('Online Image').fontSize(14)
554          Image('https://www.example.com/xxx.png') // Enter a specific online image URL.
555            .objectFit(ImageFit.Contain).draggable(true)
556            .onDragStart(() => {})
557            .width(100).height(100)
558        }
559        .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
560        .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
561
562        // Drag a local image.
563        Column() {
564          Text('Local Image').fontSize(14)
565          Image($r('app.media.example'))
566            .objectFit(ImageFit.Contain).draggable(true)
567            .onDragStart(() => {})
568            .width(100).height(100)
569        }
570        .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
571        .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
572
573        // Drag a PixelMap object.
574        Column() {
575          Text('PixelMap').fontSize(14)
576          Image(this.context?.resourceManager.getDrawableDescriptor($r('app.media.example').id).getPixelMap())
577            .objectFit(ImageFit.Contain).draggable(true)
578            .onDragStart(() => {})
579            .width(100).height(100)
580        }
581        .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
582        .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
583      }
584
585      // Set the drop data type to Image.
586      Text('Data type is Image').fontSize(14).margin({ top: 10 })
587      Column() {
588        Image(this.targetImage1)
589          .objectFit(ImageFit.Contain)
590          .width('70%').height('70%')
591          .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])
592          .onDrop((event: DragEvent, extraParams: string) => {
593            // Obtain the image through extraParams.
594            let arr: Record<string, object> = JSON.parse(extraParams) as Record<string, object>;
595            let uri = arr['extraInfo'];
596            if (typeof uri == 'string') {
597              this.targetImage1 = uri;
598
599              try {
600                request.downloadFile(this.context, {
601                  url: uri,
602                  filePath: this.filesDir + '/example.png'
603                }).then((downloadTask: request.DownloadTask) => {
604                  let file = fileIo.openSync(this.filesDir + '/example.png', fileIo.OpenMode.READ_WRITE);
605                  let arrayBuffer = new ArrayBuffer(1024);
606                  let readLen = fileIo.readSync(file.fd, arrayBuffer);
607                  let buf = buffer.from(arrayBuffer, 0, readLen);
608                  console.info(`The content of file: ${buf.toString()}`);
609                  fileIo.closeSync(file);
610                })
611              } catch (error) {}
612            }
613          })
614      }
615      .width('70%').height('25%')
616      .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
617      .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
618
619      Column() {
620        Image(this.targetImage2)
621          .objectFit(ImageFit.Contain)
622          .width('70%').height('70%')
623          .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])
624          .onDrop((event: DragEvent, extraParams: string) => {
625            // Obtain the image through uniformTypeDescriptor.
626            let data: UnifiedData = event.getData();
627            let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
628            if (records[0].getType() ===uniformTypeDescriptor.UniformDataType.IMAGE) {
629              let image: unifiedDataChannel.Image = records[0] as unifiedDataChannel.Image;
630              this.targetImage2 = image.imageUri;
631            }
632          })
633      }
634      .width('70%').height('25%')
635      .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
636      .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
637
638      // Set the drop data type to PixelMap.
639      Text('Data type is PixelMap').fontSize(14).margin({ top: 10 })
640      Column() {
641        Image(this.targetImage3)
642          .objectFit(ImageFit.Contain)
643          .width('70%').height('70%')
644          .allowDrop([uniformTypeDescriptor.UniformDataType.OPENHARMONY_PIXEL_MAP])
645          .onDrop(async (event: DragEvent, extraParams: string) => {
646            // Obtain the image through uniformTypeDescriptor.
647            let data: UnifiedData = event.getData();
648            let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
649            if (records[0].getType() ===uniformTypeDescriptor.UniformDataType.OPENHARMONY_PIXEL_MAP) {
650              let record: unifiedDataChannel.SystemDefinedPixelMap = records[0] as unifiedDataChannel.SystemDefinedPixelMap;
651              this.targetImage3 = await this.createPixelMap(record);
652
653              // Save data to local storage.
654              const imagePackerApi = image.createImagePacker();
655              let packOpts : image.PackingOption = { format: "image/jpeg", quality:98 };
656              const path : string = this.context?.cacheDir + "/pixel_map.jpg";
657              let file = fileIo.openSync(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
658              imagePackerApi.packToFile(this.targetImage3, file.fd, packOpts).then(() => {
659                // Pack the image into the file.
660              }).catch((error : BusinessError) => {
661                console.error('Failed to pack the image. And the error is: ' + error);
662              })
663            }
664          })
665      }
666      .width('70%').height('25%')
667      .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
668      .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
669
670    }.width('100%').height('100%')
671  }
672}
673```
674
675![imageDrag.gif](figures/imageDrag.gif)
676
677### Example 8: Enabling Haptic Feedback for Dragging
678This example demonstrates how to enable haptic feedback for image dragging by setting **enableHapticFeedback**.
679```ts
680// xxx.ets
681@Entry
682@Component
683struct DragPreviewDemo{
684  @Builder MenuBuilder() {
685    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
686      Text("menu item 1")
687        .fontSize(15)
688        .width(100)
689        .height(40)
690        .textAlign(TextAlign.Center)
691        .fontColor(Color.Black)
692        .backgroundColor(Color.Pink)
693      Divider()
694        .height(5)
695      Text("menu item 2")
696        .fontSize(15)
697        .width(100)
698        .height(40)
699        .textAlign(TextAlign.Center)
700        .fontColor(Color.Black)
701        .backgroundColor(Color.Pink)
702    }
703    .width(100)
704  }
705
706  build() {
707    Row() {
708      Column() {
709        Image($r('app.media.app_icon'))
710          .width("30%")
711          .draggable(true)
712          .dragPreviewOptions({}, {isMultiSelectionEnabled:true, defaultAnimationBeforeLifting:true, enableHapticFeedback: true})
713          .bindContextMenu(this.MenuBuilder, ResponseType.LongPress)
714          .onDragStart(() => {
715            console.log("Image onDragStart")
716          })
717      }
718      .width("100%")
719    }
720    .height("100%")
721  }
722}
723```
724
725### Example 9: Customizing the Drag Preview
726This example demonstrates how to customize the drag preview using **onlyForLifting** for lifting effects and **isLiftingDisabled** to disable the lifting effect.
727```ts
728// xxx.ets
729@Entry
730@Component
731struct LiftingExampleDemo {
732  @Builder
733  dragPreviewBuilder() {
734    Column() {
735      Text("dragPreview builder")
736        .width(150)
737        .height(50)
738        .fontSize(20)
739        .borderRadius(10)
740        .textAlign(TextAlign.Center)
741        .fontColor(Color.Black)
742        .backgroundColor(Color.Green)
743    }
744  }
745  @Builder
746  MenuBuilder() {
747    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
748      Text("menu 1")
749        .fontSize(25)
750        .width(200)
751        .height(60)
752        .textAlign(TextAlign.Center)
753        .fontColor(Color.Black)
754        .backgroundColor(Color.Green)
755      Divider()
756        .height(5)
757      Text("menu 2")
758        .fontSize(25)
759        .width(200)
760        .height(60)
761        .textAlign(TextAlign.Center)
762        .fontColor(Color.Black)
763        .backgroundColor(Color.Green)
764    }
765    .width(100)
766  }
767  build() {
768    Column() {
769      Column() {
770        Text("Lifting effect disabled")
771          .fontSize(30)
772          .height(30)
773          .backgroundColor('#FFFFFF')
774          .margin({ top: 30 })
775        Image($r('app.media.startIcon'))
776          .width("40%")
777          .draggable(true)
778          .margin({ top: 15 })
779          .bindContextMenu(this.MenuBuilder, ResponseType.LongPress)
780          .onDragStart(() => {
781          })
782          .dragPreviewOptions({}, {
783            isLiftingDisabled: true
784          })
785          .dragPreview(this.dragPreviewBuilder, {
786            onlyForLifting: true,
787            delayCreating: true
788          })
789      }.width("%")
790      Column() {
791        Text("Lifting effect only")
792          .fontSize(30)
793          .height(30)
794          .backgroundColor('#FFFFFF')
795          .margin({ top: 80 })
796        Image($r('app.media.startIcon'))
797          .width("40%")
798          .draggable(true)
799          .margin({ top: 15 })
800          .onDragStart(() => {
801          })
802          .dragPreviewOptions({}, {
803            isLiftingDisabled: false
804          })
805          .dragPreview(this.dragPreviewBuilder, {
806            onlyForLifting: true,
807            delayCreating: true
808          })
809      }.width("100%")
810    }.height("100%")
811  }
812}
813```
814
815Custom preview for the lifting effect only
816
817![onlyForLifting.gif](figures/onlyForLifting.gif)
818
819Custom preview with the lifting effect disabled
820
821![isLiftingDisabled.gif](figures/isLiftingDisabled.gif)
822