Drag and drop

With the Android drag/drop framework, you can allow your users to move data
from one View to another using a graphical drag and drop gesture.
The framework includes a drag event class, drag listeners, and helper methods and classes.

Although the framework is primarily designed for data movement, you can use
it for other UI actions. For example, you could create an app that mixes colors when the user
drags a color icon over another icon. The rest of this topic, however, describes the
framework in terms of data movement.

Overview

A drag and drop operation starts when the user makes some gesture that you recognize as a
signal to start dragging data. In response, your application tells the system that the drag is
starting. The system calls back to your application to get a representation of the data
being dragged. As the user's finger moves this representation (a "drag shadow")
over the current layout, the system sends drag events to the drag event listener objects and
drag event callback methods associated with the View objects in the layout.
Once the user releases the drag shadow, the system ends the drag operation.

Note: For the sake of simplicity, the following sections refer to the routine
that receives drag events as the "drag event listener", even though it may actually
be a callback method.

When you start a drag, you include both the data you are moving and metadata describing this
data as part of the call to the system. During the drag, the system sends drag events to the
drag event listeners or callback methods of each View in the layout. The listeners or callback
methods can use the metadata to decide if they want to accept the data when it is dropped.
If the user drops the data over a View object, and that View object's listener or callback
method has previously told the system that it wants to accept the drop, then the system sends
the data to the listener or callback method in a drag event.

Your application tells the system to start a drag by calling the
startDrag()
method. This tells the system to start sending drag events. The method also sends the data that
you are dragging.

You can call
startDrag()
for any attached View in the current layout. The system only uses the View object to get access
to global settings in your layout.

Once your application calls
startDrag(),
the rest of the process uses events that the system sends to the View objects in your current
layout.

The drag/drop process

There are basically four steps or states in the drag and drop process:

Started

In response to the user's gesture to begin a drag, your application calls
startDrag()
to tell the system to start a drag. The arguments
startDrag()
provide the data to be dragged, metadata for this data, and a callback for drawing the
drag shadow.

The system first responds by calling back to your application to get a drag shadow. It
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 listeners for
all the View objects in the current layout. To continue to receive drag events,
including a possible drop event, a drag event listener must return true.
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
View object to show that the listener can accept a drop event.

If the drag event listener returns false, then it will not receive drag
events for the current operation until the system sends a drag event with action type
ACTION_DRAG_ENDED. By sending false, the
listener tells the system that it is not interested in the drag operation and
does not want to accept the dragged data.

Continuing

The user continues the drag. As the drag shadow intersects the bounding box of a View
object, the system sends one or more drag events to the View object's drag event
listener (if it is registered to receive events). The listener may choose to
alter its View object's appearance in response to the event. For example, if the event
indicates that the drag shadow has entered the bounding box of the View
(action type ACTION_DRAG_ENTERED), the listener
can react by highlighting its View.

Dropped

The user releases the drag shadow within the bounding box of a View that can accept the
data. The system sends the View object's listener a drag event with action type
ACTION_DROP. The drag event contains the data that was
passed to the system in the call to
startDrag()
that started the operation. The listener is expected to return boolean true to
the system if code for accepting the drop succeeds.

Note that this step only occurs if the user drops the drag shadow within the bounding
box of a View whose listener is registered to receive drag events. If the user releases
the drag shadow in any other situation, no ACTION_DROP
drag event is sent.

Ended

After the user releases the drag shadow, and after the system sends out (if necessary)
a drag event with action type ACTION_DROP, the system sends
out a drag event with action type ACTION_DRAG_ENDED to
indicate that the drag operation is over. This is done regardless of where the user released
the drag shadow. The event is sent to every listener that is registered to receive drag
events, even if the listener received the ACTION_DROP event.

You will probably want to use the listener in most cases. When you design UIs, you usually
don't subclass View classes, but using the callback method forces you to do this in order to
override the method. In comparison, you can implement one listener class and then use it with
several different View objects. You can also implement it as an anonymous inline class. To
set the listener for a View object, call
setOnDragListener().

You can have both a listener and a callback method for View object. If this occurs,
the system first calls the listener. The system doesn't call the callback method unless the
listener returns false.

Drag events

The system sends out a drag event in the form of a DragEvent object. The
object contains an action type that tells the listener what is happening in the drag/drop
process. The object contains other data, depending on the action type.

To get the action type, a listener calls getAction(). There
are six possible values, defined by constants in the DragEvent class. These
are listed in table 1.

The DragEvent object also contains the data that your application provided
to the system in the call to
startDrag().
Some of the data is valid only for certain action types. The data that is valid for each action
type is summarized in table 2. It is also described in detail with
the event for which it is valid in the section
Designing a Drag and Drop Operation.

A View object's drag event listener receives this event action type when the drag shadow
has just entered the bounding box of the View. This is the first event action type the
listener receives when the drag shadow enters the bounding box. If the listener wants to
continue receiving drag events for this operation, it must return boolean
true to the system.

