Simulacrum, từ simulacrum Latin, là một sự bắt chước, giả mạo hoặc hư cấu. Khái niệm này được liên kết với mô phỏng, đó là hành động mô phỏng .Một...
Kéo và thả | Android Developers
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.
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()
.
Bạn đang đọc: Kéo và thả | Android Developers
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ọiOnReceiveContentListener
. -
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()
và View.OnDragListener
sẽ tương tự như cách kết hợp onTouchEvent()
và 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ượngView
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ácACTION_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()
- 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ácACTION_DRAG_ENTERED
), thì trình nghe có thể phản ứng bằng cách làm nổi bậtView
. - 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ácACTION_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ọistartDragAndDrop()
đã bắt đầu thao tác. Trình nghe dự kiến sẽ trả về booleantrue
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éoACTION_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ácACTION_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ệnACTION_DROP
.
của thành phần hiển thị.
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
.
Loại thao tác | Ý nghĩa |
---|---|
ACTION_DRAG_STARTED |
Ứng dụng gọi
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 |
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ả.
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()
và 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ượngView
trong đối tượngView.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ượngView.DragShadowBuilder
(trường này được đặt giá trị lànull
). Bạn phải mở rộngView.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àox
và chiều cao được truyền vàoy
. - 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àox
và vị trí Y được truyền vàoy
.
onDrawShadow()
-
Ngay sau khi gọi
onProvideShadowMetrics()
, hệ thống sẽ gọionDrawShadow()
để tạo bóng khi kéo. Phương thức này có một đối số duy nhất, đối tượngCanvas
mà hệ thống tạo từ tham số bạn cung cấp trongonProvideShadowMetrics()
.
Phương thức này sẽ kéo bóng khi kéo trênCanvas
đượ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:
-
Tạo một đối tượng
ClipData
và đối tượngClipData.Item
cho dữ liệu đang được di chuyển. Là một phần củaClipData
, cung cấp siêu dữ liệu được lưu trữ trong đối tượngClipDescription
trongClipData
. Đố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ụngnull
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ượngClipData
chứa thẻ (hoặc nhãn) củaImageView
: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; });
-
Đoạn mã sau đây định nghĩa
myDragShadowBuilder
bằng cách ghi đè các phương thức trongView.DragShadowBuilder
. Mã này tạo một bóng khi kéo hình chữ nhật nhỏ màu xám choTextView
: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ạoView.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:
-
Gọi
DragEvent#getClipDescription()
và sử dụng các phương thức loại MIME trongClipDescription
đượ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 .
-
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ửiACTION_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()
và 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ủaView
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ệnACTION_DRAG_EXITED
, trình nghe sẽ nhận được một sự kiệnACTION_DRAG_LOCATION
mỗi khi điểm chạm di chuyển. Phương thứcgetX()
và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 đượcACTION_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ủaView
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ặcACTION_DRAG_LOCATION
, trình nghe có thể thay đổi giao diện củaView
để 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ệ chogetX()
và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ủaView
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 choACTION_DRAG_ENTERED
hoặcACTION_DRAG_LOCATION
. Việc này cho người dùng biết rằngView
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 View
và View
đã 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 :
-
Gọi
getClipData()
để lấy đối tượngClipData
ban đầu được cung cấp khi gọistartDragAndDrop()
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 .
-
Trả về giá trị boolean
true
để cho biết rằng sự kiện thả đã được xử lý thành công hoặcfalse
nếu không. Giá trị trả về trở thành giá trị màgetResult()
trả về cho sự kiệnACTION_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ị dogetResult()
trả về cho sự kiệnACTION_DRAG_ENDED
làfalse
.
Đối với sự kiện ACTION_DROP
, getX()
và 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 :
- 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ẽ đặtView
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. - 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ácACTION_DROP
, thìgetResult()
sẽ trả về giá trị booleantrue
. Trong tất cả các trường hợp khác,getResult()
sẽ trả về giá trị booleanfalse
, bao gồm cả trường hợp hệ thống không gửi sự kiệnACTION_DROP
. - Để 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_READ
vàDRAG_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ọirelease()
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
(xem mục Mục tiêu thả ở trên).
DropHelper.configureView(Activity, View, String[], Options,
OnReceiveContentListener)
Đá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:
EditText
nơiClipData
được thảEditText
chứa con trỏ văn bản (con nháy)EditText
đầu tiên được cung cấp khi gọiDropHelper.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
, T2
và T3
, 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