Key concepts

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 all View objects in the current layout. To continue to receive drag events—including a possible drop event—the 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 drop target View 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 type ACTION_DRAG_ENDED. By returning false, 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 type ACTION_DRAG_ENTERED —the listener can react by highlighting the View.
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 to startDragAndDrop() that starts the operation. The listener is expected to return boolean true 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 a View whose listener is registered to receive drag events (a drop target). 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 a drag event with action type ACTION_DROP, if necessary, the system sends a drag event with action type ACTION_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 the ACTION_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:

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 true if it successfully processes the drop. Otherwise, it must return false.

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 View objects. The constructor stores the View object in the View.DragShadowBuilder object, so the callbacks can access it to construct the drag shadow. The view doesn't have to be a View 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 the View 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 the View.DragShadowBuilder object. The field is set to null. You must extend View.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: a Point object. The drag shadow width goes in x, and its height goes in y.

outShadowTouchPoint: a Point 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 in x and its Y position goes in y.

onDrawShadow()

Immediately after the call to onProvideShadowMetrics() the system calls onDrawShadow() to create the drag shadow. The method has a single argument, a Canvas object that the system constructs from the parameters you provide in onProvideShadowMetrics(). The method draws the drag shadow on the provided Canvas.

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 a ContentInfo object. The method invokes the OnReceiveContentListener.

  • 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.