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_STARTEDto the drag event listener of allViewobjects 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 targetViewobject 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
Viewin 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 booleantrueto 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 aViewwhose listener is registered to receive drag events (a drop target). If the user releases the drag shadow in any other situation, noACTION_DROPdrag 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_ENDEDto 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_DROPevent.
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:
Table 1. DragEvent action types
| 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.
Table 2. Valid DragEvent data by action type
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
Viewobjects. The constructor stores theViewobject in theView.DragShadowBuilderobject, so the callbacks can access it to construct the drag shadow. The view doesn't have to be aViewthat the user selects to start the drag operation.If you use this constructor, you don't have to extend
View.DragShadowBuilderor override its methods. By default, you get a drag shadow that has the same appearance as theViewyou pass as an argument, centered under the location where the user touches the screen.View.DragShadowBuilder()If you use this constructor, no
Viewobject is available in theView.DragShadowBuilderobject. The field is set tonull. You must extendView.DragShadowBuilderand 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: aPointobject. The drag shadow width goes inx, and its height goes iny.outShadowTouchPoint: aPointobject. 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 inxand 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, aCanvasobject 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 aContentInfoobject. The method invokes theOnReceiveContentListener.Returns true if the drag-and-drop data is dropped on the view and the
OnReceiveContentListenerconsumes 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.