In this article I want to talk about the new ability to drag and drop data between different Activities in the joint display mode, which appeared with the release of Android 7 Nougat. Previously, data could only be dragged within one Activity.
Description of technology and analysis of the example
For data transfer while dragging, the ClipData and DragEvent classes are used. In an application that initiates a drag and drop operation, the data is packed using ClipData. ClipData is a complex type that contains one or more copies of the transmitted data. ClipData is used to place data on the clipboard. The following data types are available for dragging:
- MIMETYPE_TEXT_HTML: required to transfer HTML text;
- MIMETYPE_TEXT_INTENT: required for transferring the intent;
- MIMETYPE_TEXT_PLAIN: required for the transmission of plain text;
- MIMETYPE_TEXT_URILIST: required for submitting a URL.
In the receiver application, a drag listener is installed on the corresponding interface element, which accepts DragEvent events.
Let's look at an example of using the drag and drop function. The example presents two applications: an initializer (DragNDropExample) and a drag receiver (DragNDropReceiver).
')
In order for an application to be displayed in multi-window mode, you need to set the following parameter in AndroidManifest in the Application or Activity section:
<activity … android:resizeableActivity="true" … </activity>
DragNDropExample application implements dragging images and text. The image file is stored in the Downloads folder. When you drag an image to another process, the URI of the image file is transferred. Initialization of dragging occurs by long pressing on the corresponding elements.
Install the listener long press.
imageView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { File downloadFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); String[] filNames = downloadFolder.list();
We get the file URI to pass to another process. In the new version of Android, you need to use FileProvider to access files placed in multimedia folders.
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", new File(downloadFolder, filNames[0]));
Create a ClipData object and package the URI into it. When creating a ClipData object, the type of packed data is indicated. The type MIMETYPE_TEXT_URILIST indicates that they are packaged.
ClipData.Item item = new ClipData.Item(uri); ClipData clip = new ClipData("text", new String[]{ ClipDescription.MIMETYPE_TEXT_URILIST}, item);
Initialize the drag by passing the ClipData object, the dragShadowBuilder object and setting the appropriate flags. Flag View.DRAG_FLAG_GLOBAL allows dragging between operations. The View.DRAG_FLAG_GLOBAL_URI_READ and View.DRAG_FLAG_GLOBAL_URI_WRITE flags allow reading and writing of URIs respectively. DragShadowBuilder is used to create the shadow while dragging. In this example, the shadow is the interface element itself:
View.DragShadowBuilder dragShadowBuilder = new View.DragShadowBuilder(view); imageView.startDragAndDrop(clip, dragShadowBuilder, null, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ | View.DRAG_FLAG_GLOBAL_URI_WRITE); return true; } });
Everything is similar for dragging text:
textView = (TextView) findViewById(R.id.text_view); textView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { ClipData.Item item = new ClipData.Item(textView.getText()); ClipData clip = new ClipData("text", new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN}, item); View.DragShadowBuilder dragShadowBuilder = new View.DragShadowBuilder(view); textView.startDragAndDrop(clip, dragShadowBuilder, null, View.DRAG_FLAG_GLOBAL); return true; } });
The OnDragListener listeners are installed in the receiver application:
imageView.setOnDragListener(new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) {
The listener defines actions for specific drag and drop events. In this example, actions are set for the following events:
- ACTION_DRAG_STARTED: dragging started;
- ACTION_DROP: the user has released the dragged item.
switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: Toast toast = Toast.makeText(getApplicationContext(), R.string.drop_started, Toast.LENGTH_SHORT); toast.show(); imageView.setBackgroundResource(R.color.grey_200); break; case DragEvent.ACTION_DROP: requestDragAndDropPermissions(event); ClipData.Item item = event.getClipData().getItemAt(0);
In order to insert an image made only in the ImageView, and the text in the TextView requires the appropriate verification. In this section of the code, the type of the dragged data is checked and compared with the necessary one.
if (event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_URILIST)) { Uri uri = item.getUri(); imageView.setImageURI(uri); } } return true; } });
To drag and drop text, everything happens in a similar way.
textView.setOnDragListener(new View.OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: Toast toast = Toast.makeText(getApplicationContext(), R.string.drop_started, Toast.LENGTH_SHORT); toast.show(); break; case DragEvent.ACTION_DROP: requestDragAndDropPermissions(event); ClipData.Item item = event.getClipData().getItemAt(0); if (event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_PLAIN)) { CharSequence text = item.getText(); textView.setText(text); } } return true; } });
Below are screenshots of the screen while dragging.
In the process of dragging an image:
After dragging text and image:
Conclusion
The ability to drag and drop information between different processes allows you to speed up the exchange of data between applications, to make it more convenient and intuitive for the user.
Links