Drag-and-drop im Mehrfenstermodus

Geräte mit Android 7.0 (API-Level 24) oder höher unterstützen den Mehrfenstermodus, in dem Nutzer Daten per Drag-and-drop von einer App in eine andere verschieben können.

Die Daten werden von der Quell-App bereitgestellt, in der der Vorgang gestartet wird. Die Ziel-App, an der der Vorgang endet, empfängt die Daten.

Wenn der Nutzer beginnt, Inhalte zu ziehen, sollte in der Quell-App das Flag DRAG_FLAG_GLOBAL festgelegt werden, um anzugeben, dass der Nutzer Daten in eine andere App ziehen kann.

Da die Daten über Anwendungsgrenzen hinweg verschoben werden, haben die Anwendungen über einen Inhalts-URI Zugriff auf die Daten. Hierfür ist Folgendes erforderlich:

  • Die Quellanwendung muss entweder das Flag DRAG_FLAG_GLOBAL_URI_READ oder das Flag DRAG_FLAG_GLOBAL_URI_WRITE oder beide festlegen, je nachdem, welchen Lese- oder Schreibzugriff auf die Daten die Quell-App der Zielanwendung gewähren möchte.
  • Die Ziel-App muss sofort requestDragAndDropPermissions() aufrufen, bevor die Daten verarbeitet werden, die der Nutzer in die App zieht. Wenn die Ziel-App keinen Zugriff mehr auf die gezogenen Daten benötigt, kann die App release() für das Objekt aufrufen, das von requestDragAndDropPermissions() zurückgegeben wurde. Andernfalls werden die Berechtigungen freigegeben, wenn die enthaltende Aktivität gelöscht wird. Wenn Ihre Implementierung eine neue Activity startet, um die verworfenen URIs zu verarbeiten, müssen Sie der neuen Activity die gleichen Berechtigungen erteilen. Sie müssen die Clipdaten und ein Flag festlegen:

    Kotlin

    intent.setClipData(clipData)
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    

    Java

    intent.setClipData(clipData);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

Die folgenden Code-Snippets zeigen, wie der Lesezugriff auf gezogene Daten sofort nach dem Löschen durch den Nutzer freigegeben wird. Ein vollständigeres Beispiel finden Sie auf GitHub im Drag-and-Drop-Beispiel.

Quellaktivität

Kotlin

// 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()
}

Java

// 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();

Zielaktivität

Kotlin

// 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
        }

    }
}

Java

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