Kéo và thả ở chế độ nhiều cửa sổ
Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Các thiết bị chạy Android 7.0 (API cấp 24) trở lên có hỗ trợ chế độ nhiều cửa sổ
, tính năng này cho phép người dùng
di chuyển dữ liệu từ ứng dụng này sang ứng dụng khác bằng cách kéo và thả.
Ứng dụng nguồn, nơi bắt đầu thao tác, sẽ cung cấp dữ liệu.
Ứng dụng mục tiêu, khi thao tác kết thúc sẽ nhận được dữ liệu.
Khi người dùng bắt đầu kéo nội dung, ứng dụng nguồn sẽ đặt
DRAG_FLAG_GLOBAL
gắn cờ thành
cho biết rằng người dùng có thể kéo dữ liệu sang một ứng dụng khác.
Vì dữ liệu di chuyển qua ranh giới ứng dụng, nên các ứng dụng sẽ chia sẻ quyền truy cập vào dữ liệu
bằng cách sử dụng URI nội dung. Điều này yêu cầu:
- Ứng dụng nguồn phải đặt một hoặc cả hai thuộc tính
DRAG_FLAG_GLOBAL_URI_READ
và
DRAG_FLAG_GLOBAL_URI_WRITE
cờ, tuỳ thuộc vào quyền đọc hoặc ghi vào dữ liệu mà ứng dụng nguồn
muốn cấp cho ứng dụng mục tiêu.
- Ứng dụng đích phải gọi
requestDragAndDropPermissions()
ngay trước khi xử lý dữ liệu mà người dùng kéo vào ứng dụng. Nếu
ứng dụng đích không còn cần quyền truy cập vào dữ liệu được kéo nữa, ứng dụng có thể
sau đó gọi
release()
bật
đối tượng được trả về qua requestDragAndDropPermissions()
.
Nếu không, các quyền truy cập sẽ được huỷ khi hoạt động (activity) chứa bị huỷ bỏ.
Nếu quá trình triển khai của bạn bao gồm cả việc bắt đầu một Hoạt động mới để xử lý
URI bị thả, bạn sẽ cần cấp các quyền tương tự cho Hoạt động mới.
Bạn phải đặt dữ liệu đoạn video và cờ:
Kotlin
intent.setClipData(clipData)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
Java
intent.setClipData(clipData);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Các đoạn mã sau đây minh hoạ cách cấp quyền chỉ có thể đọc cho
dữ liệu đã kéo ngay sau khi thao tác thả người dùng diễn ra. Xem
Bản minh hoạ kéo và thả
trên GitHub để biết ví dụ hoàn chỉnh hơn.
Hoạt động nguồn
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();
Hoạt động mục tiêu
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;
});
Nội dung và mã mẫu trên trang này phải tuân thủ các giấy phép như mô tả trong phần Giấy phép nội dung. Java và OpenJDK là nhãn hiệu hoặc nhãn hiệu đã đăng ký của Oracle và/hoặc đơn vị liên kết của Oracle.
Cập nhật lần gần đây nhất: 2025-07-27 UTC.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-07-27 UTC."],[],[],null,["# Drag and drop in multi-window mode\n\nDevices that run Android 7.0 (API level 24) or higher support [multi-window\nmode](/develop/ui/views/layout/support-multi-window-mode), which lets users\nmove data from one app to another by dragging and dropping.\n\nThe *source app* , where the operation starts, provides the data.\nThe *target app*, where the operation ends, receives the data.\n\nWhen the user starts to drag content, the source app should set the\n[`DRAG_FLAG_GLOBAL`](/reference/android/view/View#DRAG_FLAG_GLOBAL) flag to\nindicate that the user can drag data to another app.\n\nBecause the data moves across app boundaries, the apps share access to the data\nusing a content URI. This requires the following:\n\n- The source app must set either or both of the [`DRAG_FLAG_GLOBAL_URI_READ`](/reference/android/view/View#DRAG_FLAG_GLOBAL_URI_READ) and [`DRAG_FLAG_GLOBAL_URI_WRITE`](/reference/android/view/View#DRAG_FLAG_GLOBAL_URI_WRITE) flags, depending on the read or write access to the data that the source app wants to grant to the target app.\n- The target app must call [`requestDragAndDropPermissions()`](/reference/android/app/Activity#requestDragAndDropPermissions(android.view.DragEvent)) immediately before handling the data that the user drags into the app. If the target app no longer needs access to the dragged data, the app can then call [`release()`](/reference/android/view/DragAndDropPermissions#release()) on the object that was returned from `requestDragAndDropPermissions()`. Otherwise, the permissions are released when the containing activity is destroyed. If your implementation involves starting a new Activity to process the dropped URIs, you will need to grant the new Activity the same permissions. You must set the clip data and a flag: \n\n ### Kotlin\n\n ```kotlin\n intent.setClipData(clipData)\n intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n ```\n\n ### Java\n\n ```java\n intent.setClipData(clipData);\n intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n ```\n\nThe following code snippets demonstrate how to release read-only access to\ndragged data immediately after the user drop operation takes place. See the\n[Drag and Drop Demo](https://github.com/android/user-interface-samples/tree/main/DragAndDrop)\non GitHub for a more complete example.\n\nSource activity\n---------------\n\n### Kotlin\n\n```kotlin\n// Drag a file stored in an images/ directory in internal storage.\nval internalImagesDir = File(context.filesDir, \"images\")\nval imageFile = File(internalImagesDir, imageFilename)\nval uri = FileProvider.getUriForFile(context, contentAuthority, imageFile)\n\nval listener = OnDragStartListener@{ view: View, _: DragStartHelper -\u003e\n val clipData = ClipData(ClipDescription(\"Image Description\",\n arrayOf(\"image/*\")),\n ClipData.Item(uri))\n // Must include DRAG_FLAG_GLOBAL to permit dragging data between apps.\n // This example provides read-only access to the data.\n val flags = View.DRAG_FLAG_GLOBAL or View.DRAG_FLAG_GLOBAL_URI_READ\n return@OnDragStartListener view.startDragAndDrop(clipData,\n View.DragShadowBuilder(view),\n null,\n flags)\n}\n\n// Container where the image originally appears in the source app.\nval srcImageView = findViewById\u003cImageView\u003e(R.id.imageView)\n\n// Detect and start the drag event.\nDragStartHelper(srcImageView, listener).apply {\n attach()\n}\n```\n\n### Java\n\n```java\n// Drag a file stored in an images/ directory in internal storage.\nFile internalImagesDir = new File(context.getFilesDir(), \"images\");\nFile imageFile = new File(internalImagesDir, imageFilename);\nfinal Uri uri = FileProvider.getUriForFile(context, contentAuthority, imageFile);\n\n// Container where the image originally appears in the source app.\nImageView srcImageView = findViewById(R.id.imageView);\n\n// Enable the view to detect and start the drag event.\nnew DragStartHelper(srcImageView, (view, helper) -\u003e {\n ClipData clipData = new ClipData(new ClipDescription(\"Image Description\",\n new String[] {\"image/*\"}),\n new ClipData.Item(uri));\n // Must include DRAG_FLAG_GLOBAL to permit dragging data between apps.\n // This example provides read-only access to the data.\n int flags = View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ;\n return view.startDragAndDrop(clipData,\n new View.DragShadowBuilder(view),\n null,\n flags);\n}).attach();\n```\n\nTarget activity\n---------------\n\n### Kotlin\n\n```kotlin\n// Container where the image is to be dropped in the target app.\nval targetImageView = findViewById\u003cImageView\u003e(R.id.imageView)\n\ntargetImageView.setOnDragListener { view, event -\u003e\n\n when (event.action) {\n\n ACTION_DROP -\u003e {\n val imageItem: ClipData.Item = event.clipData.getItemAt(0)\n val uri = imageItem.uri\n\n // Request permission to access the image data being dragged into\n // the target activity's ImageView element.\n val dropPermissions = requestDragAndDropPermissions(event)\n (view as ImageView).setImageURI(uri)\n\n // Release the permission immediately afterward because it's no\n // longer needed.\n dropPermissions.release()\n return@setOnDragListener true\n }\n\n // Implement logic for other DragEvent cases here.\n\n // An unknown action type is received.\n else -\u003e {\n Log.e(\"DragDrop Example\", \"Unknown action type received by View.OnDragListener.\")\n return@setOnDragListener false\n }\n\n }\n}\n```\n\n### Java\n\n```java\n// Container where the image is to be dropped in the target app.\nImageView targetImageView = findViewById(R.id.imageView);\n\ntargetImageView.setOnDragListener( (view, event) -\u003e {\n\n switch (event.getAction()) {\n\n case ACTION_DROP:\n ClipData.Item imageItem = event.getClipData().getItemAt(0);\n Uri uri = imageItem.getUri();\n\n // Request permission to access the image data being dragged into\n // the target activity's ImageView element.\n DragAndDropPermissions dropPermissions =\n requestDragAndDropPermissions(event);\n\n ((ImageView)view).setImageURI(uri);\n\n // Release the permission immediately afterward because it's no\n // longer needed.\n dropPermissions.release();\n\n return true;\n\n // Implement logic for other DragEvent cases here.\n\n // An unknown action type was received.\n default:\n Log.e(\"DragDrop Example\",\"Unknown action type received by View.OnDragListener.\");\n break;\n }\n\n return false;\n});\n```"]]