Drag-and-Drop — một cử chỉ tương tác để kéo một đối tượng trực quan trên màn hình bằng ngón tay. Cử chỉ này bao gồm ba giai đoạn: nắm (nhấn giữ hoặc chạm), di chuyển (di chuyển ngón tay cùng đối tượng) và thả (thả vào vùng mục tiêu). Nó được sử dụng để sắp xếp lại các phần tử, di chuyển tập tin giữa các thư mục và tổ chức giao diện (drag to reorder, drag to resize). Trên iOS, nó được thực hiện qua UIDragInteraction, trên Android — qua DragEvent và startDragAndDrop.
Ý chính
Drag-and-Drop (DnD) là một phương pháp thao tác các phần tử giao diện đồ họa, trong đó người dùng nắm một đối tượng bằng ngón tay hoặc chuột, di chuyển nó đến một vị trí khác và thả ra. Trong giao diện di động, Drag-and-Drop thường được khởi tạo bằng thao tác nhấn giữ — người dùng giữ phần tử trong 300–500 ms, sau đó hệ thống chuyển giao diện sang chế độ kéo và chuyển động của ngón tay di chuyển đối tượng đi theo.
Về lịch sử, Drag-and-Drop xuất hiện trong giao diện máy tính để bàn (Xerox Star, 1981; Macintosh, 1984) như một sự thay thế cho các lệnh sao chép/dán. Trên các nền tảng di động, hỗ trợ đầy đủ xuất hiện trong iOS 11 (2017) với UIDragInteraction và trong Android 7.0 (2016) với Multi-Window Drag-and-Drop. Trước đó, việc kéo trong các ứng dụng di động được thực hiện bằng các giải pháp tùy chỉnh thông qua bộ nhận dạng cử chỉ.
Các khái niệm chính: phiên kéo (drag session), mục kéo (drag items), xem trước kéo (drag preview — biểu diễn trực quan của đối tượng), vùng thả (drop target), đề xuất thả (drop proposal — hành động khi thả: di chuyển, sao chép, liên kết). Hệ thống quản lý phiên ở cấp hệ điều hành, cho phép kéo các đối tượng giữa các ứng dụng khác nhau (ví dụ: ảnh từ Thư viện ảnh đến Ghi chú).
Drag-and-Drop được chia thành ba giai đoạn tuần tự. Giai đoạn 1 — Nắm (Bắt đầu kéo): người dùng khởi tạo cử chỉ (thường là nhấn giữ), hệ thống tạo một phiên kéo, thu thập dữ liệu của đối tượng và hiển thị bản xem trước kéo — một bản sao bán trong suốt của phần tử theo ngón tay. Trong giai đoạn này, hệ thống từ chối các cử chỉ khác (cuộn, chọn văn bản) cho đến khi hoàn tất DnD.
Giai đoạn 2 — Di chuyển (Di chuyển kéo): ngón tay di chuyển trên màn hình, bản xem trước kéo theo điểm chạm. Hệ thống liên tục xác định vùng thả dưới ngón tay và gửi các sự kiện drag-enter, drag-move và drag-exit. Vùng thả có thể phản ứng trực quan: làm nổi bật, mở rộng hoặc hiển thị một đường chèn. Giai đoạn này kéo dài từ 0,5 đến 10+ giây tùy thuộc vào độ phức tạp của nhiệm vụ.
Giai đoạn 3 — Thả (Thả): ngón tay nhấc khỏi màn hình. Hệ thống kết thúc phiên kéo và gọi callback thả trên phần tử mục tiêu. Nếu vùng thả từ chối việc thả (ví dụ: kéo tập tin vào thư mục không có quyền ghi), bản xem trước kéo sẽ hoạt ảnh trở về vị trí ban đầu (hoạt ảnh hủy). Nếu việc thả được chấp nhận, phần tử biến mất khỏi vị trí ban đầu và xuất hiện tại mục tiêu.
| Giai đoạn | Sự kiện iOS | Sự kiện Android | Trạng thái trực quan |
|---|---|---|---|
| Nắm | UIDragInteractionDelegate.dragSessionWillBegin | View.OnLongClickListener → startDragAndDrop | Bản xem trước kéo xuất hiện dưới ngón tay |
| Di chuyển | UIDropInteractionDelegate.dragSessionDidUpdate | View.OnDragListener.onDrag (ACTION_DRAG_MOVED) | Bản xem trước di chuyển, mục tiêu được làm nổi bật |
| Thả | UIDropInteractionDelegate.performDrop | View.OnDragListener.onDrag (ACTION_DROP) | Hoạt ảnh thả hoặc quay lại |
Kéo giữa các ứng dụng là một tính năng có sẵn trên cả hai nền tảng. iOS hỗ trợ Drag-and-Drop giữa các ứng dụng ở chế độ Split View và Slide Over (iPad). Android hỗ trợ kéo giữa các ứng dụng ở chế độ Split Screen. Dữ liệu được truyền qua UIPasteConfiguration (iOS) hoặc ClipData (Android). Nhà phát triển chỉ định các loại dữ liệu được hỗ trợ (UTI cho iOS, MIME cho Android).
UIDragInteraction là API iOS chính cho việc kéo, được thêm vào iOS 11. Để bật kéo từ một View, hãy thêm UIDragInteraction với UIDragInteractionDelegate. Ủy quyền xác định phần tử nào có thể kéo, hình thức và hành vi của chúng. Để nhận các phần tử, hãy sử dụng UIDropInteraction với UIDropInteractionDelegate, xác định liệu View có thể chấp nhận thả hay không và cách xử lý dữ liệu.
// UIDragInteraction — kéo một phần tử từ bộ sưu tập
class DragViewController: UIViewController, UIDragInteractionDelegate {
@IBOutlet var dragSourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let dragInteraction = UIDragInteraction(delegate: self)
dragSourceView.addInteraction(dragInteraction)
}
func dragInteraction(_ interaction: UIDragInteraction,
itemsForBeginning session: UIDragSession) -> [UIDragItem] {
guard let data = Data(contentsOf: selectedFileURL) else { return [] }
let itemProvider = NSItemProvider(object: data as NSData)
let dragItem = UIDragItem(itemProvider: itemProvider)
// Xem trước kéo — bản sao thu nhỏ của phần tử
dragItem.previewProvider = {
let previewView = UIImageView(image: thumbnailImage)
previewView.alpha = 0.8
return UIDragPreview(view: previewView)
}
return [dragItem]
}
}UIDropInteractionDelegate là giao diện phản chiếu để nhận các thả. Phương thức canHandle(_:) kiểm tra xem loại dữ liệu có được hỗ trợ hay không. sessionDidUpdate(_:) cập nhật trạng thái trực quan (làm nổi bật, đường chèn). performDrop(_:) thực hiện xử lý dữ liệu cuối cùng. iOS tự động gọi UIDropProposal với loại thao tác: .move, .copy hoặc .forbidden. Loại thao tác ảnh hưởng đến hiển thị trực quan (biểu tượng + ở góc bản xem trước cho sao chép, dấu cấm cho forbidden).
View.startDragAndDrop() là phương thức Android SDK để bắt đầu kéo, có sẵn từ API 24 (Android 7.0). Nó được gọi sau khi nắm phần tử (thường trong onLongClick). Các tham số: ClipData (dữ liệu), DragShadowBuilder (bóng trực quan), Object myLocalState (trạng thái cục bộ), flags. DragShadowBuilder tạo một Bitmap theo ngón tay. Hệ thống tự động quản lý hoạt ảnh và các sự kiện cảm ứng trong khi kéo.
// Drag-and-Drop phần tử RecyclerView với sắp xếp lại
class ReorderAdapter(private val items: MutableList<String>) :
RecyclerView.Adapter<ReorderAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.setOnLongClickListener {
// Tạo ClipData để truyền dữ liệu
val clipData = ClipData.newPlainText("item", items[position])
// DragShadowBuilder tùy chỉnh với bản xem trước thu nhỏ
val shadowBuilder = object : View.DragShadowBuilder(holder.itemView) {
override fun onProvideShadowMetrics(shadowSize: Point, shadowTouchPoint: Point) {
shadowSize.set(itemView.width, itemView.height / 2)
}
}
holder.itemView.startDragAndDrop(clipData, shadowBuilder, position, 0)
true
}
}
}Nhận thả trong Android được thực hiện qua View.setOnDragListener(). Trình lắng nghe nhận các sự kiện ACTION_DRAG_STARTED, ACTION_DRAG_ENTERED, ACTION_DRAG_LOCATION, ACTION_DRAG_EXITED, ACTION_DROP và ACTION_DRAG_ENDED. Trong sự kiện ACTION_DROP, dữ liệu được trích xuất từ ClipData và hành động mục tiêu được thực hiện. Đối với RecyclerView có sắp xếp lại, ItemTouchHelper.SimpleCallback được khuyến nghị — một cơ chế tích hợp sẵn tự động xử lý nhấn giữ để nắm, hoạt ảnh di chuyển và các phương thức tiện ích onMove/onSwiped.
Sắp xếp lại danh sách (drag to reorder) là kịch bản DnD phổ biến nhất: người dùng kéo các mục danh sách để thay đổi thứ tự của chúng. Ví dụ: sắp xếp lại danh bạ trong mục yêu thích, sắp xếp nhiệm vụ trong các trình theo dõi (Trello, Todoist), sắp xếp lại bài hát trong danh sách phát (Spotify, Apple Music). Được thực hiện qua ItemTouchHelper trong Android hoặc UILongPressGestureRecognizer với sắp xếp lại ô trong iOS.
Trình quản lý tập tin — Drag-and-Drop để sao chép và di chuyển tập tin giữa các thư mục. iOS Files App, Android Files by Google, Dropbox — tất cả đều hỗ trợ DnD. Kéo một tập tin vào thư mục sẽ di chuyển nó vào bên trong (move), kéo khi giữ Option/Ctrl sẽ sao chép nó (copy). Vùng thả sẽ làm nổi bật trực quan khi di chuển qua, hiển thị ranh giới thư mục.
Bảng Kanban — Trello, Jira, Notion sử dụng Drag-and-Drop để di chuyển thẻ giữa các cột (To Do → In Progress → Done). Khi kéo, thẻ tách khỏi cột nguồn, theo ngón tay và khi vượt qua ranh giới của một cột khác, nó sẽ chèn vào với hoạt ảnh. iOS 11+ hỗ trợ UIDragPreview với hiệu ứng thị sai (parallax), nơi bản xem trước nghiêng nhẹ khỏi điểm chạm, tạo chiều sâu.
Các phần Springboard (iOS) — kéo các biểu tượng ứng dụng trên màn hình chính iOS. Nhấn giữ một biểu tượng sẽ kích hoạt chế độ “rung lắc” (các biểu tượng rung), sau đó người dùng có thể di chuyển các biểu tượng giữa các trang và tạo thư mục. Đây là một trong những mẫu UX DnD phức tạp nhất vì nó yêu cầu xử lý đồng thời việc kéo, hoạt ảnh rung và nhóm vào thư mục.
Phản hồi trực quan là cực kỳ quan trọng đối với Drag-and-Drop. Đối tượng được kéo phải thay đổi: thu nhỏ (iOS: tỷ lệ 0,8–0,9), trở nên bán trong suốt (alpha 0,7–0,8) và nâng lên trên giao diện với bóng đổ (shadow offset). Vùng thả phải phản ứng trực quan khi di chuển qua: làm nổi bật, mở rộng hoặc hiển thị đường viền. Nếu không có các tín hiệu này, người dùng không thể biết nơi họ có thể thả đối tượng.
Hoạt ảnh quay lại (hủy kéo) — nếu người dùng thả phần tử bên ngoài vùng thả hoặc trên vùng không hợp lệ, đối tượng phải hoạt ảnh trở về vị trí ban đầu. iOS UIDragInteractionDelegate cung cấp dragInteraction(_:sessionDidMove:) để theo dõi và dragInteraction(_:session:willEndWith:) cho hoạt ảnh quay lại. Android View.setOnDragListener nhận được ACTION_DRAG_ENDED, nơi có thể bắt đầu hoạt ảnh quay lại qua ValueAnimator hoặc ObjectAnimator.
| Khuyến nghị | iOS | Android |
|---|---|---|
| Nắm bằng cử chỉ | Nhấn giữ (mặc định) | OnLongClickListener |
| Xem trước kéo | UIDragItem.previewProvider | DragShadowBuilder |
| Phản hồi xúc giác | UIImpactFeedbackGenerator | HapticFeedbackConstants.LONG_PRESS |
| Hoạt ảnh quay lại | UIDragPreviewTarget | ViewPropertyAnimator |
| Đề xuất thả | UIDropProposal (.move/.copy) | ACTION_DROP + cờ |
| DnD giữa các ứng dụng | iOS 11+ Split View (iPad) | Android 7.0+ Split Screen |
Khả năng tiếp cận: người dùng bị hạn chế vận động có thể không thực hiện được cử chỉ kéo. Hãy cung cấp các lựa chọn thay thế qua các nút “Di chuyển lên/xuống” cho danh sách và trình đơn ngữ cảnh “Di chuyển đến thư mục” cho tập tin. iOS VoiceOver hỗ trợ hành động răng quay “Kéo” để di chuyển các phần tử. Android TalkBack cung cấp các hành động tùy chỉnh qua AccessibilityNodeInfo.addAction(). Nike Run Club và Apple Health sử dụng DnD để sắp xếp lại các chỉ số trên bảng điều khiển — với quyền truy cập qua các nút tiêu chuẩn.
Câu hỏi thường gặp
Cử chỉ tiêu chuẩn để bắt đầu Drag-and-Drop là nhấn giữ trong 300–500 ms. iOS sử dụng cơ chế UIDragInteraction tích hợp sẵn, tự động chuyển đổi một cú chạm duy trì thành thao tác nắm. Android yêu cầu gọi tường minh startDragAndDrop() trong callback OnLongClickListener. Trong một số trường hợp (ví dụ: trò chơi), DnD có thể bắt đầu bằng một cú chạm đơn giản mà không có độ trễ.
Có, Drag-and-Drop giữa các ứng dụng được hỗ trợ trên iOS 11+ (iPad) và Android 7.0+. Trên iPad, kéo hoạt động ở chế độ Split View và Slide Over — người dùng có thể lấy văn bản từ Safari và thả vào Ghi chú. Trên Android, DnD giữa các ứng dụng hoạt động ở Split Screen. Dữ liệu được truyền qua UIPasteConfiguration (iOS) hoặc ClipData (Android) với các loại được hỗ trợ đã chỉ định (UTI, MIME).
Xung đột DnD và Cuộn được giải quyết thông qua độ trễ nắm. Nếu người dùng bắt đầu di chuyển ngón tay trước khi hết thời gian chờ nhấn giữ (500 ms), cử chỉ sẽ được hiểu là cuộn thay vì Drag-and-Drop. Trên iOS, UIDragInteraction tự động quản lý xung đột này qua cancelsTouchesInView. Trên Android, ItemTouchHelper sử dụng một ngưỡng — nếu chuyển động vượt quá ngưỡng TouchSlop, nhấn giữ sẽ bị hủy để ưu tiên cuộn.
Drag-and-Drop hỗ trợ mọi loại dữ liệu: văn bản, hình ảnh, tập tin, URL, đối tượng tùy chỉnh. iOS sử dụng NSItemProvider với các loại UTI (public.text, public.image, public.file-url). Android sử dụng ClipData với các loại MIME (text/plain, image/png, application/octet-stream). Đối với dữ liệu tùy chỉnh, một ClipData.Item với Intent được tạo. Đối với DnD giữa các ứng dụng, dữ liệu được đóng gói vào các định dạng phổ biến: văn bản thuần, HTML, RTF, UIImage/PNG.
Trên iOS, hoạt ảnh quay lại được xử lý qua UIDragPreviewTarget — vị trí cuối cùng mà bản xem trước quay về bằng hoạt ảnh. Trên Android, hãy sử dụng ViewPropertyAnimator với translateX/translateY để đưa phần tử trở về vị trí ban đầu. Thời lượng hoạt ảnh là 200–300 ms với đường cong easing (EaseOut). UIKit tự động thêm UIDragPreview với hiệu ứng thị sai, quay lại mượt mà khi cử chỉ bị hủy.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm