Networks Business Online Việt Nam & International VH2

Kéo và thả | Android Developers

Đăng ngày 08 November, 2022 bởi admin

Khung kéo và thả của Android cho phép bạn thêm khả năng kéo và thả tương tác vào ứng dụng của mình. Với thao tác kéo và thả, người dùng có thể sao chép hoặc di chuyển văn bản, hình ảnh, đối tượng – bất kỳ nội dung nào có thể được biểu thị bằng một URI – từ View sang thành phần khác trong ứng dụng hoặc, trongchế độ nhiều cửa sổ giữa các ứng dụng.

Chuỗi văn bản và hình ảnh được kéo và thả trong một ứng dụng. Chuỗi văn bản và hình ảnh được kéo và thả giữa các ứng dụng trong chế độ chia đôi màn hình.
Hình 1. Kéo và thả trong ứng dụng.

Hình 2. Kéo và thả giữa các ứng dụng.

Khung này gồm có một lớp sự kiện kéo, những trình nghe sự kiện kéo ( drag listener ), những lớp tương hỗ ( helper ) và phương pháp. Mặc dù được phong cách thiết kế đa phần để cho phép chuyển tài liệu, nhưng bạn hoàn toàn có thể sử dụng khung này cho những thao tác khác trên giao diện người dùng. Ví dụ : bạn hoàn toàn có thể tạo một ứng dụng phối hợp những màu khi người dùng kéo một hình tượng màu lên trên một hình tượng khác. Tuy nhiên, phần còn lại của hướng dẫn này miêu tả khung kéo và thả trong toàn cảnh truyền tài liệu .

Tổng quan

Thao tác kéo và thả sẽ bắt đầu khi người dùng thực hiện một cử chỉ trên giao diện người dùng mà ứng dụng nhận dạng là một tín hiệu để bắt đầu kéo dữ liệu. Để phản hồi, ứng dụng sẽ thông báo cho hệ thống rằng thao tác kéo và thả đang bắt đầu. Hệ thống gọi lại ứng dụng của bạn để lấy định dạng dữ liệu đang được kéo (bóng khi kéo). Khi người dùng di chuyển bóng khi kéo (drag shadow) trên bố cục của ứng dụng, hệ thống sẽ gửi các sự kiện kéo đến trình nghe sự kiện kéo và các phương thức gọi lại liên kết với các đối tượng View trong bố cục. Nếu người dùng thả bóng khi kéo sang một thành phần hiển thị có thể chấp nhận dữ liệu (mục tiêu thả), hệ thống sẽ gửi dữ liệu đến mục tiêu. Thao tác kéo và thả kết thúc khi người dùng thả bóng khi kéo cho dù bóng khi kéo có nằm trên mục tiêu thả hay không.

Bạn tạo một trình nghe sự kiện kéo bằng cách triển khai View.OnDragListener. Bạn thiết lập trình nghe cho mục tiêu thả bằng phương thức setOnDragListener() của đối tượng View. Mỗi thành phần hiển thị trong bố cục cũng có một phương thức gọi lại onDragEvent().

Lưu ý:true phải được đính kèm với thành phần hiển thị (view) hoặc phương thức true. Mặc dù mọi thành phần hiển thị trong bố cục đều có lệnh gọi lại onDragEvent(), phương thức triển khai mặc định sẽ trả về false.

Để thành phần hiển thị có thể trở thành mục tiêu thả, View.OnDragListener trả vềphải được đính kèm với thành phần hiển thị (view) hoặc phương thức onDragEvent() của thành phần hiển thị phải trả về. Mặc dù mọi thành phần hiển thị trong bố cục đều có lệnh gọi lại, phương thức triển khai mặc định sẽ trả về

Ứng dụng của bạn thông báo cho hệ thống bắt đầu thao tác kéo và thả bằng cách gọi phương thức startDragAndDrop(). Phương thức này sẽ cho hệ thống biết để bắt đầu gửi các sự kiện kéo. Phương thức này cũng cung cấp cho hệ thống dữ liệu mà người dùng đang kéo và siêu dữ liệu mô tả dữ liệu đó. Bạn có thể gọi startDragAndDrop() trên bất kỳ View nào trong bố cục hiện tại. Hệ thống chỉ sử dụng đối tượng View để có quyền truy cập vào các tùy chọn cài đặt chung trong bố cục.

Trong quá trình kéo và thả, hệ thống sẽ gửi các sự kiện kéo đến trình nghe sự kiện kéo hoặc phương thức gọi lại của các đối tượng View trong bố cục. Trình nghe sự kiện hoặc phương thức gọi lại sử dụng siêu dữ liệu để quyết định xem có muốn chấp nhận dữ liệu khi dữ liệu đó được thả hay không. Nếu người dùng thả dữ liệu trên mục tiêu thả (View chấp nhận dữ liệu đó), hệ thống sẽ gửi một đối tượng sự kiện kéo chứa dữ liệu đến trình nghe sự kiện kéo hoặc phương thức gọi lại của mục tiêu thả.

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

View nhận sự kiện kéo bằng trình lắng nghe sự kiện kéo mà sẽ triển khai View.OnDragListener hoặc bằng phương thức gọi lại onDragEvent() của thành phần hiển thị. Khi gọi phương thức hoặc trình nghe sự kiện, 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 thẻ này dưới dạng lớp nội tuyến ẩn danh hoặc biểu thức lambda. Để thiết lập trình nghe cho đối tượng View, hãy gọi setOnDragListener().

Thay vào đó, bạn có thể thay đổi phương thức triển khai mặc định của onDragEvent() mà không cần ghi đè phương thức. Nếu bạn thiết lập OnReceiveContentListener trên một thành phần hiển thị (xem setOnReceiveContentListener()), phương thức onDragEvent() theo mặc định sẽ thực hiện những việc sau:

  • Trả về giá trị đúng (true) khi gọi tới startDragAndDrop()
  • Gọi performReceiveContent() nếu dữ liệu kéo và thả được thả trên thành phần hiển thị

    Dữ liệu được truyền vào phương thức dưới dạng đối tượng ContentInfo. Phương thức gọi OnReceiveContentListener.

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

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

Bạn có thể có 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. Hệ thống không gọi phương thức gọi lại trừ khi trình nghe trả về false.

Việc kết hợp phương thức onDragEvent()View.OnDragListener sẽ tương tự như cách kết hợp onTouchEvent()View.OnTouchListener dùng cho các sự kiện chạm.

Từ khoá chính:

Các phần sau của hướng dẫn này đề cập đến phương thức nhận sự kiện kéo dưới dạng trình nghe sự kiện kéo; tuy nhiên, phương thức này có thể là lệnh gọi lại onDragEvent() hoặc phương thức View.OnDragListener hoặc cả hai.

Quy trình kéo và thả

Về cơ bản, có 4 bước hoặc trạng thái trong quy trình kéo và thả : Bắt đầu ( Started ), Tiếp tục ( Continuing ), Thả ( Dropped ) và Kết thúc ( Ended ) .

Bắt đầu

Để phản hồi thao tác kéo của người dùng, ứng dụng của bạn 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 các yếu tố 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
Trước tiên, mạng lưới 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 đó, mạng lưới 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 cho trình nghe sự kiện kéo của tất cả 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. Việ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 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 biết rằng thành phần hiển thị có thể chấp nhận sự kiện thả.

Nếu trình nghe sự kiện kéo trả về false, thì trình nghe sự kiện sẽ không nhận được sự kiện kéo cho thao tác hiện tại cho đến khi hệ thống gửi 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.

Lưu ý:false từ

Giá trị trả về làtừ View.OnDragListener sẽ kích hoạt trình xử lý onDragEvent()

của thành phần hiển thị.

Tiếp tục

