Puoi implementare il processo di trascinamento nelle visualizzazioni rispondendo agli eventi che potrebbero attivare un inizio trascinamento, rispondere e consumare eventi di caduta.
Avvia un trascinamento
L'utente avvia un trascinamento con un gesto, solitamente toccando o facendo clic e tenendo premuto su un elemento da trascinare.
Per gestirla in un View
, crea un
ClipData
e
ClipData.Item
per
dei dati che vengono spostati. Nell'ambito di ClipData
, fornisci i metadati che
archiviati in un
Oggetto ClipDescription
all'interno di ClipData
. Per un'operazione di trascinamento che non rappresenta
movimento di dati, potresti utilizzare null
al posto di un oggetto effettivo.
Ad esempio, questo snippet di codice mostra come rispondere a un tocco e tieni premuto
gesto su ImageView
creando un oggetto ClipData
che contiene
tag (o etichetta) di 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; });
Rispondere a un inizio trascinamento
Durante l'operazione di trascinamento, il sistema invia eventi di trascinamento all'evento di trascinamento.
listener degli oggetti View
nel layout corrente. Gli ascoltatori reagiscono
chiamata DragEvent.getAction()
per ottenere il tipo di azione. All'inizio di una trascinamento,
questo metodo restituisce ACTION_DRAG_STARTED
.
In risposta a un evento con il tipo di azione ACTION_DRAG_STARTED
, un evento di trascinamento
listener deve eseguire le seguenti operazioni:
Chiama
DragEvent.getClipDescription()
e utilizza i metodi di tipo MIME nel fileClipDescription
restituito per vedere se il listener può accettare i dati trascinati.Se l'operazione di trascinamento non rappresenta lo spostamento dei dati, inutili.
Se il listener di eventi di trascinamento può accettare un calo, deve restituire
true
per indicare al sistema per continuare a inviare eventi di trascinamento al listener. Se il listener non può accettare un drop, il listener deve restituirefalse
e il sistema si arresta inviare eventi di trascinamento al listener finché il sistema non inviaACTION_DRAG_ENDED
per concludere l'operazione di trascinamento.
Per un evento ACTION_DRAG_STARTED
, i seguenti metodi DragEvent
non sono
validità: getClipData()
,
getX()
,
getY()
e
getResult()
.
Gestire gli eventi durante il trascinamento
Durante l'azione di trascinamento, trascina i listener di eventi che restituiscono true
in risposta a.
l'evento di trascinamento ACTION_DRAG_STARTED
continua a ricevere eventi di trascinamento. I tipi
di eventi di trascinamento ricevuti da un listener durante il trascinamento dipendono dalla posizione
l'ombra di trascinamento e la visibilità dell'elemento View
del listener. I listener utilizzano la trascinamento
eventi principalmente per decidere se è necessario modificare l'aspetto di View
.
Durante l'azione di trascinamento, DragEvent.getAction()
restituisce uno di tre valori:
ACTION_DRAG_ENTERED
: il listener riceve questo tipo di azione evento quando il punto di contatto, punto sullo schermo sotto il dito o il mouse dell'utente: entra riquadro di delimitazione del valoreView
del listener.ACTION_DRAG_LOCATION
: quando riceve un eventoACTION_DRAG_ENTERED
, il listener riceve un nuovoACTION_DRAG_LOCATION
evento ogni volta che il punto di contatto si muove fino a quando non si muove riceve un eventoACTION_DRAG_EXITED
. MetodigetX()
egetY()
restituiscono le coordinate X e Y del punto di contatto.ACTION_DRAG_EXITED
: questo tipo di azione evento viene inviato a un listener che in precedenza riceveACTION_DRAG_ENTERED
. L'evento viene inviato quando il punto di contatto dell'ombra di trascinamento viene inviato si sposta dall'interno del riquadro di delimitazione dell'elementoView
del listener all'esterno riquadro di delimitazione.
Il listener di eventi di trascinamento non deve reagire a nessuno di questi tipi di azioni. Se il listener restituisce un valore al sistema e questo viene ignorato.
Di seguito sono riportate alcune linee guida per rispondere a ciascuno di questi tipi di azione:
- In risposta a
ACTION_DRAG_ENTERED
oACTION_DRAG_LOCATION
, l'ascoltatore puoi modificare l'aspetto diView
per indicare che la visualizzazione è di un potenziale calo. - Un evento con il tipo di azione
ACTION_DRAG_LOCATION
contiene dati validi pergetX()
egetY()
corrispondenti alla posizione del punto di contatto. La il listener può utilizzare queste informazioni per modificare l'aspetto diView
al punto di contatto o per determinare la posizione esatta in cui l'utente può rilasciare contenuti. - In risposta a
ACTION_DRAG_EXITED
, il listener deve reimpostare qualsiasi aspetto modifiche che si applica in risposta aACTION_DRAG_ENTERED
oACTION_DRAG_LOCATION
. Questo indica all'utente cheView
non è un obiettivo imminente di lancio.
Rispondere a un calo
Quando l'utente rilascia l'ombra di trascinamento su View
e View
in precedenza
segnala che può accettare il contenuto trascinato, il sistema invia una
trascina l'evento in View
con il tipo di azione ACTION_DROP
.
Il listener di eventi di trascinamento deve:
Chiama
getClipData()
per ottenere l'oggettoClipData
originale fornite nella chiamata astartDragAndDrop()
: ed elaborare i dati. Se l'operazione di trascinamento non rappresenta i dati movimento, questo non è necessario.Restituisce il valore booleano
true
per indicare che la riduzione viene elaborata correttamente, ofalse
se non lo è. Il valore restituito diventa il valore restituito dagetResult()
per l'eventuale eventoACTION_DRAG_ENDED
. Se il sistema non invia un eventoACTION_DROP
, il valore restituito dagetResult()
per un eventoACTION_DRAG_ENDED
èfalse
.
Per un evento ACTION_DROP
, getX()
e getY()
utilizzano il sistema di coordinate di
View
che riceve il calo per restituire le posizioni X e Y della
il punto di contatto nel momento del lancio.
L'utente è in grado di rilasciare l'ombra di trascinamento su un View
il cui evento di trascinamento
listener non riceve eventi di trascinamento, aree vuote dell'UI dell'app o addirittura
in aree esterne alla tua applicazione, Android non invia un evento con azione
digita ACTION_DROP
e invierà solo un evento ACTION_DRAG_ENDED
.
Rispondere a un trascinamento
Subito dopo che l'utente rilascia l'ombra di trascinamento, il sistema invia un trascinamento
evento con un tipo di azione ACTION_DRAG_ENDED
a tutti i listener di eventi di trascinamento
nella tua applicazione. Questo indica che l'operazione di trascinamento è terminata.
Ogni listener di eventi di trascinamento deve:
- Se il listener cambia il proprio aspetto durante l'operazione, deve essere reimpostato all'aspetto predefinito come indicazione visiva per l'utente che l'operazione è terminata.
- Facoltativamente, il listener può chiamare
getResult()
per saperne di più sul operativa. Se un listener restituiscetrue
in risposta a un evento di azione digitaACTION_DROP
, quindigetResult()
restituisce il valore booleanotrue
. In tutte le altre casi,getResult()
restituisce il valore booleanofalse
, anche quando il sistema non invia un eventoACTION_DROP
. - Per indicare il corretto completamento dell'operazione di rilascio, il listener
dovrebbe restituire il valore booleano
true
al sistema. Se non restituiscifalse
, un il segnale visivo che mostra l'ombra che torna all'origine potrebbe suggerire di all'utente che l'operazione non è riuscita.
Esempio di risposta a eventi di trascinamento
Tutti gli eventi di trascinamento vengono ricevuti dal metodo o listener di eventi di trascinamento. La Il seguente snippet di codice è un esempio di risposta agli eventi di trascinamento:
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; });
Personalizzare un'ombra di trascinamento
Puoi definire un valore myDragShadowBuilder
personalizzato eseguendo l'override dei metodi in
View.DragShadowBuilder
. Il seguente snippet di codice crea un piccolo
Ombra di trascinamento grigia e rettangolare per 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); } }