The following sections explains a few key concepts for drag-and-drop process.
Drag-and-drop process
There are four steps or states in the drag-and-drop process: started, continuing, dropped, and ended.
- Started
In response to a user's drag gesture, your application calls
startDragAndDrop()
to tell the system to start a drag-and-drop operation. The method's arguments provide the following:- The data to be dragged.
- A callback for drawing the drag shadow
- Metadata that describes the dragged data
- The system responds by calling back to your application to get a drag shadow. The system then displays the drag shadow on the device.
- Next, the system sends a drag event with action type
ACTION_DRAG_STARTED
to the drag event listener of allView
objects in the current layout. To continue to receive drag events—including a possible drop event—the drag event listener must returntrue
. This registers the listener with the system. Only registered listeners continue to receive drag events. At this point, listeners can also change the appearance of their drop targetView
object to show that the view can accept a drop event. - If the drag event listener returns
false
, it doesn't receive drag events for the current operation until the system sends a drag event with action typeACTION_DRAG_ENDED
. By returningfalse
, the listener tells the system that it isn't interested in the drag-and-drop operation and doesn't want to accept the dragged data.
- Continuing
- The user continues the drag. As the drag shadow intersects the
bounding box of a drop target, the system sends one or more drag events to
the target's drag event listener. The listener might alter the appearance of
the drop target
View
in response to the event. For example, if the event indicates that the drag shadow enters the bounding box of the drop target—action typeACTION_DRAG_ENTERED
—the listener can react by highlighting theView
. - Dropped
- The user releases the drag shadow within the bounding box of a drop
target. The system sends the drop target's listener a drag event with action
type
ACTION_DROP
. The drag event object contains the data that passes to the system in the call tostartDragAndDrop()
that starts the operation. The listener is expected to return booleantrue
to the system if the listener successfully processes the dropped data. : This step only occurs if the user drops the drag shadow within the bounding box of aView
whose listener is registered to receive drag events (a drop target). If the user releases the drag shadow in any other situation, noACTION_DROP
drag event is sent. - Ended
After the user releases the drag shadow, and after the system sends
out a drag event with action type
ACTION_DROP
, if necessary, the system sends a drag event with action typeACTION_DRAG_ENDED
to indicate that the drag-and-drop operation is over. This is done regardless of where the user releases the drag shadow. The event is sent to every listener that is registered to receive drag events, even if the listener also receives theACTION_DROP
event.
Each of these steps is described in more detail in the section called A drag-and-drop operation.
Drag events
The system sends out a drag event in the form of a DragEvent
object, which
contains an action type that describes what is happening in the drag-and-drop
process. Depending on the action type, the object can also contain other data.
Drag event listeners receive the DragEvent
object. To get the action type,
listeners call
DragEvent.getAction()
.
There are six possible values defined by constants in the DragEvent
class,
which are described in table 1:
Action type | Meaning |
---|---|
ACTION_DRAG_STARTED |
The application calls startDragAndDrop() and obtains
a drag shadow. If the listener wants to continue receiving drag events
for this operation, it must return boolean true to the
system.
|
ACTION_DRAG_ENTERED |
The drag shadow enters the bounding box of the drag event listener's
View . This is the first event action type the listener
receives when the drag shadow enters the bounding box.
|
ACTION_DRAG_LOCATION |
Subsequent to an
ACTION_DRAG_ENTERED event, the drag shadow is still
within the bounding box of the drag event listener's
View .
|
ACTION_DRAG_EXITED |
Following an ACTION_DRAG_ENTERED and at least one
ACTION_DRAG_LOCATION event, the drag shadow moves
outside the bounding box of the drag event listener's
View .
|
ACTION_DROP |
The drag shadow releases over the drag event listener's
View . This action type is sent to a View
object's listener only if the listener returns boolean
true in response to the
ACTION_DRAG_STARTED drag event. This action type isn't
sent if the user releases the drag shadow over a View
whose listener isn't registered or if the user releases the drag
shadow over anything that isn't part of the current layout.
The listener returns boolean |
ACTION_DRAG_ENDED |
The system is ending the drag-and-drop operation. This action type
isn't necessarily preceded by an ACTION_DROP event. If
the system sends an ACTION_DROP , receiving the
ACTION_DRAG_ENDED action type doesn't imply that the
drop succeeded. The listener must call
getResult() ,
as shown in table 2, to get the value that is
returned in response to ACTION_DROP . If an
ACTION_DROP event isn't sent, then
getResult() returns false .
|
The DragEvent
object also contains the data and metadata that your application
provides to the system in the call to startDragAndDrop()
. Some of the data is
valid only for certain action types as summarized in table 2. For more
information about events and their associated data, see the section called A
drag-and-drop operation.
getAction() value |
getClipDescription() value |
getLocalState() value |
getX() value |
getY() value |
getClipData() value |
getResult() value |
---|---|---|---|---|---|---|
ACTION_DRAG_STARTED |
✓ | ✓ | ||||
ACTION_DRAG_ENTERED |
✓ | ✓ | ||||
ACTION_DRAG_LOCATION |
✓ | ✓ | ✓ | ✓ | ||
ACTION_DRAG_EXITED |
✓ | ✓ | ||||
ACTION_DROP |
✓ | ✓ | ✓ | ✓ | ✓ | |
ACTION_DRAG_ENDED |
✓ | ✓ |
The DragEvent
methods getAction()
,
describeContents()
,
writeToParcel()
,
and toString()
always
return valid data.
If a method doesn't contain valid data for a particular action type, it returns
null
or 0, depending on its result type.
Drag shadow
During a drag-and-drop operation, the system displays an image that the user drags. For data movement, this image represents the data being dragged. For other operations, the image represents some aspect of the drag operation.
The image is called a drag shadow. You create it with methods you declare for
a
View.DragShadowBuilder
object. You pass the builder to the system when you start a drag-and-drop
operation using startDragAndDrop()
. As part of its response to
startDragAndDrop()
, the system invokes the callback methods you define in
View.DragShadowBuilder
to obtain a drag shadow.
The View.DragShadowBuilder
class has two constructors:
View.DragShadowBuilder(View)
This constructor accepts any of your application's
View
objects. The constructor stores theView
object in theView.DragShadowBuilder
object, so the callbacks can access it to construct the drag shadow. The view doesn't have to be aView
that the user selects to start the drag operation.If you use this constructor, you don't have to extend
View.DragShadowBuilder
or override its methods. By default, you get a drag shadow that has the same appearance as theView
you pass as an argument, centered under the location where the user touches the screen.View.DragShadowBuilder()
If you use this constructor, no
View
object is available in theView.DragShadowBuilder
object. The field is set tonull
. You must extendView.DragShadowBuilder
and override its methods, or else you get an invisible drag shadow. The system doesn't throw an error.
The View.DragShadowBuilder
class has two methods that together create the drag
shadow:
onProvideShadowMetrics()
The system calls this method immediately after you call
startDragAndDrop()
. Use the method to send the dimensions and touch point of the drag shadow to the system. The method has two parameters:outShadowSize
: aPoint
object. The drag shadow width goes inx
, and its height goes iny
.outShadowTouchPoint
: aPoint
object. The touch point is the location within the drag shadow that must be under the user's finger during the drag. Its X position goes inx
and its Y position goes iny
.onDrawShadow()
Immediately after the call to
onProvideShadowMetrics()
the system callsonDrawShadow()
to create the drag shadow. The method has a single argument, aCanvas
object that the system constructs from the parameters you provide inonProvideShadowMetrics()
. The method draws the drag shadow on the providedCanvas
.
To improve performance, keep the size of the drag shadow small. For a single item, you might want to use an icon. For a multiple-item selection, you might want to use icons in a stack rather than full images spread out over the screen.
Drag event listeners and callback methods
A View
receives drag events with a drag event listener that implements
View.OnDragListener
or with the view's onDragEvent()
callback method. When
the system calls the method or listener, it provides a
DragEvent
argument.
In most cases, using a listener is preferable to using the callback method. When
you design UIs, you usually don't subclass View
classes, but using the
callback method forces you to create subclasses to override the method. In
comparison, you can implement one listener class and then use it with multiple
different View
objects. You can also implement it as an anonymous inline class
or lambda expression. To set the listener for a View
object, call
setOnDragListener()
.
As an alternative, you can alter the default implementation of onDragEvent()
without overriding the method. Set an
OnReceiveContentListener
on a view; for more details, see
setOnReceiveContentListener()
.
The onDragEvent()
method then does the following by default:
- Returns true in response to the call to
startDragAndDrop()
. Calls
performReceiveContent()
if the drag-and-drop data is dropped on the view. The data is passed to the method as aContentInfo
object. The method invokes theOnReceiveContentListener
.Returns true if the drag-and-drop data is dropped on the view and the
OnReceiveContentListener
consumes any of the content.
Define the OnReceiveContentListener
to handle the data specifically for your
app. For backward compatibility down to API level 24, use the Jetpack version of
OnReceiveContentListener
.
You can have a drag event listener and a callback method for a View
object, in
which case the system first calls the listener. The system doesn't call the
callback method unless the listener returns false
.
The combination of the onDragEvent()
method and View.OnDragListener
is
analogous to the combination of the
onTouchEvent()
and View.OnTouchListener
used with touch events.