Khái niệm chính

Các phần sau đây giải thích một số khái niệm chính đối với quy trình kéo và thả.

Quá trình kéo và thả

Có 4 bước hoặc trạng thái trong quá trình kéo và thả: bắt đầu, tiếp tục, đã bỏ và kết thúc.

Started (Đã khởi động)

Để phản hồi cử chỉ kéo của người dùng, ứng dụng sẽ gọi startDragAndDrop() để yêu cầu hệ thống bắt đầu thao tác kéo và thả. Các đối số của phương thức cung cấp như sau:

  • Dữ liệu cần kéo.
  • Lệnh gọi lại để vẽ bóng khi kéo
  • Siêu dữ liệu mô tả dữ liệu đã kéo
  • Hệ thống sẽ phản hồi bằng cách gọi lại ứng dụng của bạn để lấy bóng khi kéo. Sau đó, hệ thống sẽ hiển thị bóng khi kéo trên thiết bị.
  • Tiếp theo, hệ thống sẽ gửi một sự kiện kéo có loại thao tác ACTION_DRAG_STARTED đến trình nghe sự kiện kéo của tất cả các đối tượng View trong bố cục hiện tại. Để tiếp tục nhận các sự kiện kéo (bao gồm cả một sự kiện thả có thể xảy ra), trình nghe sự kiện kéo phải trả về true. Thao tác này sẽ đăng ký trình nghe với hệ thống. Chỉ những trình nghe đã đăng ký mới tiếp tục nhận được các sự kiện kéo. Tại thời điểm này, trình nghe cũng có thể thay đổi giao diện của đối tượng View mục tiêu thả để cho thấy rằng khung hiển thị có thể chấp nhận một sự kiện thả.
  • Nếu trình nghe sự kiện kéo trả về false, trình nghe đó sẽ không nhận được các sự kiện kéo cho thao tác hiện tại cho đến khi hệ thống gửi một sự kiện kéo có loại thao tác ACTION_DRAG_ENDED. Khi trả về false, trình nghe sẽ cho hệ thống biết rằng nó không quan tâm đến thao tác kéo và thả cũng như không muốn chấp nhận dữ liệu đã kéo.
Đang tiếp tục
Người dùng tiếp tục kéo. Khi bóng khi kéo giao với hộp giới hạn của mục tiêu thả, hệ thống sẽ gửi một hoặc nhiều sự kiện kéo đến trình nghe sự kiện kéo của mục tiêu. Trình nghe có thể thay đổi giao diện của mục tiêu thả View để phản hồi sự kiện. Ví dụ: nếu sự kiện cho biết rằng bóng khi kéo vào hộp giới hạn của mục tiêu thả (loại hành động ACTION_DRAG_ENTERED), thì trình nghe có thể phản ứng bằng cách làm nổi bật View.
Thả
Người dùng thả bóng khi kéo trong hộp giới hạn của mục tiêu thả. Hệ thống sẽ gửi trình nghe của mục tiêu thả một sự kiện kéo có loại thao tác ACTION_DROP. Đối tượng sự kiện kéo chứa dữ liệu truyền đến hệ thống trong lệnh gọi startDragAndDrop() để bắt đầu thao tác. Trình nghe dự kiến sẽ trả về boolean true cho hệ thống nếu trình nghe xử lý thành công dữ liệu được thả. : Bước này chỉ xảy ra nếu người dùng thả bóng khi kéo trong hộp giới hạn của View có trình nghe đã được đăng ký để nhận sự kiện kéo (mục tiêu thả). Nếu người dùng thả bóng khi kéo trong bất kỳ tình huống nào khác, thì sẽ không có sự kiện kéo ACTION_DROP nào được gửi.
Đã kết thúc

Sau khi người dùng thả bóng khi kéo và sau khi hệ thống gửi

một sự kiện kéo có loại thao tác ACTION_DROP, nếu cần, hệ thống sẽ gửi một sự kiện kéo có loại thao tác ACTION_DRAG_ENDED để cho biết thao tác kéo và thả đã kết thúc. Việc này được thực hiện bất kể người dùng thả bóng khi kéo ở đâu. Sự kiện này được gửi đến mọi trình nghe được đăng ký để nhận sự kiện kéo, ngay cả khi trình nghe cũng nhận được sự kiện ACTION_DROP.

Từng bước này được mô tả chi tiết hơn trong phần có tên là Thao tác kéo và thả.

Sự kiện kéo

Hệ thống sẽ gửi một sự kiện kéo dưới dạng đối tượng DragEvent, chứa loại thao tác mô tả những gì đang xảy ra trong quá trình kéo và thả. Tuỳ thuộc vào loại thao tác mà đối tượng cũng có thể chứa dữ liệu khác.

Trình nghe sự kiện kéo sẽ nhận đối tượng DragEvent. Để biết loại thao tác, trình nghe gọi DragEvent.getAction(). Có 6 giá trị có thể được xác định bằng các hằng số trong lớp DragEvent, như mô tả trong bảng 1:

Bảng 1. Các loại thao tác sự kiện kéo