Người dùng tiếp tục kéo. Lúc bóng khi kéo giao với hộp giới hạn (bounding box) 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ể chọn 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 bóng khi thả đã vào phạm vi hộp giới hạn của mục tiêu thả xuống (loại thao tác 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 đã được truyền đến hệ thống qua 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ả.

Lưu ý rằng 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 mà trình nghe đã đă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 đi (nếu cần) một sự kiện kéo có loại thao tác ACTION_DROP, hệ thống sẽ gửi một sự kiện kéo với 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 tới mọi trình nghe đã đă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.

Mỗi bước trong số 4 bước này đều được diễn đạt chi tiết cụ thể hơn trong phần Thao tác kéo và thả .
Lưu ý:Nếu những ứng dụng đang chạy ở chính sách nhiều hành lang cửa số, người dùng hoàn toàn có thể kéo và thả tài liệu từ ứng dụng này sang ứng dụng khác. Để biết thêm thông tin, hãy xem nội dung Kéo và thả trong bài viết Hỗ trợ nhiều hành lang cửa số

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 qua các hằng số trong lớp DragEvent.

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à có một 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 thả vừa vào phạm vi 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 lúc phần bóng khi kéo vào phạm vi hộp giới hạn.
ACTION_DRAG_LOCATION Sau sự kiện ACTION_DRAG_ENTERED, bóng (shadow) 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 đã di chuyển ra 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 lạ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 khác không thuộc bố cục hiện tại.

Trình nghe dự kiến sẽ trả về giá trị boolean true nếu xử lý thành công sự kiện thả. Nếu không, trình nghe sẽ trả về giá trị false.

ACTION_DRAG_ENDED Hệ thống đang kết thúc thao tác kéo và thả. Loại thao tác này không nhất thiết phải xảy ra 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 cho biết rằng sự kiện thả đã được thực hiện thành công. Trình nghe phải gọi getResult() (xem bảng 2) để nhận giá trị được trả về khi phản hồi ACTION_DROP. Nếu 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 startDragAndDrop(). Một số dữ liệu chỉ hợp lệ cho một số loại thao tác 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 bài Thao tác kéo và thả.

Hình 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 &check &check &check &check    
ACTION_DRAG_ENTERED &check &check        
ACTION_DRAG_LOCATION &check &check &check &check    
ACTION_DRAG_EXITED &check &check        
ACTION_DROP &check &check &check &check &check  
ACTION_DRAG_ENDED   &check       &check

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

Nếu 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ả của phương thức đó.

Bóng khi kéo

Trong quy trình kéo và thả, mạng lưới hệ thống sẽ hiển thị một hình ảnh mà người dùng kéo. Đối với vận động và di chuyển tài liệu, hình ảnh này đại diện thay mặt cho tài liệu đang được kéo. Đối với những thao tác khác, hình ảnh đại diện thay mặt cho một số ít 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 (drag shadow). Bạn tạo bóng khi kéo 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 tới 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 đã định nghĩa 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. Thành phần hiển thị không nhất thiết phải là View (nếu có) 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ẽ thấy một bóng khi kéo có giao diện giống như View mà bạn đã truyền vào làm đối số, nằm ở chính giữa vị trí mà người dùng đang 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, thì sẽ không có đối tượng View nào trong đối tượng View.DragShadowBuilder (trường này được đặt giá trị là null). Bạn phải mở rộng View.DragShadowBuilder và ghi đè các phương thức của hàm khởi tạo đó, nếu không bạn sẽ thấy một bóng khi kéo vô hình. Hệ thống không báo ngoại lệ.

Lớp View.DragShadowBuilder có hai phương thức để 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 truyền vào y.
outShadowTouchPoint
Đối tượng Point. Điểm chạm là vị trí trong bóng khi kéo và dưới ngón tay người dùng trong khi kéo. Vị trí X của bóng khi kéo được truyền vào x và vị trí Y được truyền vào 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ừ tham số bạn cung cấp trong onProvideShadowMetrics().
Phương thức này sẽ kéo bóng khi kéo trên Canvas được cung cấp.

Để cải tổ hiệu suất, bạn nên giữ cho size của bóng khi kéo nhỏ. Để chọn một mục duy nhất, bạn hoàn toàn có thể dùng hình tượng. Để chọn nhiều mục, bạn hoàn toàn có thể sử dụng những hình tượng trong ngăn xếp thay vì toàn hình ảnh bung rộng trên màn hình hiển thị .

Thao tác kéo và thả

Phần này trình diễn từng bước cách khởi đầu kéo, phản hồi những sự kiện trong quy trình kéo, phản hồi một sự kiện thả và cách kết thúc thao tác kéo và thả .

Bắt đầu kéo

Người dùng bắt đầu kéo bằng một cử chỉ kéo, thường là chạm và giữ trên đối tượng View. Để phản hồi lại, ứng dụng của bạn cần làm những việc sau:

  1. Tạo một đối tượng ClipData và đối tượng ClipData.Item cho dữ liệu đang được di chuyển. Là một phần của ClipData, cung cấp siêu dữ liệu được lưu trữ trong đối tượng ClipDescription trong ClipData. Đối với thao tác kéo và thả không để di chuyển dữ liệu, bạn có thể muốn sử dụng null thay vì một đối tượng thực tế.

    Ví dụ: đoạn mã dưới đây cho biết cách phản hồi thao tác chạm và giữ trên ImageView bằng cách tạo đối tượng ClipData chứa thẻ (hoặc nhãn) của ImageView:

    Kotlin

    // Create a string for the ImageView label.
    val IMAGEVIEW_TAG = "icon bitmap"
    
    ...
    
    val imageView = ImageView(this).apply {
        // Sets the bitmap for the ImageView from an icon bit map (defined elsewhere).
        setImageBitmap(iconBitmap)
        tag = IMAGEVIEW_TAG
        setOnLongClickListener { v ->
            // Create a new ClipData.
            // This is done in two steps to provide clarity. The convenience method
            // ClipData.newPlainText() can create a plain text ClipData in one step.
    
            // Create a new ClipData.Item from the ImageView object's tag.
            val item = ClipData.Item(v.tag as? CharSequence)
    
            // Create a new ClipData using the tag as a label, the plain text MIME type, and
            // the already-created item. This creates a new ClipDescription object within the
            // ClipData and sets its MIME type to "text/plain".
            val dragData = ClipData(
                v.tag as? CharSequence,
                arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN),
                item)
    
            // Instantiate the drag shadow builder.
            val myShadow = MyDragShadowBuilder(this)
    
            // Start the drag.
            v.startDragAndDrop(dragData,  // The data to be dragged
                               myShadow,  // The drag shadow builder
                               null,      // No need to use local data
                               0          // Flags (not currently used, set to 0)
            )
    
           // Indicate that the long-click was handled.
           true
        }
    }
    

    Java

    // Create a string for the ImageView label.
    private static final String IMAGEVIEW_TAG = "icon bitmap";
    
    ...
    
    // Create a new ImageView.
    ImageView imageView = new ImageView(this);
    
    // Set the bitmap for the ImageView from an icon bit map (defined elsewhere).
    imageView.setImageBitmap(iconBitmap);
    
    // Set the tag.
    imageView.setTag(IMAGEVIEW_TAG);
    
    // Sets a long click listener for the ImageView using an anonymous listener object that
    // implements the OnLongClickListener interface.
    imageView.setOnLongClickListener( v -> {
    
        // Create a new ClipData.
        // This is done in two steps to provide clarity. The convenience method
        // ClipData.newPlainText() can create a plain text ClipData in one step.
    
        // Create a new ClipData.Item from the ImageView object's tag.
        ClipData.Item item = new ClipData.Item((CharSequence) v.getTag());
    
        // Create a new ClipData using the tag as a label, the plain text MIME type, and
        // the already-created item. This creates a new ClipDescription object within the
        // ClipData and sets its MIME type to "text/plain".
        ClipData dragData = new ClipData(
            (CharSequence) v.getTag(),
            new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN },
            item);
    
        // Instantiate the drag shadow builder.
        View.DragShadowBuilder myShadow = new MyDragShadowBuilder(imageView);
    
        // Start the drag.
        v.startDragAndDrop(dragData,  // The data to be dragged
                           myShadow,  // The drag shadow builder
                           null,      // No need to use local data
                           0          // Flags (not currently used, set to 0)
        );
    
        // Indicate that the long-click was handled.
        return true;
    });
    
  2. Đoạn mã sau đây định nghĩa myDragShadowBuilder bằng cách ghi đè các phương thức trong View.DragShadowBuilder. Mã này tạo một bóng khi kéo hình chữ nhật nhỏ màu xám cho TextView:

    Kotlin

    private class MyDragShadowBuilder(v: View) : View.DragShadowBuilder(v) {
    
        private val shadow = ColorDrawable(Color.LTGRAY)
    
        // Defines a callback that sends the drag shadow dimensions and touch point
        // back to the system.
        override fun onProvideShadowMetrics(size: Point, touch: Point) {
    
            // Set the width of the shadow to half the width of the original View.
            val width: Int = view.width / 2
    
            // Set the height of the shadow to half the height of the original View.
            val height: Int = view.height / 2
    
            // The drag shadow is a ColorDrawable. This sets its dimensions to be the
            // same as the Canvas that the system provides. As a result, the drag shadow
            // fills the Canvas.
            shadow.setBounds(0, 0, width, height)
    
            // Set the size parameter's width and height values. These get back to
            // the system through the size parameter.
            size.set(width, height)
    
            // Set the touch point's position to be in the middle of the drag shadow.
            touch.set(width / 2, height / 2)
        }
    
        // Defines a callback that draws the drag shadow in a Canvas that the system
        // constructs from the dimensions passed to onProvideShadowMetrics().
        override fun onDrawShadow(canvas: Canvas) {
    
            // Draw the ColorDrawable on the Canvas passed in from the system.
            shadow.draw(canvas)
        }
    }
    

    Java

    private static class MyDragShadowBuilder extends View.DragShadowBuilder {
    
        // The drag shadow image, defined as a drawable object.
        private static Drawable shadow;
    
        // Constructor
        public MyDragShadowBuilder(View v) {
    
            // Stores the View parameter.
            super(v);
    
            // Creates a draggable image that fills the Canvas provided by the system.
            shadow = new ColorDrawable(Color.LTGRAY);
        }
    
        // Defines a callback that sends the drag shadow dimensions and touch point
        // back to the system.
        @Override
        public void onProvideShadowMetrics (Point size, Point touch) {
    
            // Defines local variables
            int width, height;
    
            // Set the width of the shadow to half the width of the original View.
            width = getView().getWidth() / 2;
    
            // Set the height of the shadow to half the height of the original View.
            height = getView().getHeight() / 2;
    
            // The drag shadow is a ColorDrawable. This sets its dimensions to be the
            // same as the Canvas that the system provides. As a result, the drag shadow
            // fills the Canvas.
            shadow.setBounds(0, 0, width, height);
    
            // Set the size parameter's width and height values. These get back to the
            // system through the size parameter.
            size.set(width, height);
    
            // Set the touch point's position to be in the middle of the drag shadow.
            touch.set(width / 2, height / 2);
        }
    
        // Defines a callback that draws the drag shadow in a Canvas that the system
        // constructs from the dimensions passed to onProvideShadowMetrics().
        @Override
        public void onDrawShadow(Canvas canvas) {
    
            // Draw the ColorDrawable on the Canvas passed in from the system.
            shadow.draw(canvas);
        }
    }
    

    Lưu ý:View được truyền tới. Hàm khởi tạo cũng căn giữa điểm chạm trong bóng khi kéo.

    Hãy nhớ rằng bạn không cần phải mở rộng View.DragShadowBuilder. Hàm khởi tạo View.DragShadowBuilder(View) tạo bóng khi kéo mặc định có cùng kích thước với đối sốđược truyền tới. Hàm khởi tạo cũng căn giữa điểm chạm trong bóng khi kéo.

