Anda dapat menerapkan proses tarik lalu lepas dalam tampilan dengan merespons peristiwa yang dapat memicu awal tarik lalu respons dan penggunaan peristiwa lepas.
Mulai operasi tarik
Pengguna memulai {i>drag<i} dengan {i>gesture<i}, biasanya dengan menyentuh atau mengeklik dan menyimpan item yang ingin mereka tarik.
Untuk menangani hal ini di View
, buat
Objek ClipData
dan
Objek ClipData.Item
untuk
data yang dipindahkan. Sebagai bagian dari ClipData
, berikan metadata yang
disimpan di
Objek ClipDescription
dalam ClipData
. Untuk operasi tarik lalu lepas yang tidak mewakili
perpindahan data, sebaiknya gunakan null
daripada objek yang sebenarnya.
Misalnya, cuplikan kode ini menunjukkan cara merespons gestur sentuh lama
di ImageView
dengan membuat objek ClipData
yang berisi
tag (atau label) ImageView
.
Kotlin
// Create a string for the ImageView label. val IMAGEVIEW_TAG = "icon bitmap" ... val imageView = ImageView(context).apply { // Set the bitmap for the ImageView from an icon bitmap 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. We use this imageView object // to create the default builder. val myShadow = View.DragShadowBuilder(view: 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 is 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(context); // Set the bitmap for the ImageView from an icon bitmap defined elsewhere. imageView.setImageBitmap(iconBitmap); // Set the tag. imageView.setTag(IMAGEVIEW_TAG); // Set 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. We use this imageView object // to create the default builder. View.DragShadowBuilder myShadow = new View.DragShadowBuilder(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 is handled. return true; });
Merespons awal operasi tarik
Selama operasi tarik, sistem mengirim peristiwa tarik ke pemroses
peristiwa tarik untuk objek View
dalam tata letak saat ini. Pemroses bereaksi dengan
memanggil DragEvent.getAction()
untuk mendapatkan jenis tindakan. Di awal {i>drag<i},
metode ini akan menampilkan ACTION_DRAG_STARTED
.
Sebagai respons terhadap peristiwa dengan jenis tindakan ACTION_DRAG_STARTED
, peristiwa tarik
pemroses harus melakukan hal berikut:
Telepon
DragEvent.getClipDescription()
dan gunakan metode jenis MIME dalamClipDescription
yang ditampilkan untuk melihat apakah pemroses dapat menerima data yang sedang ditarik.Jika operasi tarik lalu lepas tidak mewakili perpindahan data, hal ini mungkin tidak diperlukan.
Jika pemroses peristiwa tarik dapat menerima peristiwa lepas, pemroses harus menampilkan
true
untuk memberi tahu sistem untuk terus mengirim kejadian seret ke pemroses. Jika pemroses tidak dapat menerima pelepasan, pemroses harus menampilkanfalse
, dan sistem akan berhenti mengirimkan kejadian seret ke pemroses hingga sistem mengirimACTION_DRAG_ENDED
untuk mengakhiri operasi tarik lalu lepas.
Untuk peristiwa ACTION_DRAG_STARTED
, metode DragEvent
berikut tidak
valid: getClipData()
,
getX()
,
getY()
, dan
getResult()
.
Menangani peristiwa selama operasi tarik
Selama tindakan tarik, pemroses peristiwa tarik yang menampilkan true
sebagai respons terhadap
peristiwa tarik ACTION_DRAG_STARTED
terus menerima peristiwa tarik. Jenis
peristiwa tarik yang diterima pemroses selama operasi tarik bergantung pada lokasi
bayangan tarik dan visibilitas View
pemroses. Pemroses menggunakan fungsi tarik
peristiwa utama untuk memutuskan apakah peristiwa tersebut harus mengubah tampilan View
.
Selama operasi tarik, DragEvent.getAction()
menampilkan satu dari tiga nilai berikut:
ACTION_DRAG_ENTERED
: pemroses menerima jenis tindakan peristiwa ini saat titik sentuh— menunjuk pada layar di bawah jari atau {i>mouse<i} pengguna—memasukkan kotak pembatasView
pemroses.ACTION_DRAG_LOCATION
: setelah pemroses menerima peristiwaACTION_DRAG_ENTERED
, pemroses akan menerima peristiwa PeristiwaACTION_DRAG_LOCATION
setiap kali titik sentuh bergerak hingga titik sentuh tersebut menerima peristiwaACTION_DRAG_EXITED
. MetodegetX()
dangetY()
mengembalikan koordinat X dan Y dari titik sentuh tersebut.ACTION_DRAG_EXITED
: jenis tindakan peristiwa ini dikirim ke pemroses yang sebelumnya menerimaACTION_DRAG_ENTERED
. Peristiwa ini dikirim saat titik sentuh bayangan tarik berpindah dari dalam kotak pembatasView
pemroses ke luar kotak pembatas.
Pemroses peristiwa tarik tidak perlu bereaksi terhadap salah satu jenis tindakan ini. Jika pemroses mengembalikan nilai ke sistem, nilai itu akan diabaikan.
Inilah beberapa panduan untuk merespons setiap tipe aksi ini:
- Sebagai respons terhadap
ACTION_DRAG_ENTERED
atauACTION_DRAG_LOCATION
, pemroses dapat mengubah tampilanView
untuk menunjukkan bahwa tampilan adalah potensi target lepas. - Peristiwa dengan jenis tindakan
ACTION_DRAG_LOCATION
berisi data yang valid untukgetX()
dangetY()
yang sesuai dengan lokasi titik sentuh. Tujuan pemroses dapat menggunakan informasi ini untuk mengubah tampilanView
di atau menentukan posisi persis tempat pengguna dapat melepaskan saat ini. - Sebagai respons terhadap
ACTION_DRAG_EXITED
, pemroses harus mereset semua tampilan perubahan yang diterapkan sebagai respons terhadapACTION_DRAG_ENTERED
atauACTION_DRAG_LOCATION
. Hal ini menunjukkan kepada pengguna bahwaView
tersebut tidak lagi menjadi target operasi lepas yang akan segera terjadi.
Merespons operasi lepas
Saat pengguna merilis bayangan tarik di atas View
, dan View
sebelumnya
melaporkan bahwa ia dapat menerima konten yang ditarik, sistem akan mengirimkan
peristiwa tarik ke View
dengan jenis tindakan ACTION_DROP
.
Pemroses peristiwa tarik harus melakukan hal berikut:
Panggil
getClipData()
untuk mendapatkan objekClipData
yang awalnya yang disediakan dalam panggilan kestartDragAndDrop()
dan memproses data. Jika operasi tarik lalu lepas tidak mewakili data gerakan, ini tidak diperlukan.Menampilkan boolean
true
untuk menunjukkan bahwa operasi lepas berhasil diproses, ataufalse
jika tidak. Nilai yang ditampilkan menjadi nilai yang ditampilkan olehgetResult()
untuk peristiwaACTION_DRAG_ENDED
akhir. Jika sistem tidak mengirimkan peristiwaACTION_DROP
, nilai yang ditampilkan olehgetResult()
untuk peristiwaACTION_DRAG_ENDED
adalahfalse
.
Untuk peristiwa ACTION_DROP
, getX()
dan getY()
menggunakan sistem koordinat
View
yang menerima penurunan untuk menampilkan posisi X dan Y dari
kontak pelanggan pada saat penurunan.
Meskipun pengguna dapat melepaskan bayangan tarik di atas View
yang peristiwa tariknya
pemroses tidak menerima peristiwa tarik, area kosong pada UI aplikasi, atau bahkan
pada area di luar aplikasi, Android tidak akan mengirimkan peristiwa dengan tindakan
ketik ACTION_DROP
dan hanya akan mengirim peristiwa ACTION_DRAG_ENDED
.
Merespons akhir operasi tarik
Segera setelah pengguna melepaskan bayangan penyeretan, sistem akan mengirimkan
peristiwa dengan jenis tindakan ACTION_DRAG_ENDED
ke semua pemroses peristiwa tarik
dalam aplikasi Anda. Ini menandakan bahwa operasi seret telah selesai.
Setiap pemroses peristiwa tarik harus melakukan hal berikut:
- Jika pemroses mengubah tampilannya selama operasi, pemroses harus direset kembali ke tampilan {i>default<i}nya sebagai indikasi visual kepada pengguna bahwa selesai.
- Jika diinginkan, pemroses dapat memanggil
getResult()
untuk mengetahui operasi tersebut lebih lanjut. Jika pemroses menampilkantrue
sebagai respons terhadap peristiwa tindakan jenisACTION_DROP
, lalugetResult()
akan menampilkan booleantrue
. Di semua properti lainnya kasus,getResult()
akan menampilkan booleanfalse
, termasuk saat sistem tidak mengirim peristiwaACTION_DROP
. - Untuk menunjukkan keberhasilan penyelesaian operasi pelepasan, pemroses
akan menampilkan boolean
true
ke sistem. Dengan tidak menampilkanfalse
, isyarat visual yang menunjukkan {i>drop shadow<i} yang kembali ke sumbernya mungkin menyarankan pengguna bahwa operasi itu gagal.
Merespons peristiwa tarik: Contoh
Semua peristiwa tarik diterima oleh metode atau pemroses peristiwa tarik. Tujuan cuplikan kode berikut adalah contoh respons terhadap peristiwa tarik:
Kotlin
val imageView = ImageView(this) // Set the drag event listener for the View. imageView.setOnDragListener { v, e -> // Handle each of the expected events. when (e.action) { DragEvent.ACTION_DRAG_STARTED -> { // Determine whether this View can accept the dragged data. if (e.clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { // As an example, apply 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() // Return true to indicate that the View can accept the dragged // data. true } else { // Return false to indicate that, during the current drag and // drop operation, this View doesn't receive events again until // ACTION_DRAG_ENDED is sent. false } } DragEvent.ACTION_DRAG_ENTERED -> { // Apply a green tint to the View. (v as? ImageView)?.setColorFilter(Color.GREEN) // Invalidate the view to force a redraw in the new tint. v.invalidate() // Return true. The value is ignored. true } DragEvent.ACTION_DRAG_LOCATION -> // Ignore the event. true DragEvent.ACTION_DRAG_EXITED -> { // Reset the color tint to blue. (v as? ImageView)?.setColorFilter(Color.BLUE) // Invalidate the view to force a redraw in the new tint. v.invalidate() // Return true. The value is ignored. true } DragEvent.ACTION_DROP -> { // Get the item containing the dragged data. val item: ClipData.Item = e.clipData.getItemAt(0) // Get the text data from the item. val dragData = item.text // Display a message containing the dragged data. Toast.makeText(this, "Dragged data is $dragData", Toast.LENGTH_LONG).show() // Turn off color tints. (v as? ImageView)?.clearColorFilter() // Invalidate the view to force a redraw. v.invalidate() // Return true. DragEvent.getResult() returns true. true } DragEvent.ACTION_DRAG_ENDED -> { // Turn off color tinting. (v as? ImageView)?.clearColorFilter() // Invalidate the view to force a redraw. v.invalidate() // Do a getResult() and display what happens. 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() // Return true. The value is ignored. true } else -> { // An unknown action type is 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) -> { // Handle each of the expected events. switch(e.getAction()) { case DragEvent.ACTION_DRAG_STARTED: // Determine whether this View can accept the dragged data. if (e.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { // As an example, apply 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(); // Return true to indicate that the View can accept the dragged // data. return true; } // Return false to indicate that, during the current drag-and-drop // operation, this View doesn't receive events again until // ACTION_DRAG_ENDED is sent. return false; case DragEvent.ACTION_DRAG_ENTERED: // Apply a green tint to the View. ((ImageView)v).setColorFilter(Color.GREEN); // Invalidate the view to force a redraw in the new tint. v.invalidate(); // Return true. The value is ignored. return true; case DragEvent.ACTION_DRAG_LOCATION: // Ignore the event. return true; case DragEvent.ACTION_DRAG_EXITED: // Reset the color tint to blue. ((ImageView)v).setColorFilter(Color.BLUE); // Invalidate the view to force a redraw in the new tint. v.invalidate(); // Return true. The value is ignored. return true; case DragEvent.ACTION_DROP: // Get the item containing the dragged data. ClipData.Item item = e.getClipData().getItemAt(0); // Get the text data from the item. CharSequence dragData = item.getText(); // Display a message containing the dragged data. Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_LONG).show(); // Turn off color tints. ((ImageView)v).clearColorFilter(); // Invalidate the view to force a redraw. v.invalidate(); // Return true. DragEvent.getResult() returns true. return true; case DragEvent.ACTION_DRAG_ENDED: // Turn off color tinting. ((ImageView)v).clearColorFilter(); // Invalidate the view to force a redraw. v.invalidate(); // Do a getResult() and displays what happens. 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(); } // Return true. The value is ignored. return true; // An unknown action type is received. default: Log.e("DragDrop Example","Unknown action type received by View.OnDragListener."); break; } return false; });
Menyesuaikan bayangan tarik
Anda dapat menentukan myDragShadowBuilder
yang disesuaikan dengan mengganti metode di
View.DragShadowBuilder
. Cuplikan kode berikut membuat instance
bayangan tarik persegi panjang abu-abu untuk TextView
:
Kotlin
private class MyDragShadowBuilder(view: View) : View.DragShadowBuilder(view) { private val shadow = ColorDrawable(Color.LTGRAY) // Define 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. Set 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) } // Define 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 view) { // Store the View parameter. super(view); // Create a draggable image that fills the Canvas provided by the // system. shadow = new ColorDrawable(Color.LTGRAY); } // Define a callback that sends the drag shadow dimensions and touch point // back to the system. @Override public void onProvideShadowMetrics (Point size, Point touch) { // Define 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. Set 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); } // Define 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); } }