السحب والإفلات في وضع النوافذ المتعددة

الأجهزة التي تعمل بنظام التشغيل Android 7.0 (المستوى 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;
});