In my previous post, I described how to add the possibility to drag some stuff out of your own app. Alright, but what if I want to be on the other end and want to receive some stuff? This guide will cover such cases.
In order to be able to receive a drag event from the other app, well… you need to have the app that allows you to do it. Please have a look at the sample app I developed to demonstrate how to do this.
The main part
How does the mechanism for dragging operation work? You should be familiar after having a closer look at my previous post. Let’s recall this now quickly. When an operation starts, a bunch of callbacks is being given every time a DragEvent happens:
@Override
public boolean onDrag(View view, DragEvent dragEvent) {
switch (dragEvent.getAction()) {
case DragEvent.ACTION_DRAG_ENDED:
Log.d("event", "ACTION_DRAG_ENDED");
return true;
case DragEvent.ACTION_DRAG_ENTERED:
Log.d("event", "ACTION_DRAG_ENTERED");
return true;
case DragEvent.ACTION_DRAG_EXITED:
Log.d("event", "ACTION_DRAG_EXITED");
return true;
case DragEvent.ACTION_DRAG_LOCATION:
Log.d("event", "ACTION_DRAG_LOCATION");
return true;
case DragEvent.ACTION_DRAG_STARTED:
Log.d("event", "ACTION_DRAG_STARTED");
return true;
case DragEvent.ACTION_DROP:
Log.d("event", "ACTION_DRAG_DROP");
handleDropEvent(view, dragEvent);
return true;
default:
Log.e("event", "unknown");
}
return false;
}
Listing: https://gist.github.com/hkosacki/499ac71df180fb33a8928a9aa6b38b3d
If you attach an implementation of View.OnDragListener to a particular view, it will start receiving all these callbacks.
Tip: Remember to return the true
value after you have consumed the event. Otherwise, your listener won’t receive any other new ones.
Define the drop area
Before you start, you need to define a single view that should handle dropping the items. It would be good if the area is quite wide and easy to access. For example, like the one in figure no. 1.
For these purposes, I used a FrameLayout
that contains a TextView with the message shown above. Additionally, I have defined a custom background drawable made of the dashed border in blue (let’s give a hint to the user, that it’s fine to cross the line when dragging some data).
Once you have implemented the onDrag
(View view
, DragEvent dragEvent
) method from the View.OnDragListener
interface, you are ready to go with attaching it to the corresponding view: mainLayout.setOnDragListener(listener)
. Perfect! Now we are ready to add some fireworks to the UI.
Fig. 2. The demo of the Drag Receiver app with image files in action
Add some UI tweaks
Now we need to define what kind of UI reactions we want to have when a drag operation is being performed. Let’s go one by one:
ACTION_DRAG_STARTED
- this one is triggered when a user starts a drag operation. This moment is perfect to show the target placeholder for dragging the stuff.ACTION_DRAG_ENTERED
- this is called when a user hovers the target view with a currently dragged item. In this case, we can change the UI to mark that this is the proper place to end the operation.ACTION_DRAG_LOCATION
- this is sent to the OnDragListener when a shadow of the item is moving within the borders of the view that this listener is attached to. We don’t want to change anything in this case in our app.ACTION_DRAG_EXITED
- a shadow of dragged item has exited the expected area of dropping. In this case, we may want to restore the default placeholder for dragging the stuff.ACTION_DROP
- a drag operation has just ended within the borders of the view that has the attached listener. We should consume the dropped item here.ACTION_DRAG_ENDED
- this is the final callback after the drag operation. We may want to clean up the UI to the state it was before starting the whole action.
Luckily, all of these can be simply done within the switch statement (see the listing below). I’m handling here visibility and the background of the target drop-placeholder, according to each single callback case respectively. Only the case for the ACTION_DROP
contains additional action for consuming received data.
@Override
public boolean onDrag(View view, DragEvent dragEvent) {
Log.d("dragEvent", dragEvent.toString());
switch (dragEvent.getAction()) {
case DragEvent.ACTION_DRAG_ENDED:
Log.d("event", "ACTION_DRAG_ENDED");
dragLayout.setVisibility(View.GONE);
dragLayout.setBackgroundResource(R.drawable.drag_background);
return true;
case DragEvent.ACTION_DRAG_ENTERED:
Log.d("event", "ACTION_DRAG_ENTERED");
dragLayout.setBackgroundResource(R.drawable.drag_background_active);
return true;
case DragEvent.ACTION_DRAG_EXITED:
Log.d("event", "ACTION_DRAG_EXITED");
dragLayout.setBackgroundResource(R.drawable.drag_background);
return true;
case DragEvent.ACTION_DRAG_LOCATION:
Log.d("event", "ACTION_DRAG_LOCATION");
return true;
case DragEvent.ACTION_DRAG_STARTED:
Log.d("event", "ACTION_DRAG_STARTED");
dragLayout.setVisibility(View.VISIBLE);
return true;
case DragEvent.ACTION_DROP:
Log.d("event", "ACTION_DRAG_DROP");
handleDropEvent(view, dragEvent);
return true;
default:
Log.e("event", "unknown");
}
return false;
}
Listing: https://gist.github.com/hkosacki/f743c71e70e6ca5d7aab575758981c47
Handling the drop event
Once we are able to determine if an item has been dropped over our expected area, we need to handle it properly. Similar to the way the data has been packed within the Drag’n’Drop Explorer app, we need to unpack it.
- The drag event is strongly connected to the ClipData object from which all information can be easily taken. This one can be accessed using the getClipData() method on the DragEvent object.
- The next step is to take the information from the
ClipData.Item
that is being stored within. We can get this object usinggetItemAt(index)
method. - Now the game begins. We need to determine what kind of data is being stored within the
ClipData.Item
object. The most proper way seems to be a waterfall, in which we check all properties one by one. Let’s consider the following items:- Text snippets,
- Files.
All other contents won’t be supported.
Fig. 3. This is how dragging text snippets work
For the text snippets, we’ll create a TextView that contains the dragged message. We will also support the image files - we will read them (yes, granting read permissions for the storage would be required) and display them within the same activity. For the other known file types, that are supported by some other apps, we’ll expose an Intent to handle them (the same way as it was in the Drag’n’Drop Explorer app). For the other files - we will display a message telling us that the selected file is unfortunately unsupported. The listing can be seen here.
Listing: https://gist.github.com/hkosacki/176cc27d50e5c75e7d4bdd70cb70f95e
The final experience
Complete app sources can be found on my GitHub: https://github.com/hkosacki/discover-android-n/tree/master/drag-n-drop. After combining all these simple steps, we can enjoy our app in the following ways:
Links:
https://developer.android.com/guide/topics/ui/drag-drop.html
https://developer.android.com/reference/android/view/DragEvent.html