codete Android Nougat Multi window and Drag n Drop Receiving and Consuming the Event 1 main 733c4b730b
Codete Blog

Android Nougat: Multi-window and Drag'n'Drop – Receiving and Consuming the Event

avatar male f667854eaa

27/02/2018 |

6 min read

Hubert Kosacki

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.

Fig. 1. The main screen of the app

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.

  1. 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.
  2. The next step is to take the information from the ClipData.Item that is being stored within. We can get this object using getItemAt(index) method.
  3. 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:

Fig. 4. App’s behavior when dragging image files
Fig. 5. App’s behavior when dragging text snippet

Links:

https://developer.android.com/guide/topics/ui/drag-drop.html 

https://developer.android.com/reference/android/view/DragEvent.html

Rated: 5.0 / 1 opinions
avatar male f667854eaa

Hubert Kosacki

Android fanatic. Problem solver. Mobile technologies enthusiast.

Our mission is to accelerate your growth through technology

Contact us

Codete Global
Spółka z ograniczoną odpowiedzialnością

Na Zjeździe 11
30-527 Kraków

NIP (VAT-ID): PL6762460401
REGON: 122745429
KRS: 0000983688

Get in Touch
  • icon facebook
  • icon linkedin
  • icon instagram
  • icon youtube
Offices
  • Kraków

    Na Zjeździe 11
    30-527 Kraków
    Poland

  • Lublin

    Wojciechowska 7E
    20-704 Lublin
    Poland

  • Berlin

    Bouchéstraße 12
    12435 Berlin
    Germany