Phản hồi khi bắt đầu kéo

Trong quá trình kéo, hệ thống sẽ gửi các sự kiện kéo đến trình nghe sự kiện kéo của các đối tượng View trong bố cục hiện tại. Trình nghe phản hồi bằng cách gọi DragEvent#getAction() để nhận loại thao tác. Khi bắt đầu kéo, phương thức này sẽ trả về ACTION_DRAG_STARTED.

Để phản hồi lại một sự kiện có loại thao tác ACTION_DRAG_STARTED, trình nghe sự kiện kéo sẽ thực hiện:

  1. Gọi DragEvent#getClipDescription() và sử dụng các phương thức loại MIME trong ClipDescription được trả về để xem liệu trình nghe có thể chấp nhận dữ liệu đang được kéo hay không.

    Nếu thao tác kéo và thả không để vận động và di chuyển tài liệu, thì việc này hoàn toàn có thể không thiết yếu .

  2. Nếu trình nghe sự kiện kéo có thể chấp nhận thao tác thả, thì trình nghe đó sẽ trả về true để thông báo cho hệ thống tiếp tục gửi các sự kiện kéo cho trình nghe. Nếu trình nghe không thể chấp nhận thao tác thả, trình nghe sẽ trả về false và hệ thống sẽ ngừng gửi sự kiện kéo đến trình nghe cho đến khi hệ thống gửi ACTION_DRAG_ENDED để kết thúc thao tác kéo và thả.

Lưu ý rằng đối với sự kiện ACTION_DRAG_STARTED, những phương thức DragEvent sau là không hợp lệ :getClipData(), getX(), getY()getResult().

Xử lý sự kiện trong quá trình kéo

Trong thao tác kéo, các trình nghe sự kiện kéo mà trả về true để phản hồi sự kiện kéo ACTION_DRAG_STARTED sẽ tiếp tục nhận các sự kiện kéo. Các sự kiện kéo mà trình nghe nhận được trong quá trình kéo phụ thuộc vào vị trí của bóng khi kéo và mức độ hiển thị của View của trình nghe.
Trình nghe chủ yếu sử dụng các sự kiện kéo để quyết định xem có nên thay đổi giao diện của View hay không.

