Использование Drag-and-drop между различными Activity в Android 7 Nougat

в 15:01, , рубрики: android development, android nougat, Разработка под android

В этой статье я хочу рассказать о новой возможности перетаскивать данные между различными Activity в режиме совместного отображения, которая появилась с выходом Android 7 Nougat. Ранее перетаскивать данные можно было только в рамках одной Activity.

Описание технологии и разбор примера

Для передачи данных при перетаскивании используются классы 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 файла изображения. Инициализация перетаскивания происходит по долгому нажатию на соответствующие элементы.

image

Устанавливаем слушатель долгого нажатия.

        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;
            }
        });

Ниже приведены скриншоты экрана в процессе перетаскивания

В процессе перетаскивания изображения:

image

После перетаскивания текста и изображения:

image

Заключение

Возможность перетаскивать информацию между различными процессами позволяет ускорить обмен данными между приложениями, сделать его более удобным и наглядным для пользователя.

Ссылки

  • 1. Ссылка на официальную документацию Google по Drad-n-Drop
  • 2. Ссылка на официальную документацию Google о Multi-Window Support

Автор: Conacry

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js