Loại thao tác Ý nghĩa
ACTION_DRAG_STARTED Ứng dụng gọi startDragAndDrop() và thu được bóng khi kéo. Nếu muốn tiếp tục nhận các sự kiện kéo cho thao tác này, trình nghe phải trả về giá trị boolean true cho hệ thống.
ACTION_DRAG_ENTERED Bóng khi kéo vào hộp giới hạn của View của trình nghe sự kiện kéo. Đây là loại thao tác sự kiện đầu tiên mà trình nghe nhận được khi bóng khi kéo vào hộp giới hạn.
ACTION_DRAG_LOCATION Sau sự kiện ACTION_DRAG_ENTERED, bóng đổ vẫn nằm trong hộp giới hạn của View của trình nghe sự kiện kéo.
ACTION_DRAG_EXITED Sau ACTION_DRAG_ENTERED và ít nhất một sự kiện ACTION_DRAG_LOCATION, bóng khi kéo sẽ di chuyển ra bên ngoài hộp giới hạn của View của trình nghe sự kiện kéo.
ACTION_DROP Bóng khi kéo được thả qua View của trình nghe sự kiện kéo. Loại thao tác này chỉ được gửi đến trình nghe của đối tượng View nếu trình nghe trả về boolean true để phản hồi sự kiện kéo ACTION_DRAG_STARTED. Loại thao tác này sẽ không được gửi nếu người dùng thả bóng khi kéo trên View có trình nghe chưa đăng ký, hoặc nếu người dùng thả bóng khi kéo trên bất kỳ phần tử nào không thuộc bố cục hiện tại.

Trình nghe sẽ trả về giá trị boolean true nếu xử lý thành công thao tác thả. Nếu không, hàm này phải trả về false.

ACTION_DRAG_ENDED Hệ thống đang kết thúc thao tác kéo và thả. Loại hành động này không nhất thiết phải đứng sau sự kiện ACTION_DROP. Nếu hệ thống gửi một ACTION_DROP, thì việc nhận được loại thao tác ACTION_DRAG_ENDED không có nghĩa là việc giảm đã thành công. Trình nghe phải gọi getResult(), như minh hoạ trong bảng 2 để nhận giá trị được trả về trong phản hồi ACTION_DROP. Nếu sự kiện ACTION_DROP không được gửi, thì getResult() sẽ trả về false.

Đối tượng DragEvent cũng chứa dữ liệu và siêu dữ liệu mà ứng dụng của bạn cung cấp cho hệ thống trong lệnh gọi đến startDragAndDrop(). Một số dữ liệu chỉ hợp lệ cho một số loại hành động nhất định được tóm tắt trong bảng 2. Để biết thêm thông tin về các sự kiện và dữ liệu liên quan, hãy xem phần Thao tác kéo và thả.

Bảng 2. Dữ liệu DragEvent hợp lệ theo loại thao tác

Giá trị getAction()
Giá trị getClipDescription()
Giá trị getLocalState()
Giá trị getX()
Giá trị getY()
Giá trị getClipData()
Giá trị getResult()
ACTION_DRAG_STARTED ✓ ✓        
ACTION_DRAG_ENTERED ✓ ✓        
ACTION_DRAG_LOCATION ✓ ✓ ✓ ✓    
ACTION_DRAG_EXITED ✓ ✓        
ACTION_DROP ✓ ✓ ✓ ✓ ✓  
ACTION_DRAG_ENDED   ✓       ✓

Các phương thức DragEvent getAction(), describeContents(), writeToParcel()toString() luôn trả về dữ liệu hợp lệ.

Nếu một phương thức không chứa dữ liệu hợp lệ cho một loại thao tác cụ thể, thì phương thức đó sẽ trả về null hoặc 0, tuỳ thuộc vào loại kết quả.

Bóng khi kéo

Trong khi thực hiện thao tác kéo và thả, hệ thống sẽ hiển thị một hình ảnh mà người dùng kéo. Đối với di chuyển dữ liệu, hình ảnh này đại diện cho dữ liệu đang được kéo. Đối với các thao tác khác, hình ảnh đại diện cho một số chương trình thành phần của thao tác kéo.

Hình ảnh này được gọi là đổ bóng khi kéo. Bạn tạo lớp này bằng các phương thức bạn khai báo cho đối tượng View.DragShadowBuilder. Bạn truyền trình tạo đến hệ thống khi bắt đầu thao tác kéo và thả bằng startDragAndDrop(). Để phản hồi startDragAndDrop(), hệ thống gọi các phương thức gọi lại mà bạn xác định trong View.DragShadowBuilder để lấy bóng khi kéo.

Lớp View.DragShadowBuilder có hai hàm khởi tạo (constructor):

View.DragShadowBuilder(View)

Hàm khởi tạo này chấp nhận mọi đối tượng View của ứng dụng. Hàm khởi tạo lưu trữ đối tượng View trong đối tượng View.DragShadowBuilder, vì vậy, các lệnh gọi lại có thể truy cập vào đối tượng đó để tạo bóng khi kéo. Khung hiển thị không nhất thiết phải là View mà người dùng chọn để bắt đầu thao tác kéo.

