Przeciąganie i upuszczanie w trybie wielu okien

Urządzenia z Androidem 7.0 (poziom interfejsu API 24) lub nowszym obsługują tryb wielu okien, który umożliwia użytkownikom przenoszenie danych między aplikacjami przez przeciąganie i upuszczanie.

Dane dostarcza aplikacja źródłowa, w której rozpoczyna się operacja. Aplikacja docelowa, do której operacja się kończy, otrzymuje dane.

Gdy użytkownik zacznie przeciągać treści, aplikacja źródłowa powinna ustawić flagę DRAG_FLAG_GLOBAL, aby wskazać, że użytkownik może przeciągać dane do innej aplikacji.

Dane są przesyłane poza granice aplikacji, dlatego aplikacje mają do nich dostęp za pomocą identyfikatora URI treści. Wymagania:

  • Aplikacja źródłowa musi ustawić jedną lub obie flagi DRAG_FLAG_GLOBAL_URI_READ i DRAG_FLAG_GLOBAL_URI_WRITE w zależności od uprawnień do odczytu lub zapisu danych, które aplikacja źródłowa chce przyznać aplikacji docelowej.
  • Aplikacja docelowa musi wywołać metodę requestDragAndDropPermissions() bezpośrednio przed obsługą danych przeciąganych przez użytkownika do aplikacji. Jeśli aplikacja docelowa nie potrzebuje już dostępu do przeciągniętych danych, może wywołać metodę release() na obiekcie zwróconym z metody requestDragAndDropPermissions(). W przeciwnym razie uprawnienia zostaną anulowane po zniszczeniu aktywności, która zawiera. Jeśli Twoja implementacja obejmuje uruchomienie nowego działania w celu przetworzenia utraconych identyfikatorów URI, musisz przyznać nowemu działaniu te same uprawnienia. Musisz ustawić dane dotyczące klipu i flagę:

    Kotlin

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

    Java

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

Podane niżej fragmenty kodu pokazują, jak przyznać dostęp tylko do odczytu do przeciągniętych danych bezpośrednio po przeprowadzeniu operacji usuwania danych użytkownika. Pełniejszy przykład znajdziesz w tym przykładzie na GitHubie.

Aktywność źródła

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

Docelowa aktywność

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