Устройства под управлением Android 7.0 (уровень API 24) или выше поддерживают многооконный режим , который позволяет пользователям перемещать данные из одного приложения в другое путем перетаскивания.
Исходное приложение , в котором начинается операция, предоставляет данные. Целевое приложение , где заканчивается операция, получает данные.
Когда пользователь начинает перетаскивать контент, исходное приложение должно установить флаг DRAG_FLAG_GLOBAL
чтобы указать, что пользователь может перетаскивать данные в другое приложение.
Поскольку данные перемещаются за пределы приложений, приложения разделяют доступ к данным с помощью URI контента. Для этого необходимо следующее:
- Исходное приложение должно установить один или оба флага
DRAG_FLAG_GLOBAL_URI_READ
иDRAG_FLAG_GLOBAL_URI_WRITE
, в зависимости от доступа на чтение или запись к данным, которые исходное приложение хочет предоставить целевому приложению. - Целевое приложение должно вызвать
requestDragAndDropPermissions()
непосредственно перед обработкой данных, которые пользователь перетаскивает в приложение. Если целевому приложению больше не требуется доступ к перетаскиваемым данным, оно может затем вызватьrelease()
для объекта, который был возвращен изrequestDragAndDropPermissions()
. В противном случае разрешения освобождаются при уничтожении содержащего действия. Если ваша реализация предполагает запуск нового действия для обработки удаленных URI, вам потребуется предоставить новому действию те же разрешения. Вы должны установить данные клипа и флаг:Котлин
intent.setClipData(clipData) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
Ява
intent.setClipData(clipData); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Следующие фрагменты кода демонстрируют, как освободить доступ только для чтения к перетаскиваемым данным сразу после выполнения операции удаления пользователя. Более полный пример см. в демо-версии перетаскивания на GitHub.
Исходная активность
Котлин
// Drag a file stored in an images/ directory in internal storage. val internalImagesDir = File(context.filesDir, "images") val imageFile = File(internalImagesDir, imageFilename) val uri = FileProvider.getUriForFile(context, contentAuthority, imageFile) val listener = OnDragStartListener@{ view: View, _: DragStartHelper -> val clipData = ClipData(ClipDescription("Image Description", arrayOf("image/*")), ClipData.Item(uri)) // Must include DRAG_FLAG_GLOBAL to permit dragging data between apps. // This example provides read-only access to the data. val flags = View.DRAG_FLAG_GLOBAL or View.DRAG_FLAG_GLOBAL_URI_READ return@OnDragStartListener view.startDragAndDrop(clipData, View.DragShadowBuilder(view), null, flags) } // Container where the image originally appears in the source app. val srcImageView = findViewById<ImageView>(R.id.imageView) // Detect and start the drag event. DragStartHelper(srcImageView, listener).apply { attach() }
Ява
// Drag a file stored in an images/ directory in internal storage. File internalImagesDir = new File(context.getFilesDir(), "images"); File imageFile = new File(internalImagesDir, imageFilename); final Uri uri = FileProvider.getUriForFile(context, contentAuthority, imageFile); // Container where the image originally appears in the source app. ImageView srcImageView = findViewById(R.id.imageView); // Enable the view to detect and start the drag event. new DragStartHelper(srcImageView, (view, helper) -> { ClipData clipData = new ClipData(new ClipDescription("Image Description", new String[] {"image/*"}), new ClipData.Item(uri)); // Must include DRAG_FLAG_GLOBAL to permit dragging data between apps. // This example provides read-only access to the data. int flags = View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ; return view.startDragAndDrop(clipData, new View.DragShadowBuilder(view), null, flags); }).attach();
Целевая активность
Котлин
// Container where the image is to be dropped in the target app. val targetImageView = findViewById<ImageView>(R.id.imageView) targetImageView.setOnDragListener { view, event -> when (event.action) { ACTION_DROP -> { val imageItem: ClipData.Item = event.clipData.getItemAt(0) val uri = imageItem.uri // Request permission to access the image data being dragged into // the target activity's ImageView element. val dropPermissions = requestDragAndDropPermissions(event) (view as ImageView).setImageURI(uri) // Release the permission immediately afterward because it's no // longer needed. dropPermissions.release() return@setOnDragListener true } // Implement logic for other DragEvent cases here. // An unknown action type is received. else -> { Log.e("DragDrop Example", "Unknown action type received by View.OnDragListener.") return@setOnDragListener false } } }
Ява
// Container where the image is to be dropped in the target app. ImageView targetImageView = findViewById(R.id.imageView); targetImageView.setOnDragListener( (view, event) -> { switch (event.getAction()) { case ACTION_DROP: ClipData.Item imageItem = event.getClipData().getItemAt(0); Uri uri = imageItem.getUri(); // Request permission to access the image data being dragged into // the target activity's ImageView element. DragAndDropPermissions dropPermissions = requestDragAndDropPermissions(event); ((ImageView)view).setImageURI(uri); // Release the permission immediately afterward because it's no // longer needed. dropPermissions.release(); return true; // Implement logic for other DragEvent cases here. // An unknown action type was received. default: Log.e("DragDrop Example","Unknown action type received by View.OnDragListener."); break; } return false; });