Trong thao tác kéo, DragEvent#getAction() sẽ trả về một trong ba giá trị:

  • ACTION_DRAG_ENTERED: trình nghe nhận loại thao tác sự kiện này khi điểm chạm (điểm trên màn hình bên dưới ngón tay hoặc chuột của người dùng) di chuyển vào phạm vi hộp giới hạn của View của trình nghe.
  • ACTION_DRAG_LOCATION: Sau khi nhận được sự kiệnACTION_DRAG_ENTERED và trước khi nhận sự kiện ACTION_DRAG_EXITED, trình nghe sẽ nhận được một sự kiện ACTION_DRAG_LOCATION mỗi khi điểm chạm di chuyển. Phương thức getX()getY() trả về toạ độ X và Y của điểm chạm.
  • ACTION_DRAG_EXITED: Loại thao tác sự kiện này được gửi tới trình nghe từng nhận được ACTION_DRAG_ENTERED. Sự kiện được gửi khi điểm chạm bóng khi kéo di chuyển từ bên trong hộp giới hạn của View của trình nghe ra bên ngoài hộp giới hạn.

Trình nghe sự kiện kéo không cần phải phản hồi với bất kể loại thao tác nào trong số này. Nếu trình nghe trả về một giá trị cho mạng lưới hệ thống thì giá trị đó sẽ bị bỏ lỡ .
Dưới đây là một số nguyên tắc để phản hồi từng loại thao tác :

  • Để phản hồi ACTION_DRAG_ENTERED hoặc ACTION_DRAG_LOCATION, trình nghe có thể thay đổi giao diện của View để cho biết thành phần hiển thị có thể là một mục tiêu thả.
  • Sự kiện có loại thao tác ACTION_DRAG_LOCATION chứa dữ liệu hợp lệ cho getX()getY(), tương ứng với vị trí của điểm chạm. Trình nghe có thể sử dụng thông tin này để thay đổi giao diện của View tại điểm chạm hoặc để xác định vị trí chính xác mà người dùng có thể thả bóng khi kéo (tức là thả dữ liệu).
  • Để phản hồi ACTION_DRAG_EXITED, trình nghe phải đặt lại mọi thay đổi giao diện đã áp dụng cho ACTION_DRAG_ENTERED hoặc ACTION_DRAG_LOCATION. Việc này cho người dùng biết rằng View không còn là một mục tiêu thả sắp tới.

Phản hồi sự kiện thả

Khi người dùng thả bóng khi kéo qua ViewView đã báo rằng nó có thể chấp nhận nội dung đang được kéo thì hệ thống sẽ gửi một sự kiện kéo đến View với loại hành động ACTION_DROP.

Trình nghe sự kiện kéo triển khai những bước như sau :

  1. Gọi getClipData() để lấy đối tượng ClipData ban đầu được cung cấp khi gọi startDragAndDrop() và xử lý dữ liệu.

    Nếu thao tác kéo và thả không để vận động và di chuyển tài liệu, thì bạn không cần triển khai bước này .

  2. Trả về giá trị boolean true để cho biết rằng sự kiện thả đã được xử lý thành công hoặc false nếu không. Giá trị trả về trở thành giá trị mà getResult() trả về cho sự kiện ACTION_DRAG_ENDED cuối cùng.

    Lưu ý rằng nếu hệ thống không gửi sự kiện ACTION_DROP thì giá trị do getResult() trả về cho sự kiện ACTION_DRAG_ENDEDfalse.

Đối với sự kiện ACTION_DROP, getX()getY(), sử dụng hệ toạ độ của View mà nhận được sự kiện thả để trả về vị trí X và Y của điểm chạm khi thả.

Hệ thống cho phép người dùng thả bóng khi kéo trên View có trình nghe sự kiện kéo không nhận được sự kiện kéo. Hệ thống cũng cho phép người dùng thả bóng khi kéo trên các vùng trống của giao diện người dùng trong ứng dụng hoặc trên các khu vực bên ngoài ứng dụng của bạn. Trong tất cả các trường hợp này, hệ thống sẽ không gửi một sự kiện có loại thao tác là ACTION_DROP. Tuy nhiên, hệ thống sẽ gửi một sự kiện ACTION_DRAG_ENDED.

Phản hồi khi kết thúc quá trình kéo

Ngay sau khi người dùng thả bóng khi kéo, hệ thống sẽ gửi một sự kiện kéo có loại thao tác là ACTION_DRAG_ENDED cho tất cả trình nghe sự kiện kéo trong ứng dụng của bạn. Việc này cho biết rằng thao tác kéo và thả đã kết thúc.

Mỗi trình nghe sự kiện kéo triển khai những bước như sau :

  1. Nếu trình nghe thay đổi giao diện của đối tượng View trong khi thực hiện thao tác, trình nghe sẽ đặt View về lại giao diện mặc định. Đây là thông báo cho người dùng biết rằng thao tác đã kết thúc.
  2. Trình nghe có thể gọi getResult() nếu muốn để tìm hiểu thêm về thao tác. Nếu trình nghe trả về true để phản hồi một sự kiện có loại thao tác ACTION_DROP, thì getResult() sẽ trả về giá trị boolean true. Trong tất cả các trường hợp khác, getResult() sẽ trả về giá trị boolean false, bao gồm cả trường hợp hệ thống không gửi sự kiện ACTION_DROP.
  3. Để cho biết thao tác kéo và thả thành công, trình nghe phải trả về giá trị boolean true cho hệ thống.