A View object's drag event listener receives this event action type after it receives a
ACTION_DRAG_ENTERED and at least one
ACTION_DRAG_LOCATION event, and after the user has moved
the drag shadow outside the bounding box of the View.

A View object's drag event listener receives this event action type when the user
releases the drag shadow over the View object. This action type is only sent to a View
object's listener if the listener returned boolean true in response to the
ACTION_DRAG_STARTED drag event. This action type is not
sent if the user releases the drag shadow on a View whose listener is not registered,
or if the user releases the drag shadow on anything that is not part of the current
layout.

The listener is expected to return boolean true if it successfully
processes the drop. Otherwise, it should return false.

A View object's drag event listener receives this event action type
when the system is ending the drag operation. This action type is not necessarily
preceded by an ACTION_DROP event. If the system sent
a ACTION_DROP, receiving the
ACTION_DRAG_ENDED action type does not imply that the
drop operation succeeded. The listener must call
getResult() to get the value that was
returned in response to ACTION_DROP. If an
ACTION_DROP event was not sent, then
getResult() returns false.

If a method does not contain valid data for a particular action type, it returns either
null or 0, depending on its result type.

The drag shadow

During a drag and drop operation, the system displays a 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.

This constructor accepts any of your application's
View objects. The constructor stores the View object
in the View.DragShadowBuilder object, so during
the callback you can access it as you construct your drag shadow.
It doesn't have to be associated with the View (if any) that the user
selected 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 will get a drag shadow that has the same appearance as the View you pass as an
argument, centered under the location where the user is touching the screen.

If you use this constructor, no View object is available in the
View.DragShadowBuilder object (the field is set to null).
If you use this constructor, and you don't extend
View.DragShadowBuilder or override its methods,
you will get an invisible drag shadow.
The system does not give an error.

To improve performance, you should keep the size of the drag shadow small. For a single item,
you may want to use a icon. For a multiple selection, you may want to use icons in a stack
rather than full images spread out over the screen.

Designing a Drag and Drop Operation

This section shows step-by-step how to start a drag, how to respond to events during
the drag, how respond to a drop event, and how to end the drag and drop operation.

Starting a drag

The user starts a drag with a drag gesture, usually a long press, on a View object.
In response, you should do the following:

As necessary, create a ClipData and
ClipData.Item for the data being moved. As part of the
ClipData object, supply metadata that is stored in a ClipDescription
object within the ClipData. For a drag and drop operation that does not represent data
movement, you may want to use null instead of an actual object.

For example, this code snippet shows how to respond to a long press on a ImageView
by creating a ClipData object that contains the tag or label of an
ImageView. Following this snippet, the next snippet shows how to override the methods in
View.DragShadowBuilder:

Java

// Create a string for the ImageView label
private static final String IMAGEVIEW_TAG = "icon bitmap"
// Creates a new ImageView
ImageView imageView = new ImageView(this);
// Sets the bitmap for the ImageView from an icon bit map (defined elsewhere)
imageView.setImageBitmap(iconBitmap);
// Sets the tag
imageView.setTag(IMAGEVIEW_TAG);
...
// Sets a long click listener for the ImageView using an anonymous listener object that
// implements the OnLongClickListener interface
imageView.setOnLongClickListener(new View.OnLongClickListener() {
// Defines the one method for the interface, which is called when the View is long-clicked
public boolean onLongClick(View v) {
// Create a new ClipData.
// This is done in two steps to provide clarity. The convenience method
// ClipData.newPlainText() can create a plain text ClipData in one step.
// Create a new ClipData.Item from the ImageView object's tag
ClipData.Item item = new ClipData.Item(v.getTag());
// Create a new ClipData using the tag as a label, the plain text MIME type, and
// the already-created item. This will create a new ClipDescription object within the
// ClipData, and set its MIME type entry to "text/plain"
ClipData dragData = new ClipData(
v.getTag(),
new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN },
item);
// Instantiates the drag shadow builder.
View.DragShadowBuilder myShadow = new MyDragShadowBuilder(imageView);
// Starts the drag
v.startDrag(dragData, // the data to be dragged
myShadow, // the drag shadow builder
null, // no need to use local data
0 // flags (not currently used, set to 0)
);
}
}

The following code snippet defines myDragShadowBuilder
It creates a drag shadow for dragging a TextView as a small gray rectangle:

Kotlin

