• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Drag Event
2
3## Overview
4
5The drag event, bolstered by the drag and drop framework, represents a mode of data transfer using a mouse device or gesture: Users can drag data from one component to another. The component from which data is dragged is the drag source; and the component to which data is dropped is the drop target. This drag and drop operation enables users to easily move, copy, or delete data. The following are some key concepts involved:
6
7* Drag operation: an operation that begins when a user selects a draggable component, continues when the user drags the component on the screen, and ends when the user releases the component on a droppable component.
8* Drag preview (background): a visual representation of the data being dragged. You can set it by using [CustomerBuilder](../reference/apis-arkui/arkui-ts/ts-types.md#custombuilder8) or [DragItemInfo](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#dragiteminfo) of [onDragStart](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondragstart), or by using the universal attribute [dragPreview](../reference/apis-arkui/arkui-ts/ts-universal-attributes-drag-drop.md#dragpreview11).
9* Drag data: data being dragged; encapsulated using the UDMF API [UnifiedData](../reference/apis-arkdata/js-apis-data-unifiedDataChannel.md#unifieddata).
10* Drag source: component that initiates the drag operation and provides data.
11* Drop target: component that can receive and process drag data.
12* Drag point: position where the mouse device or finger has in contact with the screen. It is used to determine whether data enters a drop target.
13
14## Drag Process
15
16### ​Gesture-based Drag Operation
17
18​If a drag operation is initiated by a gesture, the framework checks whether the current component is draggable. For draggable components ([Search](../reference/apis-arkui/arkui-ts/ts-basic-components-search.md), [TextInput](../reference/apis-arkui/arkui-ts/ts-basic-components-textinput.md), [TextArea](../reference/apis-arkui/arkui-ts/ts-basic-components-textarea.md), [RichEditor](../reference/apis-arkui/arkui-ts/ts-basic-components-richeditor.md), [Text](../reference/apis-arkui/arkui-ts/ts-basic-components-text.md), [Image](../reference/apis-arkui/arkui-ts/ts-basic-components-image.md), <!--Del-->[FormComponent](../reference/apis-arkui/arkui-ts/ts-basic-components-formcomponent-sys.md), <!--DelEnd-->[Hyperlink](../reference/apis-arkui/arkui-ts/ts-container-hyperlink.md)), the framework checks whether their [draggable](../reference/apis-arkui/arkui-ts/ts-universal-attributes-drag-drop.md#draggable) attribute is set to **true** (this attribute is true by default if layered parameters are used); for other components, the framework checks whether the **onDragStart** callback is set. If the attribute or callback is set as required, the framework starts dragging once a user has long pressed the component for 500 ms or longer, and displays a drag preview once the user has long pressed the component for 800 ms.
19
20Below you can see the drag process initiated by a gesture (finger or stylus).
21
22![en-us_image_0000001562820825](figures/en-us_image_0000001562820825.png)
23
24### ​Mouse-based Drag Operation
25
26When a mouse device is used as the pointer, the framework starts dragging once the draggable component has been moved with the left mouse button by more than 1 vp.
27
28A drag and drop can occur in a single application, or start in one application and end in another. The following callback events are provided for you to detect the dragging status and intervene in the default dragging behavior of the system.
29
30| Callback Event| Description|
31| ---------------- | ------------------------|
32| onDragStart | Triggered when a draggable component is dragged.<br>You can use this callback to detect the initiation of dragging behavior. You can also set the drag data and drag preview in this callback. To avoid extra performance overhead, it is recommended that the drag preview be returned in the mode of **pixelmap**, instead of **customBuilder**.|
33| onDragEnter | Triggered when a dragged item enters the boundaries of the component. This callback is called only when the component listens for the [onDrop](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondrop) event.|
34| onDragMove| Triggered when the dragged item moves in the boundaries of the component. This callback is called only when the component listens for the **onDrop** event.<br>During the movement, the **setResult** API in [DragEvent](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#dragevent) can be used to affect the system appearance in some scenarios.<br>1. Set **DragResult.DROP\_ENABLED**.<br>2. Set **DragResult.DROP\_DISABLED**.|
35| onDragLeave | Triggered when the dragged item leaves the boundaries of the component. This callback is called only when the component listens for the **onDrop** event.<br>By default, the **onDragLeave** callback is not called in the following cases:<br>1. An item in a parent component is dragged to one of its child components.<br>2. The layout of the drop target component overlaps that of the drag source component.<br>Since API version 12, the [setDragEventStrictReportingEnabled](../reference/apis-arkui/js-apis-arkui-UIContext.md#setdrageventstrictreportingenabled12) API in [UIContext](../reference/apis-arkui/js-apis-arkui-UIContext.md) can be used to trigger the **onDragLeave** event in a strict fashion.|
36| onDrop | Triggered when the dragged item is dropped on the component. The dragging result must be set in this callback through the **setResult** API in **DragEvent**. Otherwise, the **getResult** API in the **onDragEnd** method of the drag source component only returns the default result **DragResult.DRAG\_FAILED**.<br>This callback is where you can intervene in the default drop processing behavior. The system preferentially executes the **onDrop** callback and processes the drag data based on the **setResult** method in the callback function.<br>1. If **DragResult.DRAG\_SUCCESSFUL** is set, you need to process the data on your own; the system does not process the data.<br>2. If **DragResult.DRAG\_FAILED** is set, the system does not process the data.<br>3. If **DragResult.DRAG\_CANCELED** is set, the system does not process the data.<br>4. Setting **DragResult.DROP\_ENABLED** or **DragResult.DROP\_DISABLED** will be ignored, producing the same effect as **DragResult.DRAG\_FAILED**.|
37| onDragEnd | Triggered when dragging of the component ends.|
38| onPreDrag | Triggered when the component enters a state prior to a drop and drop operation.<br>You can use this callback to listen for the value of [PreDragStatus](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#predragstatus12) to prepare corresponding data.<br>1. **ACTION\_DETECTING\_STATUS**: A drag gesture is being detected. (Triggered when the component is long pressed for 50 ms.)<br>2. **READY\_TO\_TRIGGER\_DRAG\_ACTION**: The component is ready to be dragged. (Triggered when the component is long pressed for 500 ms.)<br>3. **PREVIEW\_LIFT\_STARTED**: A lift animation is started. (Triggered when the component is long pressed for 800 ms.)<br>4. **PREVIEW\_LIFT\_FINISHED**: A lift animation is finished. (Triggered at the completion of the lift animation.)<br>5. **PREVIEW\_LANDING\_STARTED**: A drop animation is started. (Triggered when the drop animation starts.)<br>6. **PREVIEW\_LANDING\_FINISHED**: A drop animation is finished. (Triggered when the drop animation ends.)<br>7. **ACTION\_CANCELED\_BEFORE\_DRAG**: A drop animation is terminated. (Triggered when the finger is lifted off the screen after the component enters the **READY\_TO\_TRIGGER\_DRAG\_ACTION** state.)|
39
40For more usage, see [Drag Event](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md)
41
42## Drag Preview
43
44The drag preview is an image displayed during the drag and drop operation. It is a visual representation of the drag data, not the component itself. You can set it to any supported image that you want to display to users. The **customBuilder** or **pixelmap** object returned by the **onDragStart** callback can be used to set the drag preview – snapshot of the component by default – displayed during dragging and moving. The **customBuilder** or **pixelmap** object set by the **dragpreview** attribute can be used to set the drag preview – snapshot of the component by default – during a lift animation and dragging.
45
46You can set the opacity, rounded corners, shadow, and blur for the drag preview. For details, see [Drag and Drop Control](../reference/apis-arkui/arkui-ts/ts-universal-attributes-drag-drop.md).
47
48**Constraints**:
49
50* For a container component, if the internal content exceeds the range of the container component due to **position**, **offset**, and other settings, the component snapshot does not capture the content beyond the range. To show the out-of-range content, you can expand the container scope or customize the container.
51* Regardless of how the component snapshot is captured, using a custom builder or the default mode, the snapshot does not support the transform APIs, such as [scale](../reference/apis-arkui/arkui-ts/ts-universal-attributes-transformation.md#scale) and [rotate](../reference/apis-arkui/arkui-ts/ts-universal-attributes-transformation.md#rotate).
52
53## How to Develop
54
55### General Drag and Drop Adaptation
56
57The following uses the [Image](../reference/apis-arkui/arkui-ts/ts-basic-components-image.md) component as an example to describe the basic procedure for drag and drop development and the precautions to be taken during development.
58
591. Make the component draggable.
60
61* Set the **draggable** attribute to **true** and set the **onDragStart** callback function. In the callback function, you can use UDMF to set the drag data and return the custom drag preview.
62
63    ```ts
64    import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';
65
66    Image($r('app.media.app_icon'))
67        .width(100)
68        .height(100)
69        .draggable(true)
70        .onDragStart((event) => {
71            let data: unifiedDataChannel.Image = new unifiedDataChannel.Image();
72            data.imageUri = 'common/pic/img.png';
73            let unifiedData = new unifiedDataChannel.UnifiedData(data);
74            event.setData(unifiedData);
75
76            let dragItemInfo: DragItemInfo = {
77            pixelMap: this.pixmap,
78            extraInfo: "this is extraInfo",
79            };
80            // The custom drag preview is returned in onDragStart.
81            return dragItemInfo;
82        })
83    ```
84
85* The gesture-based drag operation is initiated by a long press gesture bound at the underlying layer. If a long press gesture is also bound to the dragged component, gesture conflict will occur, resulting in dragging to fail. To avoid such an issue, you can use parallel gestures.
86
87    ```ts
88    .parallelGesture(LongPressGesture().onAction(() => {
89       promptAction.showToast({ duration: 100, message: 'Long press gesture trigger' });
90    }))
91    ```
92
932. Customize the drag preview.
94
95  * Prepare the pixel map for the custom drag preview within the callback triggered by [onPreDrag](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#onpredrag12) after a long press of 50 ms.
96
97    ```ts
98    .onPreDrag((status: PreDragStatus) => {
99        if (preDragStatus == PreDragStatus.ACTION_DETECTING_STATUS) {
100            this.getComponentSnapshot();
101        }
102    })
103    ```
104
105   * Generate the specific pixel map by calling [componentSnapshot.createFromBuilder](../reference/apis-arkui/js-apis-arkui-componentSnapshot.md#componentsnapshotcreatefrombuilder).
106
107      ```ts
108      @Builder
109      pixelMapBuilder() {
110          Column() {
111            Image($r('app.media.startIcon'))
112              .width(120)
113              .height(120)
114              .backgroundColor(Color.Yellow)
115          }
116        }
117        private getComponentSnapshot(): void {
118        componentSnapshot.createFromBuilder(()=>{this.pixelMapBuilder()},
119        (error: Error, pixmap: image.PixelMap) => {
120            if(error){
121              console.log("error: " + JSON.stringify(error))
122              return;
123            }
124            this.pixmap = pixmap;
125        })
126      }
127      ```
128
1293. To strictly execute the [onDragLeave](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#ondragleave) event, use the [setDragEventStrictReportingEnabled](../reference/apis-arkui/js-apis-arkui-UIContext.md#setdrageventstrictreportingenabled12) API.
130
131    ```ts
132    import { UIAbility } from '@kit.AbilityKit';
133    import { window, UIContext } from '@kit.ArkUI';
134
135    export default class EntryAbility extends UIAbility {
136      onWindowStageCreate(windowStage: window.WindowStage): void {
137        windowStage.loadContent('pages/Index', (err, data) => {
138          if (err.code) {
139            return;
140          }
141          windowStage.getMainWindow((err, data) => {
142            if (err.code) {
143              return;
144            }
145            let windowClass: window.Window = data;
146            let uiContext: UIContext = windowClass.getUIContext();
147            uiContext.getDragController().setDragEventStrictReportingEnabled(true);
148          });
149        });
150      }
151    }
152    ```
153
1544. Set the badge displayed during dragging.
155
156* You can set [allowDrop](../reference/apis-arkui/arkui-ts/ts-universal-attributes-drag-drop.md#allowdrop) to define the allowed data types for dropping to affect the badge display. The COPY badge is displayed when the drag data matches the allowed data types, the FORBIDDEN badge when it does not, and the MOVE badge if **allowDrop** is not set. The following example allows only data of HYPERLINK and PLAIN_TEXT types defined in UnifiedData.
157
158    ```ts
159    .allowDrop([uniformTypeDescriptor.UniformDataType.HYPERLINK, uniformTypeDescriptor.UniformDataType.PLAIN_TEXT])
160    ```
161
162* If the **onDrop** callback is implemented, you can control the badge display by setting [DragResult](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#dragresult10) to **DROP\_ENABLED** in **onDragMove** and setting [DragBehavior](../reference/apis-arkui/arkui-ts/ts-universal-events-drag-drop.md#dragbehavior10) to **COPY** or **MOVE**. The following code forces the badge to display **MOVE** during a drag operation:
163
164    ```ts
165    .onDragMove((event) => {
166        event.setResult(DragResult.DROP_ENABLED);
167        event.dragBehavior = DragBehavior.MOVE;
168    })
169    ```
170
1715. Receive drag data.
172
173* Set the **onDrop** callback to handle the drag data and determine the drag result.
174
175    ```ts
176    .onDrop((dragEvent?: DragEvent) => {
177        // Obtain the drag data.
178        this.getDataFromUdmf((dragEvent as DragEvent), (event: DragEvent) => {
179        let records: Array<unifiedDataChannel.UnifiedRecord> = event.getData().getRecords();
180        let rect: Rectangle = event.getPreviewRect();
181        this.imageWidth = Number(rect.width);
182        this.imageHeight = Number(rect.height);
183        this.targetImage = (records[0] as unifiedDataChannel.Image).imageUri;
184        this.imgState = Visibility.None;
185        // Explicitly set the result to successful, and then pass this value to onDragEnd of the drag source.
186        event.setResult(DragResult.DRAG_SUCCESSFUL);
187    })
188    ```
189
190* Data transfer is managed by UDMF, which may experience latency with large data volumes. Therefore, you are advised to implement a retry mechanism with a 1500 ms delay after the initial data acquisition fails.
191
192    ```ts
193    getDataFromUdmfRetry(event: DragEvent, callback: (data: DragEvent) => void) {
194       try {
195         let data: UnifiedData = event.getData();
196         if (!data) {
197           return false;
198         }
199         let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
200         if (!records || records.length <= 0) {
201           return false;
202         }
203         callback(event);
204         return true;
205       } catch (e) {
206         console.log("getData failed, code: " + (e as BusinessError).code + ", message: " + (e as BusinessError).message);
207         return false;
208       }
209    }
210
211    getDataFromUdmf(event: DragEvent, callback: (data: DragEvent) => void) {
212      if (this.getDataFromUdmfRetry(event, callback)) {
213        return;
214      }
215      setTimeout(() => {
216        this.getDataFromUdmfRetry(event, callback);
217      }, 1500);
218    }
219    ```
220
2216. The drag initiator can detect the result of the drag operation by setting the **onDragEnd** callback.
222
223    ```ts
224    import { promptAction } from '@kit.ArkUI';
225
226    .onDragEnd((event) => {
227        // The result value obtained from onDragEnd is set in onDrop of the drop target.
228      if (event.getResult() === DragResult.DRAG_SUCCESSFUL) {
229        promptAction.showToast({ duration: 100, message: 'Drag Success' });
230      } else if (event.getResult() === DragResult.DRAG_FAILED) {
231        promptAction.showToast({ duration: 100, message: 'Drag failed' });
232      }
233    })
234    ```
235
236### Multi-Select Drag and Drop Adaptation
237
238Since API version 12, the [Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md) and [List](../reference/apis-arkui/arkui-ts/ts-container-list.md) components, specifically the **GridItem** and **ListItem** components within them, support multi-select drag and drop, currently only accessible through the **onDragStart** API. The following uses **Grid** as an example to describe the basic procedure for multi-select drag and drop development and key considrations during development.
239
2401. Enable multi-select drag and drop.
241
242* Create **GridItem** child components and bind the **onDragStart** callback to them. In addition, set the **GridItem** components to be selectable.
243
244    ```ts
245    Grid() {
246      ForEach(this.numbers, (idx: number) => {
247        GridItem() {
248          Column()
249            .backgroundColor(this.colors[idx % 9])
250            .width(50)
251            .height(50)
252            .opacity(1.0)
253            .id('grid'+idx)
254        }
255        .onDragStart(()=>{})
256        .selectable(true)
257      }, (idx: string) => idx)
258    }
259    ```
260
261* Multi-select drag and drop is disabled by default. To enable it, set **isMultiSelectionEnabled** to **true** in the **DragInteractionOptions** parameter of the [dragPreviewOptions](../reference/apis-arkui/arkui-ts/ts-universal-attributes-drag-drop.md#dragpreviewoptions11) API. **DragInteractionOptions** also has the **defaultAnimationBeforeLifting** parameter, which, when set to **true**, applies a default scaling down animation as the lift animation for the component.
262
263    ```ts
264    .dragPreviewOptions({isMultiSelectionEnabled:true,defaultAnimationBeforeLifting:true})
265    ```
266
267* To maintain the selected state, set the **selected** attribute of the **GridItem** child component to **true**. For example, use [onClick](../reference/apis-arkui/arkui-ts/ts-universal-events-click.md#onclick) to set a specific component to the selected state.
268
269    ```ts
270    .selected(this.isSelectedGrid[idx])
271    .onClick(()=>{
272        this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
273    })
274    ```
275
2762. Optimize the multi-select drag and drop performance.
277
278* In multi-select drag and drop scenarios, there is a clustering animation effect when multiple items are selected. This effect captures a snapshot of the selected components currently displayed on the screen, which can lead to high performance costs if there are too many selected components. To save on performance, multi-select drag and drop supports obtaining a snapshot from **dragPreview** to use as the basis for the clustering animation.
279
280    ```ts
281    .dragPreview({
282        pixelMap:this.pixmap
283    })
284    ```
285
286* You can obtain snapshots of a component by calling the **get** method of **componentSnapshot** when the component is selected. The following shows how to use the component ID to obtain the snapshot.
287
288    ```ts
289    @State previewData: DragItemInfo[] = []
290    @State isSelectedGrid: boolean[] = []
291    .onClick(()=>{
292        this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
293        if (this.isSelectedGrid[idx]) {
294            let gridItemName = 'grid' + idx
295            componentSnapshot.get(gridItemName, (error: Error, pixmap: image.PixelMap)=>{
296                this.pixmap = pixmap
297                this.previewData[idx] = {
298                    pixelMap:this.pixmap
299                }
300            })
301        }
302    })
303    ```
304
3053. Set the multi-select display effects.
306
307    Use **stateStyles** to set display effects for selected and unselected states for easy distinction.
308
309    ```ts
310    @Styles
311    normalStyles(): void{
312      .opacity(1.0)
313    }
314
315    @Styles
316    selectStyles(): void{
317      .opacity(0.4)
318    }
319
320    .stateStyles({
321      normal : this.normalStyles,
322      selected: this.selectStyles
323    })
324    ```
325
3264. Adapt the number badge.
327
328    Configure the number badge for multi-select drag and drop using the **numberBadge** parameter in **dragPreviewOptions**, adjusting it based on the number of selected items.
329
330    ```ts
331    @State numberBadge: number = 0;
332
333    .onClick(()=>{
334        this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
335        if (this.isSelectedGrid[idx]) {
336          this.numberBadge++;
337        } else {
338          this.numberBadge--;
339      }
340    })
341    // Set the numberBadge parameter in dragPreviewOptions for the number badge in multi-select scenarios.
342    .dragPreviewOptions({numberBadge: this.numberBadge})
343    ```
344
345## Example
346
347### General Drag and Drop Adaptation Case
348
349```ts
350import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';
351import { promptAction, componentSnapshot } from '@kit.ArkUI';
352import { BusinessError } from '@kit.BasicServicesKit';
353import { image } from '@kit.ImageKit';
354
355@Entry
356@Component
357struct Index {
358  @State targetImage: string = '';
359  @State imageWidth: number = 100;
360  @State imageHeight: number = 100;
361  @State imgState: Visibility = Visibility.Visible;
362  @State pixmap: image.PixelMap|undefined = undefined
363
364  @Builder
365  pixelMapBuilder() {
366    Column() {
367      Image($r('app.media.startIcon'))
368        .width(120)
369        .height(120)
370        .backgroundColor(Color.Yellow)
371    }
372  }
373
374  getDataFromUdmfRetry(event: DragEvent, callback: (data: DragEvent) => void) {
375    try {
376      let data: UnifiedData = event.getData();
377      if (!data) {
378        return false;
379      }
380      let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
381      if (!records || records.length <= 0) {
382        return false;
383      }
384      callback(event);
385      return true;
386    } catch (e) {
387      console.log("getData failed, code: " + (e as BusinessError).code + ", message: " + (e as BusinessError).message);
388      return false;
389    }
390  }
391  // Obtain UDMF data with a retry mechanism of 1500 ms if the initial attempt fails.
392  getDataFromUdmf(event: DragEvent, callback: (data: DragEvent) => void) {
393    if (this.getDataFromUdmfRetry(event, callback)) {
394      return;
395    }
396    setTimeout(() => {
397      this.getDataFromUdmfRetry(event, callback);
398    }, 1500);
399  }
400  // Use the createFromBuilder API of componentSnapshot to capture a snapshot of a custom builder.
401  private getComponentSnapshot(): void {
402    componentSnapshot.createFromBuilder(()=>{this.pixelMapBuilder()},
403      (error: Error, pixmap: image.PixelMap) => {
404        if(error){
405          console.log("error: " + JSON.stringify(error))
406          return;
407        }
408        this.pixmap = pixmap;
409      })
410  }
411  // Prepare a custom screenshot pixel map after a 50 ms long press is detected.
412  private PreDragChange(preDragStatus: PreDragStatus): void {
413    if (preDragStatus == PreDragStatus.ACTION_DETECTING_STATUS) {
414      this.getComponentSnapshot();
415    }
416  }
417
418  build() {
419    Row() {
420      Column() {
421        Text('start Drag')
422          .fontSize(18)
423          .width('100%')
424          .height(40)
425          .margin(10)
426          .backgroundColor('#008888')
427        Row() {
428          Image($r('app.media.app_icon'))
429            .width(100)
430            .height(100)
431            .draggable(true)
432            .margin({ left: 15 })
433            .visibility(this.imgState)
434            // Bind a parallel gesture to trigger a custom long press gesture.
435            .parallelGesture(LongPressGesture().onAction(() => {
436              promptAction.showToast({ duration: 100, message: 'Long press gesture trigger' });
437            }))
438            .onDragStart((event) => {
439              let data: unifiedDataChannel.Image = new unifiedDataChannel.Image();
440              data.imageUri = 'common/pic/img.png';
441              let unifiedData = new unifiedDataChannel.UnifiedData(data);
442              event.setData(unifiedData);
443
444              let dragItemInfo: DragItemInfo = {
445                pixelMap: this.pixmap,
446                extraInfo: "this is extraInfo",
447              };
448              return dragItemInfo;
449            })
450              // Prepare a custom drag preview in advance.
451            .onPreDrag((status: PreDragStatus) => {
452              this.PreDragChange(status);
453            })
454            .onDragEnd((event) => {
455              // The result value obtained from onDragEnd is set in onDrop of the drop target.
456              if (event.getResult() === DragResult.DRAG_SUCCESSFUL) {
457                promptAction.showToast({ duration: 100, message: 'Drag Success' });
458              } else if (event.getResult() === DragResult.DRAG_FAILED) {
459                promptAction.showToast({ duration: 100, message: 'Drag failed' });
460              }
461            })
462        }
463
464        Text('Drag Target Area')
465          .fontSize(20)
466          .width('100%')
467          .height(40)
468          .margin(10)
469          .backgroundColor('#008888')
470        Row() {
471          Image(this.targetImage)
472            .width(this.imageWidth)
473            .height(this.imageHeight)
474            .draggable(true)
475            .margin({ left: 15 })
476            .border({ color: Color.Black, width: 1 })
477            // Set the drag behavior to MOVE, which means no badge is displayed.
478            .onDragMove((event) => {
479              event.setResult(DragResult.DROP_ENABLED)
480              event.dragBehavior = DragBehavior.MOVE
481            })
482            .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])
483            .onDrop((dragEvent?: DragEvent) => {
484              // Obtain the drag data.
485              this.getDataFromUdmf((dragEvent as DragEvent), (event: DragEvent) => {
486                let records: Array<unifiedDataChannel.UnifiedRecord> = event.getData().getRecords();
487                let rect: Rectangle = event.getPreviewRect();
488                this.imageWidth = Number(rect.width);
489                this.imageHeight = Number(rect.height);
490                this.targetImage = (records[0] as unifiedDataChannel.Image).imageUri;
491                this.imgState = Visibility.None;
492                // Explicitly set the result to successful, and then pass this value to onDragEnd of the drag source.
493                event.setResult(DragResult.DRAG_SUCCESSFUL);
494              })
495            })
496        }
497      }
498      .width('100%')
499      .height('100%')
500    }
501    .height('100%')
502  }
503}
504
505```
506
507### Multi-Select Drag and Drop Adaptation Case
508
509```ts
510import { componentSnapshot } from '@kit.ArkUI';
511import { image } from '@kit.ImageKit';
512
513@Entry
514@Component
515struct GridEts {
516  @State pixmap: image.PixelMap|undefined = undefined
517  @State numbers: number[] = []
518  @State isSelectedGrid: boolean[] = []
519  @State previewData: DragItemInfo[] = []
520  @State colors: Color[] = [Color.Red, Color.Blue, Color.Brown, Color.Gray, Color.Green, Color.Grey, Color.Orange,Color.Pink ,Color.Yellow]
521  @State numberBadge: number = 0;
522
523  @Styles
524  normalStyles(): void{
525    .opacity(1.0)
526  }
527
528  @Styles
529  selectStyles(): void{
530    .opacity(0.4)
531  }
532
533  onPageShow(): void {
534    let i: number = 0
535    for(i=0;i<100;i++){
536      this.numbers.push(i)
537      this.isSelectedGrid.push(false)
538      this.previewData.push({})
539    }
540  }
541
542  @Builder
543  RandomBuilder(idx: number) {
544    Column()
545      .backgroundColor(this.colors[idx % 9])
546      .width(50)
547      .height(50)
548      .opacity(1.0)
549  }
550
551  build() {
552    Column({ space: 5 }) {
553      Grid() {
554        ForEach(this.numbers, (idx: number) => {
555          GridItem() {
556            Column()
557              .backgroundColor(this.colors[idx % 9])
558              .width(50)
559              .height(50)
560              .opacity(1.0)
561              .id('grid'+idx)
562          }
563          .dragPreview(this.previewData[idx])
564          .selectable(true)
565          .selected(this.isSelectedGrid[idx])
566          // Set the multi-select display effects.
567          .stateStyles({
568            normal : this.normalStyles,
569            selected: this.selectStyles
570          })
571          .onClick(()=>{
572            this.isSelectedGrid[idx] = !this.isSelectedGrid[idx]
573            if (this.isSelectedGrid[idx]) {
574              this.numberBadge++;
575              let gridItemName = 'grid' + idx
576              // Call the get API in componentSnapshot to obtain the component snapshot pixel map on selection.
577              componentSnapshot.get(gridItemName, (error: Error, pixmap: image.PixelMap)=>{
578                this.pixmap = pixmap
579                this.previewData[idx] = {
580                  pixelMap:this.pixmap
581                }
582              })
583            } else {
584              this.numberBadge--;
585            }
586          })
587          // Enable multiselect and set the number badge.
588          .dragPreviewOptions({numberBadge: this.numberBadge},{isMultiSelectionEnabled:true,defaultAnimationBeforeLifting:true})
589          .onDragStart(()=>{
590          })
591        }, (idx: string) => idx)
592      }
593      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
594      .columnsGap(5)
595      .rowsGap(10)
596      .backgroundColor(0xFAEEE0)
597    }.width('100%').margin({ top: 5 })
598  }
599}
600```
601