Ví dụ về phản hồi sự kiện kéo

Tất cả sự kiện kéo sẽ được phương pháp sự kiện kéo hoặc trình nghe nhận. Đoạn mã sau đây là một ví dụ đơn thuần về việc phản hồi lại những sự kiện kéo :

Kotlin

val imageView = ImageView(this)

// Set the drag event listener for the View.
imageView.setOnDragListener { v, e ->

    // Handles each of the expected events.
    when (e.action) {
        DragEvent.ACTION_DRAG_STARTED -> {
            // Determines if this View can accept the dragged data.
            if (e.clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
                // As an example of what your application might do, applies a blue color tint
                // to the View to indicate that it can accept data.
                (v as? ImageView)?.setColorFilter(Color.BLUE)

                // Invalidate the view to force a redraw in the new tint.
                v.invalidate()

                // Returns true to indicate that the View can accept the dragged data.
                true
            } else {
                // Returns false to indicate that, during the current drag and drop operation,
                // this View will not receive events again until ACTION_DRAG_ENDED is sent.
                false
            }
        }
        DragEvent.ACTION_DRAG_ENTERED -> {
            // Applies a green tint to the View.
            (v as? ImageView)?.setColorFilter(Color.GREEN)

            // Invalidates the view to force a redraw in the new tint.
            v.invalidate()

            // Returns true; the value is ignored.
            true
        }

        DragEvent.ACTION_DRAG_LOCATION ->
            // Ignore the event.
            true
        DragEvent.ACTION_DRAG_EXITED -> {
            // Resets the color tint to blue.
            (v as? ImageView)?.setColorFilter(Color.BLUE)

            // Invalidates the view to force a redraw in the new tint.
            v.invalidate()

            // Returns true; the value is ignored.
            true
        }
        DragEvent.ACTION_DROP -> {
            // Gets the item containing the dragged data.
            val item: ClipData.Item = e.clipData.getItemAt(0)

            // Gets the text data from the item.
            val dragData = item.text

            // Displays a message containing the dragged data.
            Toast.makeText(this, "Dragged data is $dragData", Toast.LENGTH_LONG).show()

            // Turns off any color tints.
            (v as? ImageView)?.clearColorFilter()

            // Invalidates the view to force a redraw.
            v.invalidate()

            // Returns true. DragEvent.getResult() will return true.
            true
        }

        DragEvent.ACTION_DRAG_ENDED -> {
            // Turns off any color tinting.
            (v as? ImageView)?.clearColorFilter()

            // Invalidates the view to force a redraw.
            v.invalidate()

            // Does a getResult(), and displays what happened.
            when(e.result) {
                true ->
                    Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG)
                else ->
                    Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG)
            }.show()

            // Returns true; the value is ignored.
            true
        }
        else -> {
            // An unknown action type was received.
            Log.e("DragDrop Example", "Unknown action type received by View.OnDragListener.")
            false
        }
    }
}

Java

View imageView = new ImageView(this);

// Set the drag event listener for the View.
imageView.setOnDragListener( (v, e) -> {

    // Handles each of the expected events.
    switch(e.getAction()) {

        case DragEvent.ACTION_DRAG_STARTED:

            // Determines if this View can accept the dragged data.
            if (e.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {

                // As an example of what your application might do, applies a blue color tint
                // to the View to indicate that it can accept data.
                ((ImageView)v).setColorFilter(Color.BLUE);

                // Invalidate the view to force a redraw in the new tint.
                v.invalidate();

                // Returns true to indicate that the View can accept the dragged data.
                return true;

            }

            // Returns false to indicate that, during the current drag and drop operation,
            // this View will not receive events again until ACTION_DRAG_ENDED is sent.
            return false;

        case DragEvent.ACTION_DRAG_ENTERED:

            // Applies a green tint to the View.
            ((ImageView)v).setColorFilter(Color.GREEN);

            // Invalidates the view to force a redraw in the new tint.
            v.invalidate();

            // Returns true; the value is ignored.
            return true;

        case DragEvent.ACTION_DRAG_LOCATION:

            // Ignore the event.
            return true;

        case DragEvent.ACTION_DRAG_EXITED:

            // Resets the color tint to blue.
            ((ImageView)v).setColorFilter(Color.BLUE);

            // Invalidates the view to force a redraw in the new tint.
            v.invalidate();

            // Returns true; the value is ignored.
            return true;

        case DragEvent.ACTION_DROP:

            // Gets the item containing the dragged data.
            ClipData.Item item = e.getClipData().getItemAt(0);

            // Gets the text data from the item.
            CharSequence dragData = item.getText();

            // Displays a message containing the dragged data.
            Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_LONG).show();

            // Turns off any color tints.
            ((ImageView)v).clearColorFilter();

            // Invalidates the view to force a redraw.
            v.invalidate();

            // Returns true. DragEvent.getResult() will return true.
            return true;

        case DragEvent.ACTION_DRAG_ENDED:

            // Turns off any color tinting.
            ((ImageView)v).clearColorFilter();

            // Invalidates the view to force a redraw.
            v.invalidate();

            // Does a getResult(), and displays what happened.
            if (e.getResult()) {
                Toast.makeText(this, "The drop was handled.", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_LONG).show();
            }

            // Returns true; the value is ignored.
            return true;

        // An unknown action type was received.
        default:
            Log.e("DragDrop Example","Unknown action type received by View.OnDragListener.");
            break;
    }

    return false;

});

Kéo và thả ở chế độ nhiều cửa sổ

