主要概念

以下部分介绍了拖放过程的一些关键概念。

拖放过程

拖放过程有四个步骤或状态:已开始、 继续、丢弃和结束

已开始

为响应用户的拖动手势,您的应用会调用 startDragAndDrop(),用于指示系统开始执行拖放操作。通过 方法的参数提供以下内容:

  • 要拖动的数据。
  • 用于绘制拖动阴影的回调
  • 用于描述所拖动数据的元数据
  • 系统通过回调应用进行响应以获取拖动 阴影。然后,系统会在设备上显示拖动阴影。
  • 接下来,系统发送具有操作类型的拖动事件 ACTION_DRAG_STARTED:拖动到拖动事件 当前布局中所有 View 对象的监听器。接收者 继续接收拖拽事件,包括可能的放下 事件 - 拖动事件监听器必须返回 true。这会注册 监听器。只有已注册的监听器才能继续 用于接收拖动事件。此时,监听器也可更改 拖放目标 View 对象的外观,表明该视图可以 接受丢弃事件。
  • 如果拖动事件监听器返回 false,则不会接收拖动事件 事件,直到系统发送拖动事件 操作类型为ACTION_DRAG_ENDED。 通过返回 false,监听器会告知系统它不感兴趣 而不想接受拖动的数据。
正在继续
用户继续拖动。当拖动阴影与 边界框,则系统会发送一个或多个拖动事件 拖动事件监听器。监听器可能会改变 放置目标 View 以响应该事件。例如,如果事件 表示拖动阴影会进入拖放的边界框 target - 操作类型 ACTION_DRAG_ENTERED — 监听器可通过突出显示 View 来做出反应。
已放下
用户在拖放的边界框内释放拖动阴影 目标。系统向放置目标的监听器发送包含操作的拖动事件 类型 ACTION_DROP。 拖动事件对象包含在 对启动操作的 startDragAndDrop() 的调用。监听器为 如果监听器成功,预期会返回布尔值 true 给系统 处理丢弃的数据。 :仅当用户将拖动阴影放在 View 的边界框,其监听器已注册接收拖动事件 (拖放目标)。如果用户在任何其他时间之后释放拖动阴影, 系统不会发送任何 ACTION_DROP 拖动事件。
已结束

在用户释放拖动阴影且系统发送

触发操作类型为 ACTION_DROP 的拖动事件,如有必要, 会发送一个操作类型为 ACTION_DRAG_ENDED 的拖动事件,以指明 拖放操作结束。无论用户身在何处 释放拖动阴影。该事件会发送到 注册以接收拖动事件,即使监听器也收到了 ACTION_DROP 事件。

有关以上每个步骤的详细介绍,请参阅 拖放操作

拖动事件

系统以 DragEvent 对象的形式发出拖动事件, 包含描述拖放操作中发生的情况的操作类型 过程。该对象还可能包含其他数据,具体取决于操作类型。

拖动事件监听器可接收 DragEvent 对象。为了获取操作类型,监听器会调用 DragEvent.getAction()。有六个可能的值由 DragEvent 类中的常量定义, 如表 1 所述:

表 1. DragEvent 操作类型

操作类型 含义
ACTION_DRAG_STARTED 应用调用 startDragAndDrop() 并获取 拖动阴影。如果监听器想要继续接收拖动事件 对于此操作,必须向true 系统。
ACTION_DRAG_ENTERED 拖动阴影会进入拖动事件监听器的 View。这是监听器的第一个事件操作类型 。
ACTION_DRAG_LOCATION 在其后 ACTION_DRAG_ENTERED 事件,拖动阴影仍然是 在拖动事件监听器的 View
ACTION_DRAG_EXITED 已关注“ACTION_DRAG_ENTERED”和至少一个 ACTION_DRAG_LOCATION 事件,拖动阴影会移动 在拖动事件监听器的 View
ACTION_DROP 拖动阴影会在拖动事件监听器的 View。系统会将此操作类型发送到 View。 仅当监听器返回布尔值时,才返回该对象的监听器 true 回复 ACTION_DRAG_STARTED拖动事件。此操作类型 当用户将拖动阴影释放到 View 上时发送 其监听器未注册或用户松开拖动操作 覆盖不属于当前布局的任何内容。

如果监听器返回布尔值 true, 成功处理拖放操作否则,它必须返回 false

ACTION_DRAG_ENDED 系统即将结束拖放操作。此操作类型 并不一定在 ACTION_DROP 事件之后。如果 系统会发送 ACTION_DROP,收到 ACTION_DRAG_ENDED操作类型并不意味着 投放成功。监听器必须调用 getResult(), 如表 2 所示,以获取 在响应 ACTION_DROP 时返回。如果 未发送 ACTION_DROP 事件,则 getResult() 会返回 false

DragEvent 对象还包含应用的数据和元数据 在 startDragAndDrop() 调用中向系统提供的内容。部分数据 仅对某些操作类型有效(如表 2 所述)。有关 事件及其相关数据的详细信息,请参阅 拖放操作