private class MyDragShadowBuilder(v: View) : View.DragShadowBuilder(v) {
private val shadow = ColorDrawable(Color.LTGRAY)
// Defines a callback that sends the drag shadow dimensions and touch point back to the
// system.
override fun onProvideShadowMetrics(size: Point, touch: Point) {
// Sets the width of the shadow to half the width of the original View
val width: Int = view.width / 2
// Sets the height of the shadow to half the height of the original View
val height: Int = view.height / 2
// The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
// Canvas that the system will provide. As a result, the drag shadow will fill the
// Canvas.
shadow.setBounds(0, 0, width, height)
// Sets the size parameter's width and height values. These get back to the system
// through the size parameter.
size.set(width, height)
// Sets the touch point's position to be in the middle of the drag shadow
touch.set(width / 2, height / 2)
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
override fun onDrawShadow(canvas: Canvas) {
// Draws the ColorDrawable in the Canvas passed in from the system.
shadow.draw(canvas)
}
}

Java

private static class MyDragShadowBuilder extends View.DragShadowBuilder {
// The drag shadow image, defined as a drawable thing
private static Drawable shadow;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v) {
// Stores the View parameter passed to myDragShadowBuilder.
super(v);
// Creates a draggable image that will fill the Canvas provided by the system.
shadow = new ColorDrawable(Color.LTGRAY);
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the
// system.
@Override
public void onProvideShadowMetrics (Point size, Point touch) {
// Defines local variables
private int width, height;
// Sets the width of the shadow to half the width of the original View
width = getView().getWidth() / 2;
// Sets the height of the shadow to half the height of the original View
height = getView().getHeight() / 2;
// The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
// Canvas that the system will provide. As a result, the drag shadow will fill the
// Canvas.
shadow.setBounds(0, 0, width, height);
// Sets the size parameter's width and height values. These get back to the system
// through the size parameter.
size.set(width, height);
// Sets the touch point's position to be in the middle of the drag shadow
touch.set(width / 2, height / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
@Override
public void onDrawShadow(Canvas canvas) {
// Draws the ColorDrawable in the Canvas passed in from the system.
shadow.draw(canvas);
}
}

Responding to a drag start

During the drag operation, the system dispatches drag events to the drag event listeners
of the View objects in the current layout. The listeners should react
by calling getAction() to get the action type.
At the start of a drag, this methods returns ACTION_DRAG_STARTED.

In response to an event with the action type ACTION_DRAG_STARTED,
a listener should do the following:

If the drag and drop operation does not represent data movement, this may not be
necessary.

If the listener can accept a drop, it should return true. This tells
the system to continue to send drag events to the listener.
If it can't accept a drop, it should return false, and the system
will stop sending drag events until it sends out
ACTION_DRAG_ENDED.

Handling events during the drag

During the drag, listeners that returned true in response to
the ACTION_DRAG_STARTED drag event continue to receive drag
events. The types of drag events a listener receives during the drag depend on the location of
the drag shadow and the visibility of the listener's View.

During the drag, listeners primarily use drag events to decide if they should change the
appearance of their View.

An event with the action type ACTION_DRAG_LOCATION contains
valid data for getX() and
getY(), corresponding to the location of the touch point.
The listener may want to use this information to alter the appearance of that part of the
View that is at the touch point. The listener can also use this information
to determine the exact position where the user is going to drop the drag shadow.

Responding to a drop

When the user releases the drag shadow on a View in the application, and that View previously
reported that it could accept the content being dragged, the system dispatches a drag event
to that View with the action type ACTION_DROP. The listener
should do the following:

Call getClipData() to get the
ClipData object that was originally supplied in the call
to
startDrag()
and store it. If the drag and drop operation does not represent data movement,
this may not be necessary.

Return boolean true to indicate that the drop was processed successfully, or
boolean false if it was not. The returned value becomes the value returned by
getResult() for an
ACTION_DRAG_ENDED event.

For an ACTION_DROP event,
getX() and getY()
return the X and Y position of the drag point at the moment of the drop, using the coordinate
system of the View that received the drop.

The system does allow the user to release the drag shadow on a View whose listener is not
receiving drag events. It will also allow the user to release the drag shadow
on empty regions of the application's UI, or on areas outside of your application.
In all of these cases, the system does not send an event with action type
ACTION_DROP, although it does send out an
ACTION_DRAG_ENDED event.

Responding to a drag end

Immediately after the user releases the drag shadow, the system sends a
drag event to all of the drag event listeners in your application, with an action type of
ACTION_DRAG_ENDED. This indicates that the drag operation is
over.

Each listener should do the following:

If listener changed its View object's appearance during the operation, it should reset the
View to its default appearance. This is a visual indication to the user that the operation
is over.

The listener can optionally call getResult() to find out more
about the operation. If a listener returned true in response to an event of
action type ACTION_DROP, then
getResult() will return boolean true. In all
other cases, getResult() returns boolean false,
including any case in which the system did not send out a
ACTION_DROP event.

The listener should return boolean true to the system.

Responding to drag events: an example

All drag events are initially received by your drag event method or listener. The following
code snippet is a simple example of reacting to drag events in a listener:

The target app must call
requestDragAndDropPermissions()
immediately before handling the data that the user drags into the app. If the
target app no longer needs access to the drag data, the app can then call
release() on the
object that was returned from requestDragAndDropPermissions(). Otherwise, the
permission is released when the containing activity is destroyed.

The following code snippet demonstrates how to release read-only access to drag
data immediately after the drag-and-drop operation takes place. A more complete
example appears within the DragAndDropAcrossApps
sample,
available on GitHub.