Các thiết bị chạy Android 7.0 ( API cấp 24 ) trở lên tương hỗ chính sách nhiều hành lang cửa số, được cho phép người dùng chuyển dời tài liệu từ ứng dụng này sang ứng dụng khác bằng thao tác kéo và thả ( xem nội dung Hỗ trợ nhiều hành lang cửa số ) .
Ứng dụng nguồn cung ứng tài liệu. Thao tác kéo và thả khởi đầu trong ứng dụng nguồn. Ứng dụng đích sẽ nhận được tài liệu. Thao tác kéo và thả kết thúc trong ứng dụng đích .

Khi bắt đầu thao tác kéo và thả, ứng dụng nguồn phải thiết lập cờ DRAG_FLAG_GLOBAL để cho biết người dùng có thể kéo dữ liệu sang một ứng dụng khác.

Vì tài liệu vận động và di chuyển qua ranh giới ứng dụng nên những ứng dụng san sẻ quyền truy vấn tài liệu bằng cách sử dụng URI nội dung :

  • Ứng dụng nguồn phải thiết lập một hoặc cả hai cờ DRAG_FLAG_GLOBAL_URI_READDRAG_FLAG_GLOBAL_URI_WRITE, tuỳ thuộc vào quyền đọc/ghi dữ liệu mà ứng dụng nguồn muốn cấp cho ứng dụng đích.
  • Ứ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 kéo và thả, ứng dụng có thể gọi release() trên đối tượng được trả về từ 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ỏ.

Đoạn mã sau đây minh họa cách hủy quyền chỉ hoàn toàn có thể đọc để kéo và thả tài liệu ngay sau khi thao tác kéo và thả diễn ra. Hãy xem mẫu DragAndDrop trên GitHub để tìm hiểu thêm ví dụ hoàn hảo hơn .

Hoạt động kéo và thả ở nguồn

Kotlin

// Drag a file stored in internal storage. The file is in an "images/" directory.
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 allow for 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(R.id.imageView)

// Detect and start the drag event.
DragStartHelper(srcImageView, listener).apply {
    attach()
}

Java

// Drag a file stored under 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 allow for 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 kéo và thả ở đích

Kotlin

// Container for where the image is to be dropped in the target app.
val targetImageView = findViewById(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 afterwards because it's
            // no longer needed.
            dropPermissions.release()
            return@setOnDragListener true
        }

        // Implement logic for other DragEvent cases here.

        // An unknown action type was 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 afterwards 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;
});

Kéo và thả đơn giản hơn nhờ DropHelper

Lớp DropHelper đơn giản hoá việc triển khai khả năng kéo và thả. Là một phần của thư viện Jetpack DragAndDrop, DropHelper hỗ trợ tương thích ngược đến cấp API 24.

Hãy dùng DropHelper để chỉ định các mục tiêu thả, tuỳ chỉnh việc làm nổi bật mục mục tiêu thả và xác định cách xử lý dữ liệu được thả.

Mục tiêu thả

DropHelper#configureView() là một phương thức tĩnh, nạp chồng cho phép bạn chỉ định các mục tiêu thả (drop target). Gồm có các tham số sau:

  • Activity hiện tại (dùng cho quyền URI)
  • View đóng vai trò là mục tiêu thả
  • Loại MIME mà mục tiêu thả có thể chấp nhận từ dữ liệu được thả
  • Các tuỳ chọn cấu hình cho mục tiêu thả (cụ thể là danh sách các trường EditText được nhúng)
  • OnReceiveContentListener để xử lý dữ liệu được thả

Ví dụ : để tạo tiềm năng thả đồng ý hình ảnh, hãy sử dụng một trong những lệnh gọi phương pháp sau :

Kotlin

configureView(
    myActivity,
    targetView,
    arrayOf("image/*"),
    options,
    onReceiveContentListener)

// or

configureView(
    myActivity,
    targetView,
    arrayOf("image/*"),
    onReceiveContentListener)

Java

DropHelper.configureView(
    myActivity,
    targetView,
    new String[] {"image/*"},
    options,
    onReceiveContentlistener);

// or

DropHelper.configureView(
    myActivity,
    targetView,
    new String[] {"image/*"},
    onReceiveContentlistener);

Lệnh gọi thứ hai bỏ lỡ những tùy chọn thông số kỹ thuật tiềm năng thả, trong trường hợp này, màu dùng để làm điển hình nổi bật tiềm năng thả được đặt thành màu chủ đề phụ ( hoặc màu nhấn ), nửa đường kính góc của tiềm năng thả được làm điển hình nổi bật sẽ được đặt thành 16 dp và list EditTexts sẽ trống ( xem nội dung Cấu hình tiềm năng thả dưới đây ) .

Cấu hình mục tiêu thả

Lớp DropHelper.Options cho phép bạn định cấu hình các mục tiêu thả. Bạn cung cấp một thực thể (instance) của lớp cho phương thức
DropHelper.configureView(Activity, View, String[], Options,
OnReceiveContentListener)
(xem mục Mục tiêu thả ở trên).

Đánh dấu mục tiêu thả

DropHelper định cấu hình các mục tiêu thả để hiển thị vùng nổi bật khi người dùng kéo nội dung lên trên mục tiêu thả. DropHelper cung cấp kiểu mặc định, nhưng DropHelper.Options cho phép bạn đặt màu của vùng nổi bật và chỉ định bán kính góc của hình chữ nhật được làm nổi bật.

Sử dụng lớp DropHelper.Options.Builder để tạo một thực thể DropHelper.Options và đặt các tuỳ chọn cấu hình, ví dụ:

