В этой статье я хочу рассказать о новой возможности перетаскивать данные между различными Activity в режиме совместного отображения, которая появилась с выходом Android 7 Nougat. Ранее перетаскивать данные можно было только в рамках одной Activity.
Для передачи данных при перетаскивании используются классы ClipData и DragEvent. В приложении, в котором инициализируется операция перетаскивания, данные упаковываются с помощью ClipData. ClipData представляет собой сложный тип, содержащий в себе один или несколько экземпляров передаваемых данных. ClipData используется для размещения данных в буфере обмена. Для перетаскивания доступны следующие типы данных:
В приложении приемнике на соответствующем элементе интерфейса устанавливается слушатель перетаскивания, который принимает события DragEvent.
Давайте рассмотрим пример использования функции перетаскивания. В примере представлены два приложения: инициализатор (DragNDropExample) и приемник перетаскивания (DragNDropReceiver).
Для того, чтобы приложение отображалось в многооконном режиме необходимо установить в AndroidManifest в секции Application или Activity следующий параметр:
В приложении DragNDropExample реализовано перетаскивание изображения и текста. Файл изображения хранится в папке Downloads. При перетаскивании изображения другому процессу передается URI файла изображения. Инициализация перетаскивания происходит по долгому нажатию на соответствующие элементы.
Устанавливаем слушатель долгого нажатия.
Получаем URI файла для передачи другому процессу. В новой версии Android для получения доступа к файлам, размещенным в мультимедийных папках, необходимо использовать FileProvider.
Создаем объект ClipData и упаковываем в него URI. При создании объекта ClipData указывается тип упакованных данных. Тип MIMETYPE_TEXT_URILIST указывает, что упаковываютсяURI.
Инициализируем перетаскивание, передавая объект ClipData, объект dragShadowBuilder и устанавливая соответствующие флаги. Флаг View.DRAG_FLAG_GLOBAL разрешает перетаскивание между операциями. Флаги View.DRAG_FLAG_GLOBAL_URI_READ и View.DRAG_FLAG_GLOBAL_URI_WRITE разрешают соответственно чтение и запись URI. Для создания тени при перетаскивании используется DragShadowBuilder. В данном примере тенью является сам элемент интерфейса:
Аналогично происходит все для перетаскивания текста:
В приложении приемнике устанавливаются слушатели OnDragListener:
В слушателе задаются действия для определенных событий перетаскивания. В данном примере установлены действия для следующих событий:
— ACTION_DRAG_STARTED: перетаскивание начато;
— ACTION_DROP: пользователь отпустил перетаскиваемый элемент.
Для того чтобы вставка изображения производилась только в ImageView, а текста в TextView необходима соответствующая проверка. В данном участке кода проверяется тип перетаскиваемых данных и сравнивается с необходимым.
Для перетаскивания текста все происходит аналогично.
В процессе перетаскивания изображения:
После перетаскивания текста и изображения:
Возможность перетаскивать информацию между различными процессами позволяет ускорить обмен данными между приложениями, сделать его более удобным и наглядным для пользователя.
Описание технологии и разбор примера
Для передачи данных при перетаскивании используются классы ClipData и DragEvent. В приложении, в котором инициализируется операция перетаскивания, данные упаковываются с помощью ClipData. ClipData представляет собой сложный тип, содержащий в себе один или несколько экземпляров передаваемых данных. ClipData используется для размещения данных в буфере обмена. Для перетаскивания доступны следующие типы данных:
— MIMETYPE_TEXT_HTML: необходим для передачи HTML текста;
— MIMETYPE_TEXT_INTENT: необходим для передачи интента;
— MIMETYPE_TEXT_PLAIN: необходим для передачи обычного текста;
— MIMETYPE_TEXT_URILIST: необходим для передачи URL.
В приложении приемнике на соответствующем элементе интерфейса устанавливается слушатель перетаскивания, который принимает события DragEvent.
Давайте рассмотрим пример использования функции перетаскивания. В примере представлены два приложения: инициализатор (DragNDropExample) и приемник перетаскивания (DragNDropReceiver).
Для того, чтобы приложение отображалось в многооконном режиме необходимо установить в AndroidManifest в секции Application или Activity следующий параметр:
<activity
…
android:resizeableActivity="true"
…
</activity>
В приложении DragNDropExample реализовано перетаскивание изображения и текста. Файл изображения хранится в папке Downloads. При перетаскивании изображения другому процессу передается URI файла изображения. Инициализация перетаскивания происходит по долгому нажатию на соответствующие элементы.
Устанавливаем слушатель долгого нажатия.
imageView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
File downloadFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String[] filNames = downloadFolder.list();
Получаем URI файла для передачи другому процессу. В новой версии Android для получения доступа к файлам, размещенным в мультимедийных папках, необходимо использовать FileProvider.
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", new File(downloadFolder, filNames[0]));
Создаем объект ClipData и упаковываем в него URI. При создании объекта ClipData указывается тип упакованных данных. Тип MIMETYPE_TEXT_URILIST указывает, что упаковываютсяURI.
ClipData.Item item = new ClipData.Item(uri);
ClipData clip = new ClipData("text", new String[]{ ClipDescription.MIMETYPE_TEXT_URILIST}, item);
Инициализируем перетаскивание, передавая объект ClipData, объект dragShadowBuilder и устанавливая соответствующие флаги. Флаг View.DRAG_FLAG_GLOBAL разрешает перетаскивание между операциями. Флаги View.DRAG_FLAG_GLOBAL_URI_READ и View.DRAG_FLAG_GLOBAL_URI_WRITE разрешают соответственно чтение и запись URI. Для создания тени при перетаскивании используется DragShadowBuilder. В данном примере тенью является сам элемент интерфейса:
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;
}
});
Аналогично происходит все для перетаскивания текста:
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;
}
});
В приложении приемнике устанавливаются слушатели OnDragListener:
imageView.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
В слушателе задаются действия для определенных событий перетаскивания. В данном примере установлены действия для следующих событий:
— ACTION_DRAG_STARTED: перетаскивание начато;
— ACTION_DROP: пользователь отпустил перетаскиваемый элемент.
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);
Для того чтобы вставка изображения производилась только в ImageView, а текста в TextView необходима соответствующая проверка. В данном участке кода проверяется тип перетаскиваемых данных и сравнивается с необходимым.
if (event.getClipDescription().getMimeType(0).equals(MIMETYPE_TEXT_URILIST)) {
Uri uri = item.getUri();
imageView.setImageURI(uri);
}
}
return true;
}
});
Для перетаскивания текста все происходит аналогично.
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;
}
});
Ниже приведены скриншоты экрана в процессе перетаскивания
В процессе перетаскивания изображения:
После перетаскивания текста и изображения:
Заключение
Возможность перетаскивать информацию между различными процессами позволяет ускорить обмен данными между приложениями, сделать его более удобным и наглядным для пользователя.
Ссылки
- 1. Ссылка на официальную документацию Google по Drad-n-Drop
- 2. Ссылка на официальную документацию Google о Multi-Window Support
Поделиться с друзьями
Комментарии (6)
petrovichtim
03.10.2016 09:31Круто, так же можно и файлы перетаскивать, например текстовые в редактор текста.
VYakushev
03.10.2016 19:47+1Не понял, как можно в приложении источнике узнать результат перетаскивания. К примеру, если мы получили
DragEvent.ACTION_DRAG_ENDED
, то в приемнике результат показать а в источнике перетаскиваемый объект скрыть.Conacry
03.10.2016 21:46Можно через установку OnDragListener в приложении источнике. Событие DragEvent происходит глобально. Единственное, для корректной отработки удаления, необходимо идентифицировать объект перетаскивания.
JEAN17RUS
Спасибо за статью!
Conacry
Пожалуйста!