גרירה ושחרור במצב ריבוי חלונות

מכשירים עם Android 7.0 (רמת API 24) ואילך תומכים בריבוי חלונות , שמאפשר למשתמשים העברת נתונים מאפליקציה אחת לאחרת באמצעות גרירה ושחרור.

אפליקציית המקור, שבה הפעולה מתחילה, מספקת את הנתונים. אפליקציית היעד, שבה הפעולה מסתיימת, מקבלת את הנתונים.

כשהמשתמש מתחיל לגרור תוכן, אפליקציית המקור צריכה להגדיר את סימון DRAG_FLAG_GLOBAL ל- לציין שהמשתמש יכול לגרור נתונים לאפליקציה אחרת.

מכיוון שהנתונים עוברים בין גבולות האפליקציות, האפליקציות חולקות את הגישה אליהם באמצעות URI של תוכן. לשם כך:

  • אפליקציית המקור חייבת להגדיר אחת מהאפשרויות האלה או את שתיהן DRAG_FLAG_GLOBAL_URI_READ וגם DRAG_FLAG_GLOBAL_URI_WRITE בהתאם לגישת הקריאה או הכתיבה לנתונים שאפליקציית המקור רוצה לתת לאפליקציה היעד.
  • אפליקציית היעד חייבת להתקשר requestDragAndDropPermissions() ממש לפני הטיפול בנתונים שהמשתמש גורר לאפליקציה. אם המיקום אפליקציית היעד כבר לא צריכה גישה לנתונים שגוררים, האפליקציה יכולה ואז להתקשר release() פועלים של האובייקט שהוחזר מ-requestDragAndDropPermissions(). אחרת, ההרשאות משוחררות כשהפעילות שמכילה מתבצעת הושמד. אם ההטמעה כוללת התחלת פעילות חדשה לצורך עיבוד עליך להעניק לפעילות החדשה את אותן ההרשאות. עליכם להגדיר את נתוני הקליפ ודגל:

    Kotlin

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

    Java

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

קטעי הקוד הבאים מדגימים איך לשחרר הרשאת קריאה בלבד אל נתונים שגוררים מיד אחרי שהפעולה של שחרור המשתמש מתבצעת. לצפייה הדגמה של גרירה ושחרור כדי לקבל דוגמה מלאה יותר, אפשר לראות דוגמה מקיפה יותר ב-GitHub.

פעילות מקור

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

פעילות היעד

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