Kotlin

val options: DropHelper.Options = DropHelper.Options.Builder()
                                      .setHighlightColor(getColor(R.color.purple_300))
                                      .setHighlightCornerRadiusPx(resources.getDimensionPixelSize(R.dimen.drop_target_corner_radius))
                                      .build()

Java

DropHelper.Options options = new DropHelper.Options.Builder()
                                     .setHighlightColor(getColor(R.color.purple_300))
                                     .setHighlightCornerRadiusPx(getResources().getDimensionPixelSize(R.dimen.drop_target_corner_radius))
                                     .build();

Các thành phần EditText trong mục tiêu thả

DropHelper cũng kiểm soát tiêu điểm trong mục tiêu thả khi mục tiêu chứa các trường văn bản có thể chỉnh sửa.

Mục tiêu thả có thể là một thành phần hiển thị hoặc hệ phân cấp thành phần hiển thị. Nếu hệ phân cấp thành phần hiển thị mục tiêu thả chứa một hoặc nhiều thành phần EditText, bạn phải cung cấp danh sách các thành phần cho DropHelper.Options.Builder#addInnerEditTexts(EditText...) để đảm bảo rằng mục tiêu thả được làm nổi bật và việc xử lý dữ liệu văn bản hoạt động chính xác.

DropHelper ngăn chặn các thành phần EditText trong hệ phân cấp thành phần hiển thị mục tiêu thả lấy cắp tiêu điểm từ thành phần hiển thị chứa trong các tương tác kéo.

Ngoài ra, nếu thao tác kéo và thả ClipData bao gồm dữ liệu văn bản và URI thì DropHelper sẽ chọn một trong các thành phần EditText trong mục tiêu thả để xử lý dữ liệu văn bản. Việc lựa chọn được dựa trên thứ tự ưu tiên sau:

  1. EditText nơi ClipData được thả
  2. EditText chứa con trỏ văn bản (con nháy)
  3. EditText đầu tiên được cung cấp khi gọi DropHelper.Options.Builder#addInnerEditTexts(EditText...)

Để đặt EditText làm trình xử lý dữ liệu văn bản mặc định, hãy truyền EditText làm đối số đầu tiên của lệnh gọi tới DropHelper.Options.Builder#addInnerEditTexts(EditText...). Ví dụ: nếu mục tiêu thả của bạn xử lý hình ảnh nhưng chứa các trường văn bản có thể chỉnh sửa T1, T2T3, hãy đặt T2 làm mặc định như sau:

Kotlin

val options: DropHelper.Options = DropHelper.Options.Builder()
                                      .addInnerEditTexts(T2, T1, T3)
                                      .build()

Java

DropHelper.Options options = new DropHelper.Options.Builder()
                                     .addInnerEditTexts(T2, T1, T3)
                                     .build();

Xử lý dữ liệu mục tiêu thả

Phương thức DropHelper#configureView() chấp nhận OnReceiveContentListener mà bạn tạo để xử lý thao tác kéo và thả ClipData. Dữ liệu kéo và thả được cung cấp cho trình nghe trong đối tượng ContentInfoCompat.
Dữ liệu văn bản đã có trong đối tượng; nội dung đa phương tiện, chẳng hạn như hình ảnh, được biểu thị bằng URI.

OnReceiveContentListener cũng xử lý dữ liệu dữ liệu được cung cấp cho mục tiêu thả do tương tác của người dùng bên cạnh dữ liệu kéo và thả (chẳng hạn như sao chép và dán) khi DropHelper#configureView() được sử dụng để định cấu hình các loại thành phần hiển thị sau đây:

  • Tất cả thành phần hiển thị nếu người dùng đang chạy Android 12 trở lên
  • AppCompatEditText xuống Android 7.0

Cảnh báo:DropHelper định cấu hình mục tiêu thả để lắng nghe các sự kiện kéo và thả và xử lý dữ liệu được thả. Đừng đính kèm DropHelper.

định cấu hình mục tiêu thả để lắng nghe các sự kiện kéo và thả và xử lý dữ liệu được thả. Đừng đính kèm View.OnDragListener hoặc OnReceiveContentListener cho mục tiêu thả khi sử dụng

Lưu ý:DropHelper hỗ trợ giao diện DropHelper không hỗ trợ giao diện nền tảng

hỗ trợ giao diện OnReceiveContentListener Jetpack tương thích ngược đến API cấp 24.không hỗ trợ giao diện nền tảng OnReceiveContentListener được giới thiệu trong API cấp 31.

Loại MIME, quyền và xác thực nội dung

Quá trình kiểm tra loại MIME của DropHelper dựa trên ClipDescription kéo và thả được tạo bởi ứng dụng cung cấp dữ liệu kéo và thả. Bạn nên xác thực ClipDescription để đảm bảo rằng bạn đã thiết lập đúng loại MIME.

DropHelper yêu cầu tất cả các quyền truy cập cho các URI nội dung có trong ClipData kéo và thả (xem DragAndDropPermissions). Các quyền này cho phép bạn phân giải các URI nội dung khi xử lý dữ liệu kéo và thả.

DropHelper không xác thực dữ liệu do nhà cung cấp nội dung trả về khi phân giải các URI trong dữ liệu được thả. Bạn nên kiểm tra xem dữ liệu có rỗng (null) hay không và xác minh tính chính xác của mọi dữ liệu đã phân giải.

Tài nguyên khác

Source: https://vh2.com.vn
Category : Tin Học