表 2. 按操作类型列出的有效 DragEvent 数据

getAction()
getClipDescription()
getLocalState()
getX()
getY()
getClipData()
getResult()
ACTION_DRAG_STARTED ✓ ✓        
ACTION_DRAG_ENTERED ✓ ✓        
ACTION_DRAG_LOCATION ✓ ✓ ✓ ✓    
ACTION_DRAG_EXITED ✓ ✓        
ACTION_DROP ✓ ✓ ✓ ✓ ✓  
ACTION_DRAG_ENDED   ✓       ✓

DragEvent 方法 getAction()describeContents(), writeToParcel()、 和toString()始终 返回有效数据。

如果某个方法不包含特定操作类型的有效数据,它会返回 null 或 0,具体取决于其结果类型。

拖动阴影

在执行拖放操作期间,系统会显示一张图片,让用户 拖动。对于数据移动操作,此图片表示用户正在拖动的数据。对于其他操作,此图片表示拖动操作的某个方面。

此图片称为拖动阴影。您可以使用针对 一 View.DragShadowBuilder 对象。您在开始拖放时将构建器传递给系统 操作(使用 startDragAndDrop())。作为对 startDragAndDrop() 时,系统会调用您在 View.DragShadowBuilder 以获取拖动阴影。

View.DragShadowBuilder 类有两个构造函数:

View.DragShadowBuilder(View)

该构造函数可接受您的应用的任何 View 对象。该构造函数会将 View 对象存储在 View.DragShadowBuilder 对象中,以便回调可以获取视图对象来构造拖动阴影。视图不一定是 用户选择开始拖动操作的 View

如果您使用该构造函数,则无需扩展 View.DragShadowBuilder,也无需替换其方法。默认情况下,系统会拖动 与作为实参传递的 View 具有相同的外观的阴影, 且广告位于用户轻触屏幕的位置之下。

View.DragShadowBuilder()

如果使用此构造函数,则 View 对象在 View.DragShadowBuilder 对象。该字段设置为 null。您必须延长 View.DragShadowBuilder 并替换其方法,否则您会收到 隐藏真实的拖动阴影。系统不会抛出错误。

View.DragShadowBuilder 类有两个方法,可共同创建拖动操作 阴影:

onProvideShadowMetrics()

在您调用 startDragAndDrop() 后,系统会立即调用该方法。使用该方法可以向系统发送拖动阴影的尺寸和接触点。该方法有两个参数:

outShadowSize:一个 Point 对象。进入拖动阴影宽度 x,并将其高度存储在 y

outShadowTouchPoint:一个 Point 对象。接触点是 处于用户手指下方的拖动阴影内。 其 X 位置存储在 x 中,Y 位置存储在 y 中。

onDrawShadow()

调用 onProvideShadowMetrics() 后,系统会立即调用 onDrawShadow() 来创建拖动阴影。该方法具有单个 参数,即一个 Canvas 对象, 系统会利用您在 onProvideShadowMetrics()。该方法会在提供的 Canvas

为了提升性能,请保持较小的拖动阴影大小。对于单个 那么您可能需要使用图标如果选择了多个项目 希望使用堆栈中的图标,而不是在屏幕上展开的完整图片。

拖动事件监听器和回调方法

View 使用实现 View.OnDragListener 或视图的 onDragEvent() 回调方法。时间 系统调用该方法或监听器时,它会提供一个 DragEvent 参数。

在大多数情况下,使用监听器比使用回调方法更可取。设计界面时,您通常不需要为 View 类创建子类,但使用回调方法时,您必须创建子类来替换回调方法。相比之下,您可以实现一个监听器类,然后将其与多个不同的 View 对象配合使用。您还可以将其实现为匿名内联类 或 lambda 表达式。如需为 View 对象设置监听器,请调用 setOnDragListener()

作为替代方案,您可以更改 onDragEvent() 的默认实现。 而无需重写 方法。设置 OnReceiveContentListener 观看次数;有关详情,请参阅 setOnReceiveContentListener()。 然后,onDragEvent() 方法将默认执行以下操作:

  • 返回 true 以响应对 startDragAndDrop() 的调用。
  • 通话 performReceiveContent() 如果拖放数据被放到了视图上。相应数据会传递到 方法指定为 ContentInfo 对象。通过 方法会调用 OnReceiveContentListener

  • 如果拖放数据被放到视图上,且 OnReceiveContentListener 会使用其中的任何内容。

定义 OnReceiveContentListener 来专门为您的 API 处理数据, 应用。要向后兼容到 API 级别 24,请使用 OnReceiveContentListener

您可以在以下位置为 View 对象添加拖动事件监听器和回调方法: 在这种情况下,系统会首先调用监听器。系统不会调用 回调方法,除非监听器返回 false

onDragEvent() 方法和 View.OnDragListener 的组合为 类似于 onTouchEvent()View.OnTouchListener 与触摸事件结合使用