• 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| sizeChangeEffect<sup>18+</sup> | [DraggingSizeChangeEffect](#draggingsizechangeeffect18)<sup>18+</sup> | 否 | 用于选择长按浮起图与拖拽跟手图过渡效果。<br/>默认值:DraggingSizeChangeEffect.DEFAULT|
113
114## DragPreviewMode<sup>11+</sup>枚举说明
115
116| 名称 | 枚举值 | 描述 |
117| -------- | ------- | -------- |
118| AUTO  | 1 | 系统根据拖拽场景自动改变跟手点位置,根据规则自动对拖拽背板图进行缩放变换等。<br>**原子化服务API**:从API version 12开始,该接口支持在原子化服务中使用。 |
119| DISABLE_SCALE  | 2 | 禁用系统对拖拽背板图的缩放行为。<br>**原子化服务API**:从API version 12开始,该接口支持在原子化服务中使用。 |
120| ENABLE_DEFAULT_SHADOW<sup>12+</sup> | 3 | 启用非文本类组件默认阴影效果。<br>**原子化服务API**:从API version 12开始,该接口支持在原子化服务中使用。 |
121| ENABLE_DEFAULT_RADIUS<sup>12+</sup> | 4 | 启用非文本类组件统一圆角效果,默认值12vp。当应用自身设置的圆角值大于默认值或modifier设置的圆角时,则显示应用自定义圆角效果。<br>**原子化服务API**:从API version 12开始,该接口支持在原子化服务中使用。 |
122| ENABLE_DRAG_ITEM_GRAY_EFFECT<sup>18+</sup> | 5 | 启用支持原拖拽对象灰显(透明度)效果,对文本内容拖拽不生效。用户拖起时原对象显示灰显效果,释放时原对象恢复原有效果。开启默认灰显效果后,不建议在拖拽开始后自行修改透明度,如果开发者在拖拽发起后自行修改应用透明度,则灰显效果将被覆盖,且在结束拖拽时无法正确恢复原始透明度效果。<br>**原子化服务API**:从API version 18开始,该接口支持在原子化服务中使用。 |
123| ENABLE_MULTI_TILE_EFFECT<sup>18+</sup> | 6 | 启用支持多选对象鼠标拖拽不聚拢效果,当满足多选的情况下isMultiSelectionEnabled为true且生效时该参数才生效。不聚拢效果优先级高于[dragPreview](#dragpreview11)。不支持二次拖拽、圆角和缩放设置。<br>**原子化服务API**:从API version 18开始,该接口支持在原子化服务中使用。 |
124| ENABLE_TOUCH_POINT_CALCULATION_BASED_ON_FINAL_PREVIEW<sup>18+</sup> | 7 | 启用支持以拖拽预览图初始尺寸计算跟手点位置,鼠标拖拽,设置DragPreviewMode.ENABLE_MULTI_TILE_EFFECT时不生效。<br>**原子化服务API**:从API version 18开始,该接口支持在原子化服务中使用。 |
125
126## DraggingSizeChangeEffect<sup>18+</sup>
127
128当一个节点上同时设置长按浮起预览(参考[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu12))与拖拽时,使用该字段设置长按浮起预览图与拖拽跟手图过渡动效方式。
129
130**原子化服务API:** 从API version 18开始,该接口支持在原子化服务中使用。
131
132**系统能力:** SystemCapability.ArkUI.ArkUI.Full
133
134| 名称 | 值 | 说明 |
135| -------- | ------- | -------- |
136| DEFAULT | 0 | 发起拖拽时直接从菜单预览图切换为最终尺寸的拖拽跟手图。 |
137| SIZE_TRANSITION | 1 | 发起拖拽时,由菜单预览图直接切换为拖拽跟手图,但尺寸逐步从菜单预览图尺寸过渡到最终跟手图尺寸,设置DragPreviewMode.DISABLE_SCALE时尺寸过渡不生效。这在长按浮起预览图与拖拽跟手图相同时使用。 |
138| SIZE_CONTENT_TRANSITION | 2 | 发起拖拽时,由菜单预览图逐步过渡切换为最终拖拽跟手图,设置DragPreviewMode.DISABLE_SCALE时尺寸过渡不生效。这常用于菜单预览图与拖拽跟手图差异较大时使用,过渡效果包含内容透明度及尺寸变化。 |
139
140
141## DragInteractionOptions<sup>12+</sup>
142
143| 名称 | 类型 | 必填 | 描述 |
144| -------- | -------- | -------- | -------- |
145| 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/>**原子化服务API**:从API version 12开始,该接口支持在原子化服务中使用。 |
146| defaultAnimationBeforeLifting | boolean | 否 | 表示是否启用长按浮起阶段组件自身的默认点按效果(缩小)。true表示启用默认点按效果,false表示不启用默认点按效果。<br/>默认值:false <br/>**原子化服务API**:从API version 12开始,该接口支持在原子化服务中使用。 |
147| isLiftingDisabled<sup>15+</sup> | boolean | 否 | 表示长按拖拽时,是否禁用浮起效果。true表示禁用浮起效果,false表示不禁用浮起效果。<br/>如果设置为true,当组件支持拖拽并同时设置[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu8)时,仅弹出配置的自定义菜单预览。 <br/>默认值:false<br/>**原子化服务API**:从API version 15开始,该接口支持在原子化服务中使用。 |
148| enableEdgeAutoScroll<sup>18+</sup> | boolean | 否 | 设置在拖拽至可滚动组件边缘时是否触发自动滚屏。true表示触发自动滚屏,false表示不触发自动滚屏。<br />默认值:true<br/>**原子化服务API**:从API version 18开始,该接口支持在原子化服务中使用。 |
149| enableHapticFeedback<sup>18+</sup> | boolean | 否 | 表示拖拽时是否启用震动。true表示启用震动,false表示不启用震动。仅在存在蒙层的预览(通过[bindContextMenu](ts-universal-attributes-menu.md#bindcontextmenu12))场景生效。<br/>**注意:** 仅当应用具备 ohos.permission.VIBRATE 权限,且用户启用了触感反馈时才会生效。<br/>默认值:false<br/>**原子化服务API**:从API version 18开始,该接口支持在原子化服务中使用。 |
150
151## 示例
152### 示例1(允许拖拽和落入)
153
154该示例通过配置allowDrop和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拖拽')
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('不允许释放区域')
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('可释放区域')
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### 示例2(设置预览图)
273
274该示例通过配置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### 示例3(设置背板图样式)
338
339该示例通过配置dragPreviewOptions为ENABLE_DEFAULT_SHADOW、ENABLE_DEFAULT_RADIUS和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### 示例4(设置多选拖拽)
375
376该示例通过配置isMultiSelectionEnabled实现Grid组件的多选拖拽效果。
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### 示例5(设置默认点按效果)
415
416该示例通过配置defaultAnimationBeforeLifting实现Grid组件的默认点按效果。
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### 示例6(自定义背板图样式)
455
456该示例通过配置ImageModifier实现Image组件的自定义背板图样式。
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### 示例7(图片拖拽设置)
502
503该示例展示了不同图片(在线图片资源、本地图片资源和PixelMap)在拖拽时组件的设置。
504使用网络图片时,需要申请权限ohos.permission.INTERNET。具体申请方式请参考[声明权限](../../../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        // 在线图片资源拖出
552        Column() {
553          Text('Online Image').fontSize(14)
554          Image('https://www.example.com/xxx.png') // 请填写一个具体的网络图片地址
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        // 本地图片资源拖出
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        // PixelMap拖出
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      // 落入数据类型为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            // 通过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            // 通过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      // 落入数据类型为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            // 通过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              // 落盘到本地
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                // 直接打包进文件
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### 示例8(设置图片拖拽震动)
678该示例通过设置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### 示例9(自定义预览图)
726该示例通过配置onlyForLifting实现自定义预览图,仅用于浮起效果以及配置isLiftingDisabled实现禁用浮起效果。
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("禁用浮起效果")
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("仅用于浮起效果")
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
815自定义预览图用于浮起效果。
816
817![onlyForLifting.gif](figures/onlyForLifting.gif)
818
819自定义预览图禁用浮起效果。
820
821![isLiftingDisabled.gif](figures/isLiftingDisabled.gif)
822
823### 示例10(以拖拽预览图初始尺寸计算跟手点位置)
824该示例通过配置DragPreviewMode.ENABLE_TOUCH_POINT_CALCULATION_BASED_ON_FINAL_PREVIEW实现以拖拽预览图初始尺寸计算跟手点位置,设置DragPreviewMode.ENABLE_MULTI_TILE_EFFECT时不生效。
825```ts
826@Entry
827@Component
828struct Index {
829  private iconStr: ResourceStr = $r("app.media.app_icon")
830
831  @Builder
832  MyPreview() {
833    Image($r('app.media.image'))
834      .width(100)
835      .height(100)
836  }
837
838  @Builder
839  MyMenuPreview() {
840    Column() {
841      Image($r('app.media.image'))
842        .width(100)
843        .height(100)
844    }
845    .backgroundColor(Color.Green)
846    .width(300)
847    .height(300)
848  }
849
850  @Builder
851  MyMenu() {
852    Menu() {
853      MenuItem({ startIcon: this.iconStr, content: "菜单选项" })
854      MenuItem({ startIcon: this.iconStr, content: "菜单选项" })
855    }
856  }
857
858  @Builder
859  SubMenu() {
860    Menu() {
861      MenuItem({ content: "复制", labelInfo: "Ctrl+C" })
862      MenuItem({ content: "粘贴", labelInfo: "Ctrl+V" })
863    }
864  }
865
866  build() {
867    NavDestination() {
868      Scroll() {
869        Column() {
870          Text("no ENABLE_TOUCH_POINT_CALCULATION_BASED_ON_FINAL_PREVIEW")
871          Image($r('app.media.image'))
872            .width(200)
873            .height(200)
874            .bindContextMenu(this.MyMenu, ResponseType.LongPress, {
875              preview: this.MyPreview
876            })
877            .dragPreview(this.MyMenuPreview)
878            .draggable(true)
879
880          Text("ENABLE_TOUCH_POINT_CALCULATION_BASED_ON_FINAL_PREVIEW")
881          Image($r('app.media.image'))
882            .width(200)
883            .height(200)
884            .bindContextMenu(this.MyMenu, ResponseType.LongPress, {
885              preview: this.MyPreview
886            })
887            .dragPreview(this.MyMenuPreview)
888            .draggable(true)
889            .dragPreviewOptions({
890              mode: [DragPreviewMode.ENABLE_TOUCH_POINT_CALCULATION_BASED_ON_FINAL_PREVIEW]
891            })
892        }.width('100%')
893      }
894    }
895    .height('100%')
896    .width('100%')
897  }
898}
899```
900
901![touchPointer.gif](figures/touchPointer.gif)
902
903### 示例11(长按浮起预览图与拖拽跟手图过渡动效)
904该示例通过配置DraggingSizeChangeEffect实现不同拖拽过渡效果。
905```ts
906@Entry
907@Component
908struct Index {
909  private iconStr: ResourceStr = $r("app.media.app_icon")
910
911  @Builder
912  MyPreview() {
913    Image($r('app.media.image'))
914      .width(200)
915      .height(200)
916  }
917
918  @Builder
919  MyMenuPreviewSame() {
920    Column() {
921      Image($r('app.media.image'))
922        .width(300)
923        .height(300)
924    }
925  }
926
927  @Builder
928  MyMenuPreview() {
929    Column() {
930      Image($r('app.media.startIcon'))
931        .width(300)
932        .height(300)
933    }
934  }
935
936  @Builder
937  MyMenu() {
938    Menu() {
939      MenuItem({ startIcon: this.iconStr, content: "菜单选项" })
940      MenuItem({ startIcon: this.iconStr, content: "菜单选项" })
941    }
942  }
943
944  @Builder
945  SubMenu() {
946    Menu() {
947      MenuItem({ content: "复制", labelInfo: "Ctrl+C" })
948      MenuItem({ content: "粘贴", labelInfo: "Ctrl+V" })
949    }
950  }
951
952  build() {
953    Column() {
954      Text("sizeChangeEffect: SIZE_TRANSITION,长按弹出菜单,拖拽移动后菜单预览图过渡到跟手图,有缩放无叠加效果")
955        .margin({ top: 10 })
956      Image($r('app.media.image'))
957        .width(200)
958        .height(200)
959        .bindContextMenu(this.MyMenu, ResponseType.LongPress, {
960          preview: this.MyMenuPreviewSame
961        })
962        .dragPreview(this.MyPreview)
963        .dragPreviewOptions({
964          sizeChangeEffect: DraggingSizeChangeEffect.SIZE_TRANSITION
965        })
966        .draggable(true)
967
968      Text("sizeChangeEffect: SIZE_CONTENT_TRANSITION,长按弹出菜单,拖拽移动后菜单预览图和拖拽跟手图两层叠加过渡")
969        .margin({ top: 10 })
970      Image($r('app.media.image'))
971        .width(200)
972        .height(200)
973        .bindContextMenu(this.MyMenu, ResponseType.LongPress, {
974          preview: this.MyMenuPreview
975        })
976        .dragPreview(this.MyPreview)
977        .dragPreviewOptions({
978          sizeChangeEffect: DraggingSizeChangeEffect.SIZE_CONTENT_TRANSITION
979        })
980        .draggable(true)
981    }
982    .height('100%')
983    .width('100%')
984  }
985}
986```
987
988![sizeChangeEffect.gif](figures/sizeChangeEffect.gif)