Nếu sử dụng hàm khởi tạo này, bạn không cần phải mở rộng View.DragShadowBuilder hoặc ghi đè phương thức của hàm khởi tạo. Theo mặc định, bạn sẽ nhận được một bóng khi kéo có hình thức giống với View mà bạn truyền dưới dạng đối số, nằm ở giữa vị trí người dùng chạm vào màn hình.

View.DragShadowBuilder()

Nếu bạn sử dụng hàm khởi tạo này, sẽ không có đối tượng View nào trong đối tượng View.DragShadowBuilder. Trường này đã được đặt thành null. Bạn phải mở rộng View.DragShadowBuilder và ghi đè các phương thức, nếu không, bạn sẽ nhận được bóng khi kéo vô hình. Hệ thống không báo lỗi.

Lớp View.DragShadowBuilder có 2 phương thức cùng tạo bóng khi kéo:

onProvideShadowMetrics()

Hệ thống sẽ gọi phương thức này ngay sau khi bạn gọi startDragAndDrop(). Sử dụng phương thức để gửi kích thước và điểm chạm của bóng khi kéo đến hệ thống. Phương thức có hai tham số:

outShadowSize: đối tượng Point. Chiều rộng bóng khi kéo được truyền vào x và chiều cao của bóng được truyền vào y.

outShadowTouchPoint: đối tượng Point. Điểm chạm là vị trí bên trong bóng khi kéo và phải nằm dưới ngón tay của người dùng trong khi kéo. Vị trí X của thiết bị nằm trong x và vị trí Y của thiết bị nằm trong y.

onDrawShadow()

Ngay sau khi gọi onProvideShadowMetrics(), hệ thống sẽ gọi onDrawShadow() để tạo bóng khi kéo. Phương thức này có một đối số duy nhất, đối tượng Canvas mà hệ thống tạo từ các tham số bạn cung cấp trong onProvideShadowMetrics(). Phương thức này sẽ vẽ bóng khi kéo trên Canvas được cung cấp.

Để cải thiện hiệu suất, hãy giữ cho kích thước của bóng khi kéo nhỏ. Đối với một mục riêng lẻ, bạn nên sử dụng biểu tượng. Để chọn nhiều mục, bạn nên sử dụng các biểu tượng trong một ngăn xếp thay vì hình ảnh đầy đủ trải rộng trên màn hình.

Trình nghe sự kiện kéo và phương thức gọi lại

View nhận các sự kiện kéo bằng trình nghe sự kiện kéo giúp triển khai View.OnDragListener hoặc bằng phương thức gọi lại onDragEvent() của khung hiển thị. Khi gọi phương thức hoặc trình nghe, hệ thống sẽ cung cấp một đối số DragEvent.

Trong hầu hết trường hợp, bạn nên sử dụng trình nghe thay vì phương thức gọi lại. Khi thiết kế giao diện người dùng, bạn thường không phân lớp các lớp View, nhưng khi sử dụng phương thức gọi lại, bạn sẽ phải tạo các lớp con để ghi đè phương thức. Khi so sánh, bạn có thể triển khai một lớp trình nghe rồi sử dụng lớp đó với nhiều đối tượng View riêng biệt. Bạn cũng có thể triển khai phương thức này dưới dạng một lớp cùng dòng hoặc biểu thức lambda ẩn danh. Để thiết lập trình nghe cho đối tượng View, hãy gọi setOnDragListener().

Ngoài ra, bạn có thể thay đổi cách triển khai mặc định của onDragEvent() mà không cần ghi đè phương thức. Đặt OnReceiveContentListener trên một khung hiển thị; để biết thêm thông tin chi tiết, hãy xem setOnReceiveContentListener(). Sau đó, phương thức onDragEvent() sẽ thực hiện những việc sau theo mặc định:

  • Trả về giá trị true (đúng) cho lệnh gọi đến startDragAndDrop().
  • Gọi performReceiveContent() nếu dữ liệu kéo và thả được thả vào khung hiển thị. Dữ liệu được truyền đến phương thức dưới dạng đối tượng ContentInfo. Phương thức này sẽ gọi OnReceiveContentListener.

  • Trả về giá trị true nếu dữ liệu kéo và thả được thả vào khung hiển thị và OnReceiveContentListener sử dụng bất kỳ nội dung nào.

Xác định OnReceiveContentListener để xử lý dữ liệu dành riêng cho ứng dụng của bạn. Để tương thích ngược xuống API cấp 24, hãy sử dụng phiên bản Jetpack của OnReceiveContentListener.

Bạn có thể có trình nghe sự kiện kéo và phương thức gọi lại cho đối tượng View, trong trường hợp này, hệ thống sẽ gọi trình nghe trước tiên. Hệ thống sẽ không gọi phương thức gọi lại trừ phi trình nghe trả về false.

Tổ hợp phương thức onDragEvent()View.OnDragListener tương tự như tổ hợp onTouchEvent()View.OnTouchListener dùng với các sự kiện chạm.