• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 拖拽控制
2
3设置组件是否可以响应拖拽事件。
4
5> **说明:**
6>
7> 从API Version 10开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
8
9ArkUI框架对以下组件实现了默认的拖拽能力,支持对数据的拖出或拖入响应。开发者也可以通过实现通用拖拽事件来自定义拖拽响应。
10
11- 默认支持拖出能力的组件(可从组件上拖出数据):[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),开发者可通过设置这些组件的[draggable](ts-universal-attributes-drag-drop.md#draggable)属性来控制对默认拖拽能力的使用。
12
13- 默认支持拖入能力的组件(目标组件可响应拖入数据):[Search](ts-basic-components-search.md)、[TextInput](ts-basic-components-textinput.md)、[TextArea](ts-basic-components-textarea.md)、[RichEditor](ts-basic-components-richeditor.md),开发者可通过设置这些组件的[allowDrop](ts-universal-attributes-drag-drop.md#allowdrop)属性为null来禁用对默认拖入能力的支持。
14
15<!--RP1--><!--RP1End-->其他组件需要开发者将draggable属性设置为true,并在onDragStart等接口中实现数据传输相关内容,才能正确处理拖拽。
16
17> **说明:**
18>
19> Text组件需配合[copyOption](ts-basic-components-text.md#copyoption9)一起使用,设置copyOptions为CopyOptions.InApp或者CopyOptions.LocalDevice20
21## allowDrop
22
23allowDrop(value: Array&lt;UniformDataType&gt; | null)
24
25设置该组件上允许落入的数据类型。
26
27**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
28
29**系统能力:** SystemCapability.ArkUI.ArkUI.Full
30
31**参数:**
32
33| 参数名 | 类型                                                         | 必填 | 说明                                            |
34| ------ | ------------------------------------------------------------ | ---- | ----------------------------------------------- |
35| value  | Array\<[UniformDataType](../../apis-arkdata/js-apis-data-uniformTypeDescriptor.md#uniformdatatype)> \| null<sup>12+</sup> | 是   | 设置该组件上允许落入的数据类型。从API version 12开始,允许设置成null使该组件不接受所有的数据类型。<br/>默认值:空 |
36
37## draggable
38
39draggable(value: boolean)
40
41设置该组件是否允许进行拖拽。
42
43**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
44
45**系统能力:** SystemCapability.ArkUI.ArkUI.Full
46
47**参数:**
48
49| 参数名 | 类型    | 必填 | 说明                                           |
50| ------ | ------- | ---- | ---------------------------------------------- |
51| value  | boolean | 是   | 设置该组件是否允许进行拖拽。true表示允许拖拽,false表示不允许拖拽。<br/>默认值:false |
52
53## dragPreview<sup>11+</sup>
54
55dragPreview(value: CustomBuilder | DragItemInfo | string)
56
57设置组件拖拽过程中的预览图。
58
59**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
60
61**系统能力:** SystemCapability.ArkUI.ArkUI.Full
62
63**参数:**
64
65| 参数名 | 类型                                                         | 必填 | 说明                                                         |
66| ------ | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
67| value  | [CustomBuilder](ts-types.md#custombuilder8)&nbsp;\|&nbsp;[DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo说明) \| string<sup>12+</sup> | 是   | 设置组件拖拽过程中的预览图,仅在onDragStart拖拽方式中有效。<br/>当组件支持拖拽并同时设置[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8)的预览图时,则长按浮起的预览图以[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8)设置的预览图为准。开发者在[onDragStart](ts-universal-events-drag-drop.md#onDragStart)中返回的背板图优先级低于[dragPreview](ts-universal-attributes-drag-drop.md#dragPreview11)设置的预览图,当设置了[dragPreview](ts-universal-attributes-drag-drop.md#dragPreview11)预览图时,拖拽过程中的背板图使用[dragPreview](ts-universal-attributes-drag-drop.md#dragPreview11)预览图。由于[CustomBuilder](ts-types.md#custombuilder8)需要离线渲染之后才能使用,因此存在一定的性能开销和时延,推荐优先使用 [DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo说明)中的[PixelMap](../../apis-image-kit/js-apis-image.md#pixelmap7)方式。<br/> 当传入类型为string的id时,则将id对应组件的截图作为预览图。如果id对应的组件无法查找到,或者id对应的组件Visibility属性设置成none/hidden,则对组件自身进行截图作为拖拽预览图。目前截图不含有亮度、阴影、模糊和旋转等视觉效果。<br/>默认值:空<br/> |
68
69## dragPreview<sup>15+</sup>
70
71dragPreview(preview: CustomBuilder | DragItemInfo | string, config?: PreviewConfiguration):T
72
73自定义组件拖拽过程中的预览图,仅用于设置浮起效果或者禁用浮起效果。
74
75**原子化服务API:** 从API version 15开始,该接口支持在原子化服务中使用。
76
77**系统能力:** SystemCapability.ArkUI.ArkUI.Full
78
79**参数:**
80
81| 参数名 | 类型                                                         | 必填 | 说明                                                         |
82| ------ | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
83| preview  | [CustomBuilder](ts-types.md#custombuilder8)&nbsp;\|&nbsp;[DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo说明) \| string | 是   | 设置组件拖拽过程中的预览图,仅在onDragStart拖拽方式中有效。<br/>当组件支持拖拽并同时设置[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8)的预览图时,则长按浮起的预览图以[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8)设置的预览图为准。开发者在[onDragStart](ts-universal-events-drag-drop.md#ondragstart)中返回的背板图优先级低于[dragPreview](ts-universal-attributes-drag-drop.md#dragpreview11)设置的预览图,当设置了[dragPreview](ts-universal-attributes-drag-drop.md#dragpreview11)预览图时,拖拽过程中的背板图使用[dragPreview](ts-universal-attributes-drag-drop.md#dragpreview11)预览图。由于[CustomBuilder](ts-types.md#custombuilder8)需要离线渲染之后才能使用,因此存在一定的性能开销和时延,推荐优先使用 [DragItemInfo](ts-universal-events-drag-drop.md#dragiteminfo说明)中的[PixelMap](../../apis-image-kit/js-apis-image.md#pixelmap7)方式。<br/> 当传入类型为string的id时,则将id对应组件的截图作为预览图。如果id对应的组件无法查找到,或者id对应的组件Visibility属性设置成none/hidden,则对组件自身进行截图作为拖拽预览图。目前截图不含有亮度、阴影、模糊和旋转等视觉效果。<br/>默认值:空 |
84| config | [PreviewConfiguration](ts-universal-events-drag-drop.md#previewconfiguration15) | 是 | 对自定义拖拽过程中的预览图进行配置。<br/>只对[dragPreview](#dragpreview11)中的预览生效。<br/>默认值:空 |
85
86## dragPreviewOptions<sup>11+</sup>
87
88dragPreviewOptions(value: DragPreviewOptions, options?: DragInteractionOptions)
89
90设置拖拽过程中背板图处理模式及数量角标的显示。不支持onItemDragStart拖拽方式。
91
92**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
93
94**系统能力:** SystemCapability.ArkUI.ArkUI.Full
95
96**参数:**
97
98| 参数名 | 类型                                                            | 必填 | 说明                                                         |
99| ------ | -------------------------------------------------------------- | ---- | ------------------------------------------------------------ |
100| value  | [DragPreviewOptions](#dragpreviewoptions11)<sup>11+</sup>      | 是   | 设置拖拽过程中背板图处理模式及数量角标的显示。<br/>默认值:空 |
101| options<sup>12+</sup>| [DragInteractionOptions](#draginteractionoptions12)<sup>12+</sup>| 否   | 设置拖拽过程中背板图浮起的交互模式。<br/>默认值:空|
102
103## DragPreviewOptions<sup>11+</sup>
104
105**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
106
107| 名称 | 类型 | 必填 | 描述 |
108| -------- | -------- | -------- | -------- |
109| mode | [DragPreviewMode](#dragpreviewmode11枚举说明) &nbsp;\|&nbsp; Array<[DragPreviewMode](#dragpreviewmode11枚举说明)><sup>12+</sup> | 否 | 表示拖拽过程中背板图处理模式。<br/>默认值:DragPreviewMode.AUTO<br/>当组件同时设置DragPreviewMode.AUTO和其它枚举值时,以DragPreviewMode.AUTO为准,其它枚举值设置无效。|
110| numberBadge<sup>12+</sup> | boolean &nbsp;\|&nbsp; number | 否 | 控制数量角标是否显示,或强制设置显示的数量。当设置数量角标时取值范围为[0,2<sup>31</sup>-1],超过取值范围时会按默认状态处理。当设置为浮点数时,只显示整数部分。<br/>**说明:** <br>在多选拖拽场景,需通过该接口设置拖拽对象的数量。<br/>默认值:true |
111| modifier<sup>12+</sup> | [ImageModifier](ts-universal-attributes-attribute-modifier.md)| 否 | 用于配置拖拽背板图的样式Modifier对象,可使用图片组件所支持的属性和样式来配置背板图样式(参考示例6),当前支持透明度,阴影,背景模糊度,圆角。文本拖拽只支持默认效果,不支持通过modifier进行自定义。<br/>1.透明度<br/>通过[opacity](ts-universal-attributes-opacity.md#opacity)设置透明度,不透明度的取值范围为0-1。设置0或不设置时采用默认值0.95,设置1或异常值时不透明。<br/>2.阴影<br/>通过[shadow](ts-universal-attributes-image-effect.md#shadow)设置阴影。<br/>3.背景模糊度<br/>通过[backgroundEffect](ts-universal-attributes-background.md#backgroundeffect11)或[backgroundBlurStyle](ts-universal-attributes-background.md#backgroundblurstyle)设置背景模糊度,如果两者同时设置,以backgroundEffect为准。<br/>4.圆角<br/>通过[border](ts-universal-attributes-border.md#border)或[borderRadius](ts-universal-attributes-border.md#borderRadius)设置圆角,当同时在mode和modifier中设置圆角,mode设置的圆角显示优先级低于modifier设置。<br/>默认值:空,无法修改属性|
112
113## DragPreviewMode<sup>11+</sup>枚举说明
114
115**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
116
117| 名称 | 枚举值 | 描述 |
118| -------- | ------- | -------- |
119| AUTO  | 1 | 系统根据拖拽场景自动改变跟手点位置,根据规则自动对拖拽背板图进行缩放变换等。 |
120| DISABLE_SCALE  | 2 | 禁用系统对拖拽背板图的缩放行为。 |
121| ENABLE_DEFAULT_SHADOW<sup>12+</sup> | 3 | 启用非文本类组件默认阴影效果。 |
122| ENABLE_DEFAULT_RADIUS<sup>12+</sup> | 4 | 启用非文本类组件统一圆角效果,默认值12vp。当应用自身设置的圆角值大于默认值或modifier设置的圆角时,则显示应用自定义圆角效果。 |
123
124## DragInteractionOptions<sup>12+</sup>
125
126**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
127
128| 名称 | 类型 | 必填 | 描述 |
129| -------- | -------- | -------- | -------- |
130| isMultiSelectionEnabled | boolean | 否 | 表示拖拽过程中背板图是否支持多选聚拢效果。true表示支持多选聚拢效果,false表示不支持多选聚拢效果。该参数只在[Grid](ts-container-grid.md)和[List](ts-container-list.md)组件中的[GridItem](ts-container-griditem.md)组件和[ListItem](ts-container-listitem.md)组件生效。<br/>当一个item组件设置为多选拖拽时,该组件的子组件不可拖拽。聚拢组件预览图设置的优先级为[dragPreview](#dragpreview11)中的string,dragPreview中的PixelMap,组件自截图,不支持dragPreview中的Builder形式。<br/>不支持组件绑定[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu12)中参数存在isShown的模式。<br/>默认值:false<br/> |
131| defaultAnimationBeforeLifting | boolean | 否 | 表示是否启用长按浮起阶段组件自身的默认点按效果(缩小)。true表示启用默认点按效果,false表示不启用默认点按效果。<br/>默认值:false <br/> |
132| isLiftingDisabled<sup>15+</sup> | boolean | 否 | 表示长按拖拽时,是否禁用浮起效果。true表示禁用浮起效果,false表示不禁用浮起效果。<br/>如果设置为true,当组件支持拖拽并同时设置[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8)时,仅弹出配置的自定义菜单预览。 <br/>默认值:false |
133
134## 示例
135### 示例1(允许拖拽和落入)
136
137该示例通过配置allowDrop和draggable分别设置组件是否可落入和拖拽。
138
139```ts
140// xxx.ets
141import { unifiedDataChannel, uniformTypeDescriptor } from '@kit.ArkData';
142
143@Entry
144@Component
145struct ImageExample {
146  @State uri: string = ""
147  @State AblockArr: string[] = []
148  @State BblockArr: string[] = []
149  @State AVisible: Visibility = Visibility.Visible
150  @State dragSuccess :Boolean = false
151
152  build() {
153    Column() {
154      Text('Image拖拽')
155        .fontSize('30dp')
156      Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceAround }) {
157        Image($r('app.media.icon'))
158          .width(100)
159          .height(100)
160          .border({ width: 1 })
161          .visibility(this.AVisible)
162          .draggable(true)
163          .onDragEnd((event: DragEvent) => {
164            let ret = event.getResult();
165            if(ret == 0) {
166              console.log("enter ret == 0")
167              this.AVisible = Visibility.Hidden;
168            } else {
169              console.log("enter ret != 0")
170              this.AVisible = Visibility.Visible;
171            }
172          })
173      }
174      .margin({ bottom: 20 })
175      Row() {
176        Column(){
177          Text('不允许释放区域')
178            .fontSize('15dp')
179            .height('10%')
180          List(){
181            ForEach(this.AblockArr, (item:string, index) => {
182              ListItem() {
183                Image(item)
184                  .width(100)
185                  .height(100)
186                  .border({width: 1})
187              }
188              .margin({ left: 30 , top : 30})
189            }, (item:string) => item)
190          }
191          .height('90%')
192          .width('100%')
193          .allowDrop([uniformTypeDescriptor.UniformDataType.TEXT])
194          .onDrop((event?: DragEvent, extraParams?: string) => {
195            this.uri = JSON.parse(extraParams as string).extraInfo;
196            this.AblockArr.splice(JSON.parse(extraParams as string).insertIndex, 0, this.uri);
197            console.log("ondrop not udmf data");
198          })
199          .border({width: 1})
200        }
201        .height("50%")
202        .width("45%")
203        .border({ width: 1 })
204        .margin({ left: 12 })
205        Column(){
206          Text('可释放区域')
207            .fontSize('15dp')
208            .height('10%')
209          List(){
210            ForEach(this.BblockArr, (item:string, index) => {
211              ListItem() {
212                Image(item)
213                  .width(100)
214                  .height(100)
215                  .border({width: 1})
216              }
217              .margin({ left: 30 , top : 30})
218            }, (item:string) => item)
219          }
220          .border({width: 1})
221          .height('90%')
222          .width('100%')
223          .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])
224          .onDrop((event?: DragEvent, extraParams?: string) => {
225            console.log("enter onDrop")
226            let dragData:UnifiedData = (event as DragEvent).getData() as UnifiedData;
227            if(dragData != undefined) {
228              let arr:Array<unifiedDataChannel.UnifiedRecord> = dragData.getRecords();
229              if(arr.length > 0) {
230                let image = arr[0] as unifiedDataChannel.Image;
231                this.uri = image.imageUri;
232                this.BblockArr.splice(JSON.parse(extraParams as string).insertIndex, 0, this.uri);
233              } else {
234                console.log(`dragData arr is null`)
235              }
236            } else {
237              console.log(`dragData  is undefined`)
238            }
239            console.log("ondrop udmf data");
240            this.dragSuccess = true
241          })
242        }
243        .height("50%")
244        .width("45%")
245        .border({ width: 1 })
246        .margin({ left: 12 })
247      }
248    }.width('100%')
249  }
250}
251```
252
253![dragImage.gif](figures/dragImage.gif)
254
255### 示例2(设置预览图)
256
257该示例通过配置dragPreview设置拖拽过程的预览图。
258
259```ts
260// xxx.ets
261@Entry
262@Component
263struct DragPreviewDemo{
264  @Builder dragPreviewBuilder() {
265    Column() {
266      Text("dragPreview")
267        .width(150)
268        .height(50)
269        .fontSize(20)
270        .borderRadius(10)
271        .textAlign(TextAlign.Center)
272        .fontColor(Color.Black)
273        .backgroundColor(Color.Pink)
274    }
275  }
276
277  @Builder MenuBuilder() {
278    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
279      Text("menu item 1")
280        .fontSize(15)
281        .width(100)
282        .height(40)
283        .textAlign(TextAlign.Center)
284        .fontColor(Color.Black)
285        .backgroundColor(Color.Pink)
286      Divider()
287        .height(5)
288      Text("menu item 2")
289        .fontSize(15)
290        .width(100)
291        .height(40)
292        .textAlign(TextAlign.Center)
293        .fontColor(Color.Black)
294        .backgroundColor(Color.Pink)
295    }
296    .width(100)
297  }
298
299  build() {
300    Row() {
301      Column() {
302        Image('/resource/image.jpeg')
303          .width("30%")
304          .draggable(true)
305          .bindContextMenu(this.MenuBuilder, ResponseType.LongPress)
306          .onDragStart(() => {
307            console.log("Image onDragStart")
308          })
309          .dragPreview(this.dragPreviewBuilder)
310      }
311      .width("100%")
312    }
313    .height("100%")
314  }
315}
316```
317
318![dragPreview.gif](figures/dragPreview.gif)
319
320### 示例3(设置背板图样式)
321
322该示例通过配置dragPreviewOptions为ENABLE_DEFAULT_SHADOW和ENABLE_DEFAULT_RADIUS设置默认阴影和统一圆角效果。
323
324```ts
325// xxx.ets
326@Entry
327@Component
328struct dragPreviewOptionsDemo{
329  build() {
330    Row() {
331      Column() {
332        Image('/resource/image.jpeg')
333          .margin({ top: 10 })
334          .width("100%")
335          .draggable(true)
336          .dragPreviewOptions({ mode: DragPreviewMode.AUTO })
337        Image('/resource/image.jpeg')
338          .margin({ top: 10 })
339          .width("80%")
340          .border({ radius: { topLeft: 1, topRight: 2, bottomLeft: 4, bottomRight: 8 } })
341          .draggable(true)
342          .dragPreviewOptions({ mode: [ DragPreviewMode.ENABLE_DEFAULT_SHADOW, DragPreviewMode.ENABLE_DEFAULT_RADIUS ] })
343      }
344      .width("100%")
345      .height("100%")
346    }
347  }
348}
349```
350
351![dragPreviewOptions.gif](figures/dragPreviewOptions.gif)
352
353
354### 示例4(设置多选拖拽)
355
356该示例通过配置isMultiSelectionEnabled实现Grid组件的多选拖拽效果。
357
358```ts
359@Entry
360@Component
361struct Example {
362  @State numbers: number[] = [0, 1, 2, 3, 4 , 5, 6, 7, 8]
363  build() {
364    Column({ space: 5}) {
365      Grid() {
366        ForEach(this.numbers, (item: number) => {
367          GridItem() {
368            Column()
369              .backgroundColor(Color.Blue)
370              .width('100%')
371              .height('100%')
372          }
373          .width(90)
374          .height(90)
375          .selectable(true)
376          .selected(true)
377          .dragPreviewOptions({}, {isMultiSelectionEnabled:true})
378          .onDragStart(()=>{
379
380          })
381    }, (item: string) => item)
382      }
383      .columnsTemplate('1fr 1fr 1fr')
384      .rowsTemplate('1fr 1fr 1fr')
385      .height(300)
386    }
387    .width('100%')
388  }
389}
390```
391
392![isMultiSelectionEnabled.gif](figures/isMultiSelectionEnabled.gif)
393
394### 示例5(设置默认点按效果)
395
396该示例通过配置defaultAnimationBeforeLifting实现Grid组件的默认点按效果。
397
398```ts
399@Entry
400@Component
401struct Example {
402  @State numbers: number[] = [0, 1, 2, 3, 4 , 5, 6, 7, 8]
403  build() {
404    Column({ space: 5}) {
405      Grid() {
406        ForEach(this.numbers, (item: number) => {
407          GridItem() {
408            Column()
409              .backgroundColor(Color.Blue)
410              .width('100%')
411              .height('100%')
412          }
413          .width(90)
414          .height(90)
415          .selectable(true)
416          .selected(true)
417          .dragPreviewOptions({}, {isMultiSelectionEnabled:true, defaultAnimationBeforeLifting:true})
418          .onDragStart(()=>{
419
420          })
421    }, (item: string) => item)
422      }
423      .columnsTemplate('1fr 1fr 1fr')
424      .rowsTemplate('1fr 1fr 1fr')
425      .height(300)
426    }
427    .width('100%')
428  }
429}
430```
431
432![defaultAnimationBeforeLifting.gif](figures/defaultAnimationBeforeLifting.gif)
433
434### 示例6(自定义背板图样式)
435
436该示例通过配置ImageModifier实现Image组件的自定义背板图样式。
437
438```ts
439// xxx.ets
440import { ImageModifier } from '@kit.ArkUI'
441
442@Entry
443@Component
444struct dragPreviewOptionsDemo{
445  @State myModifier: ImageAttribute = new ImageModifier().opacity(0.5)
446  @State vis: boolean = true
447  @State changeValue: string = ''
448  @State submitValue: string = ''
449  @State positionInfo: CaretOffset = { index: 0, x: 0, y: 0 }
450  controller: SearchController = new SearchController()
451  @State OpacityIndex: number = 0
452  @State OpacityList:(number | undefined | null)[]=[
453    0.3,0.5,0.7,1,-50,0,10,undefined,null
454  ]
455  build() {
456    Row() {
457      Column() {
458        Text(this.OpacityList[this.OpacityIndex] + "")
459        Button("Opacity")
460          .onClick(()=> {
461            this.OpacityIndex++
462            if(this.OpacityIndex > this.OpacityList.length - 1){
463              this.OpacityIndex = 0
464            }
465          })
466        Image($r('app.media.image'))
467          .margin({ top: 10 })
468          .width("100%")
469          .draggable(true)
470          .dragPreviewOptions({modifier: this.myModifier.opacity(this.OpacityList[this.OpacityIndex]) as ImageModifier})
471      }
472      .width("50%")
473      .height("50%")
474    }
475  }
476}
477```
478
479![imageModifier.gif](figures/imageModifier.gif)
480
481### 示例7(图片拖拽设置)
482
483该示例展示了不同图片(在线图片资源、本地图片资源和PixelMap)在拖拽时组件的设置。
484使用网络图片时,需要申请权限ohos.permission.INTERNET。具体申请方式请参考[声明权限](../../../security/AccessToken/declare-permissions.md)。
485
486```ts
487// xxx.ets
488import { uniformTypeDescriptor, unifiedDataChannel } from '@kit.ArkData';
489import { image } from '@kit.ImageKit';
490import { request } from '@kit.BasicServicesKit';
491import { common } from '@kit.AbilityKit';
492import { fileIo } from '@kit.CoreFileKit';
493import { buffer } from '@kit.ArkTS';
494import { BusinessError } from '@kit.BasicServicesKit';
495
496@Entry
497@Component
498struct ImageDrag {
499  @State targetImage1: string | PixelMap | null = null;
500  @State targetImage2: string | PixelMap | null = null;
501  @State targetImage3: string | PixelMap | null = null;
502  context = getContext(this) as common.UIAbilityContext;
503  filesDir = this.context.filesDir;
504
505  public async createPixelMap(pixelMap: unifiedDataChannel.SystemDefinedPixelMap): Promise<image.PixelMap | null> {
506    let mWidth: number = (pixelMap.details?.width ?? -1) as number;
507    let mHeight: number = (pixelMap.details?.width ?? -1) as number;
508    let mPixelFormat: image.PixelMapFormat =
509      (pixelMap.details?.['pixel-format'] ?? image.PixelMapFormat.UNKNOWN) as image.PixelMapFormat;
510    let mItemPixelMapData: Uint8Array = pixelMap.rawData;
511    const opts: image.InitializationOptions = {
512      editable: false, pixelFormat: mPixelFormat, size: {
513        height: mHeight,
514        width: mWidth
515      }
516    };
517    const buffer: ArrayBuffer = mItemPixelMapData.buffer.slice(mItemPixelMapData.byteOffset,
518      mItemPixelMapData.byteLength + mItemPixelMapData.byteOffset);
519    try {
520      let pixelMap: image.PixelMap = await image.createPixelMap(buffer, opts);
521      return pixelMap;
522    } catch (err) {
523      console.error('dragtest--> getPixelMap', err);
524      return null;
525    }
526  }
527
528  build() {
529    Column() {
530      Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.Center }) {
531        // 在线图片资源拖出
532        Column() {
533          Text('Online Image').fontSize(14)
534          Image('https://www.example.com/xxx.png') // 请填写一个具体的网络图片地址
535            .objectFit(ImageFit.Contain).draggable(true)
536            .onDragStart(() => {})
537            .width(100).height(100)
538        }
539        .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
540        .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
541
542        // 本地图片资源拖出
543        Column() {
544          Text('Local Image').fontSize(14)
545          Image($r('app.media.example'))
546            .objectFit(ImageFit.Contain).draggable(true)
547            .onDragStart(() => {})
548            .width(100).height(100)
549        }
550        .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
551        .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
552
553        // PixelMap拖出
554        Column() {
555          Text('PixelMap').fontSize(14)
556          Image(this.context.resourceManager.getDrawableDescriptor($r('app.media.example').id).getPixelMap())
557            .objectFit(ImageFit.Contain).draggable(true)
558            .onDragStart(() => {})
559            .width(100).height(100)
560        }
561        .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
562        .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
563      }
564
565      // 落入数据类型为Image
566      Text('Data type is Image').fontSize(14).margin({ top: 10 })
567      Column() {
568        Image(this.targetImage1)
569          .objectFit(ImageFit.Contain)
570          .width('70%').height('70%')
571          .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])
572          .onDrop((event: DragEvent, extraParams: string) => {
573            // 通过extraParams获取图片
574            let arr: Record<string, object> = JSON.parse(extraParams) as Record<string, object>;
575            let uri = arr['extraInfo'];
576            if (typeof uri == 'string') {
577              this.targetImage1 = uri;
578
579              try {
580                request.downloadFile(this.context, {
581                  url: uri,
582                  filePath: this.filesDir + '/example.png'
583                }).then((downloadTask: request.DownloadTask) => {
584                  let file = fileIo.openSync(this.filesDir + '/example.png', fileIo.OpenMode.READ_WRITE);
585                  let arrayBuffer = new ArrayBuffer(1024);
586                  let readLen = fileIo.readSync(file.fd, arrayBuffer);
587                  let buf = buffer.from(arrayBuffer, 0, readLen);
588                  console.info(`The content of file: ${buf.toString()}`);
589                  fileIo.closeSync(file);
590                })
591              } catch (error) {}
592            }
593          })
594      }
595      .width('70%').height('25%')
596      .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
597      .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
598
599      Column() {
600        Image(this.targetImage2)
601          .objectFit(ImageFit.Contain)
602          .width('70%').height('70%')
603          .allowDrop([uniformTypeDescriptor.UniformDataType.IMAGE])
604          .onDrop((event: DragEvent, extraParams: string) => {
605            // 通过uniformTypeDescriptor获取图片
606            let data: UnifiedData = event.getData();
607            let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
608            if (records[0].getType() ===uniformTypeDescriptor.UniformDataType.IMAGE) {
609              let image: unifiedDataChannel.Image = records[0] as unifiedDataChannel.Image;
610              this.targetImage2 = image.imageUri;
611            }
612          })
613      }
614      .width('70%').height('25%')
615      .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
616      .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
617
618      // 落入数据类型为PixelMap
619      Text('Data type is PixelMap').fontSize(14).margin({ top: 10 })
620      Column() {
621        Image(this.targetImage3)
622          .objectFit(ImageFit.Contain)
623          .width('70%').height('70%')
624          .allowDrop([uniformTypeDescriptor.UniformDataType.OPENHARMONY_PIXEL_MAP])
625          .onDrop(async (event: DragEvent, extraParams: string) => {
626            // 通过uniformTypeDescriptor获取图片
627            let data: UnifiedData = event.getData();
628            let records: Array<unifiedDataChannel.UnifiedRecord> = data.getRecords();
629            if (records[0].getType() ===uniformTypeDescriptor.UniformDataType.OPENHARMONY_PIXEL_MAP) {
630              let record: unifiedDataChannel.SystemDefinedPixelMap = records[0] as unifiedDataChannel.SystemDefinedPixelMap;
631              this.targetImage3 = await this.createPixelMap(record);
632
633              // 落盘到本地
634              const imagePackerApi = image.createImagePacker();
635              let packOpts : image.PackingOption = { format: "image/jpeg", quality:98 };
636              const path : string = this.context.cacheDir + "/pixel_map.jpg";
637              let file = fileIo.openSync(path, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);
638              imagePackerApi.packToFile(this.targetImage3, file.fd, packOpts).then(() => {
639                // 直接打包进文件
640              }).catch((error : BusinessError) => {
641                console.error('Failed to pack the image. And the error is: ' + error);
642              })
643            }
644          })
645      }
646      .width('70%').height('25%')
647      .border({ width: 2, color: Color.Gray, radius: 5, style: BorderStyle.Dotted })
648      .alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)
649
650    }.width('100%').height('100%')
651  }
652}
653```
654
655![imageDrag.gif](figures/imageDrag.gif)
656
657### 示例8(自定义预览图)
658该示例通过配置onlyForLifting实现自定义预览图,仅用于浮起效果以及配置isLiftingDisabled实现禁用浮起效果。
659```ts
660// xxx.ets
661@Entry
662@Component
663struct LiftingExampleDemo {
664  @Builder
665  dragPreviewBuilder() {
666    Column() {
667      Text("dragPreview builder")
668        .width(150)
669        .height(50)
670        .fontSize(20)
671        .borderRadius(10)
672        .textAlign(TextAlign.Center)
673        .fontColor(Color.Black)
674        .backgroundColor(Color.Green)
675    }
676  }
677  @Builder
678  MenuBuilder() {
679    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
680      Text("menu 1")
681        .fontSize(25)
682        .width(200)
683        .height(60)
684        .textAlign(TextAlign.Center)
685        .fontColor(Color.Black)
686        .backgroundColor(Color.Green)
687      Divider()
688        .height(5)
689      Text("menu 2")
690        .fontSize(25)
691        .width(200)
692        .height(60)
693        .textAlign(TextAlign.Center)
694        .fontColor(Color.Black)
695        .backgroundColor(Color.Green)
696    }
697    .width(100)
698  }
699  build() {
700    Column() {
701      Column() {
702        Text("禁用浮起效果")
703          .fontSize(30)
704          .height(30)
705          .backgroundColor('#FFFFFF')
706          .margin({ top: 30 })
707        Image($r('app.media.startIcon'))
708          .width("40%")
709          .draggable(true)
710          .margin({ top: 15 })
711          .bindContextMenu(this.MenuBuilder, ResponseType.LongPress)
712          .onDragStart(() => {
713          })
714          .dragPreviewOptions({}, {
715            isLiftingDisabled: true
716          })
717          .dragPreview(this.dragPreviewBuilder, {
718            onlyForLifting: true,
719            delayCreating: true
720          })
721      }.width("%")
722      Column() {
723        Text("仅用于浮起效果")
724          .fontSize(30)
725          .height(30)
726          .backgroundColor('#FFFFFF')
727          .margin({ top: 80 })
728        Image($r('app.media.startIcon'))
729          .width("40%")
730          .draggable(true)
731          .margin({ top: 15 })
732          .onDragStart(() => {
733          })
734          .dragPreviewOptions({}, {
735            isLiftingDisabled: false
736          })
737          .dragPreview(this.dragPreviewBuilder, {
738            onlyForLifting: true,
739            delayCreating: true
740          })
741      }.width("100%")
742    }.height("100%")
743  }
744}
745```
746
747自定义预览图用于浮起效果。
748
749![onlyForLifting.gif](figures/onlyForLifting.gif)
750
751自定义预览图禁用浮起效果。
752
753![isLiftingDisabled.gif](figures/isLiftingDisabled.gif)
754