Proces przeciągania i upuszczania możesz zaimplementować w widokach, odpowiadając na wydarzenia które mogą powodować przeciąganie, reagowanie i zużywanie zdarzeń upuszczania.
Rozpocznij przeciąganie
Użytkownik rozpoczyna przeciąganie gestem, zwykle przez dotknięcie lub kliknięcie i przytrzymuje element, który chcesz przeciągnąć.
Aby obsłużyć tę funkcję w View
, utwórz
ClipData
i
Obiekt ClipData.Item
dla
przenoszone dane. W ramach zasady ClipData
prześlij metadane, które są
przechowywane w
ClipDescription
obiekt
w: ClipData
. W przypadku operacji „przeciągnij i upuść”, która nie odzwierciedla
przenoszenia danych, lepiej użyć obiektu null
zamiast rzeczywistego obiektu.
Na przykład ten fragment kodu pokazuje, jak zareagować na kliknięcie & przytrzymaj
aby wykonać gest na ImageView
, tworząc obiekt ClipData
zawierający
tag (lub etykieta) elementu 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; });
Reaguj na rozpoczęcie przeciągania
Podczas operacji przeciągania system wysyła zdarzenia przeciągania do zdarzenia przeciągania
detektory obiektów View
w bieżącym układzie. Reakcja słuchaczy
wywołaj DragEvent.getAction()
, aby uzyskać typ działania. Na początku przeciągania
ta metoda zwraca ACTION_DRAG_STARTED
.
W odpowiedzi na zdarzenie o typie działania ACTION_DRAG_STARTED
– zdarzenie przeciągania
detektor musi wykonać te czynności:
Zadzwoń do nas
DragEvent.getClipDescription()
i użyj metod typu MIME w zwróconym obiekcieClipDescription
, aby sprawdzić, czy odbiornik może zaakceptować przeciągane dane.Jeśli operacja „przeciągnij i upuść” nie odzwierciedla przenoszenia danych, może to spowodować być zbędne.
Jeśli detektor zdarzeń przeciągania może zaakceptować upuszczanie, musi zwrócić wartość
true
, aby poinformować system, aby nadal wysyłał zdarzenia przeciągania do detektora. Jeśli detektor nie może zaakceptować upadku, detektor musi zwrócić wartośćfalse
, a system zatrzyma wysyła zdarzenia przeciągania do detektora, dopóki system nie wyśleACTION_DRAG_ENDED
, aby zakończyć operację przeciągania i upuszczania.
W przypadku zdarzenia ACTION_DRAG_STARTED
te metody DragEvent
nie są
prawidłowe: getClipData()
,
getX()
,
getY()
i
getResult()
.
Obsługa zdarzeń podczas przeciągania
Podczas przeciągania przeciągaj detektory zdarzeń, które zwracają parametr true
w odpowiedzi na
zdarzenia przeciągania ACTION_DRAG_STARTED
nadal będą otrzymywać zdarzenia przeciągania. Typy
zdarzeń przeciągania, które detektor odbiera podczas przeciągania, zależy od lokalizacji elementu
przeciągnąć cień i widoczność elementu View
słuchacza. Słuchacze używają „przeciągnij”,
wydarzeń, głównie wtedy, gdy decydują, czy muszą zmienić wygląd panelu View
.
Podczas przeciągania funkcja DragEvent.getAction()
zwraca jedną z 3 wartości:
ACTION_DRAG_ENTERED
: detektor odbiera ten typ działania zdarzenia, gdy punkt styczności z klientem na ekranie pod palcem lub myszą użytkownika – powoduje ramki ograniczającej elementuView
detektora.ACTION_DRAG_LOCATION
: gdy detektor odbierze zdarzenieACTION_DRAG_ENTERED
, otrzyma noweACTION_DRAG_LOCATION
zdarzenie za każdym razem, gdy punkt kontaktu przesuwa się do niego odbiera zdarzenieACTION_DRAG_EXITED
. MetodygetX()
igetY()
zwraca współrzędne X i Y punktu styku.ACTION_DRAG_EXITED
: ten typ działania zdarzenia jest wysyłany do detektora, który wcześniej odbierałACTION_DRAG_ENTERED
Zdarzenie jest wysyłane, gdy punkt kontaktu cienia przeciągania przechodzi z ramki ograniczającej wView
detektora do poza ramkę ograniczającą.
Detektor zdarzeń przeciągania nie musi reagować na żaden z tych typów działań. Jeśli detektor zwraca wartość do systemu, jest ona ignorowana.
Oto kilka wskazówek dotyczących reagowania na każdy z tych rodzajów działań:
- W odpowiedzi na polecenie
ACTION_DRAG_ENTERED
lubACTION_DRAG_LOCATION
detektor może zmienić wyglądView
, by wskazać, że jest to potencjalny spadek wartości docelowej. - Zdarzenie o typie działania
ACTION_DRAG_LOCATION
zawiera prawidłowe dane dlagetX()
igetY()
odpowiadające lokalizacji punktu styku. detektor może wykorzystać te informacje, aby zmienić wyglądView
w lub w celu określenia dokładnego miejsca, w którym użytkownik może upuścić treści. - W odpowiedzi na żądanie
ACTION_DRAG_EXITED
detektor musi zresetować każdy wygląd zmiany, jakie ma zastosować w odpowiedzi na warunkiACTION_DRAG_ENTERED
lubACTION_DRAG_LOCATION
Informuje to użytkownika, że elementView
nie jest adresem i dalszym celem.
Odpowiedz na spadek
Gdy użytkownik zwolni cień na obiekt View
, a wcześniej View
raportuje, że może przyjmować przeciąganą treść, a system wysyła
zdarzenie przeciągnięcia do komponentu View
o typie działania ACTION_DROP
.
Detektor zdarzeń przeciągania musi:
Wywołaj
getClipData()
, aby uzyskać obiektClipData
, który został pierwotnie podane w wywołaniu dostartDragAndDrop()
. i przetwarzaj dane. Jeśli funkcja „przeciągnij i upuść” nie pokazuje danych ruchu, nie jest to konieczne.Zwraca wartość logiczną
true
, która wskazuje, że spadek został przetworzony. lubfalse
, jeśli nie jest. Zwrócona wartość staje się wartością zwracaną przez funkcjęgetResult()
w przypadku końcowego zdarzeniaACTION_DRAG_ENDED
. Jeśli system nie wysyła zdarzeniaACTION_DROP
, wartość zwrócona przez funkcjęgetResult()
dla wydarzeniaACTION_DRAG_ENDED
tofalse
.
W przypadku wydarzenia ACTION_DROP
getX()
i getY()
używają układu współrzędnych
View
, który otrzymuje spadek, aby zwrócić pozycję X i Y dla
i punktu styczności z klientem w momencie spadku.
Użytkownik może zwolnić cień obiektu View
, którego zdarzenie przeciągania jest możliwe.
detektor nie odbiera zdarzeń przeciągania, pustych obszarów UI aplikacji, a nawet
poza obszarem aplikacji, Android nie wyśle zdarzenia z działaniem
wpisz ACTION_DROP
i będzie wysyłać tylko zdarzenie ACTION_DRAG_ENDED
.
Reagują na koniec przeciągania
Natychmiast po zwolnieniu cienia przez użytkownika system wysyła
zdarzenie z typem działania ACTION_DRAG_ENDED
do wszystkich detektorów zdarzeń przeciągania
w Twojej aplikacji. Oznacza to, że przeciąganie zostało zakończone.
Każdy detektor zdarzeń przeciągania musi wykonać te czynności:
- Jeśli podczas operacji detektor zmieni swój wygląd, powinien się zresetować przywrócić domyślny wygląd, aby w sposób wizualny wspomóc użytkownika, .
- Słuchacz może opcjonalnie wywołać funkcję
getResult()
, aby dowiedzieć się więcej na temat . Jeśli odbiornik zwraca wartośćtrue
w odpowiedzi na zdarzenie działania wpiszACTION_DROP
, agetResult()
zwraca wartość logicznątrue
. We wszystkich pozostałych przypadkówgetResult()
zwraca wartość logicznąfalse
, również wtedy, gdy system nie wysyła zdarzeniaACTION_DROP
. - Aby wskazać, że operacja usuwania zakończyła się powodzeniem, odbiornik
powinna zwrócić do systemu wartość logiczną
true
. Jeśli nie zwrócisz wartościfalse
, sygnalizacja świetlna wskazująca, że cień powraca do źródła, może sugerować użytkownik zobaczy, że operacja się nie powiodła.
Reagowanie na zdarzenia przeciągania: przykład
Wszystkie zdarzenia przeciągania są odbierane przez metodę lub detektor zdarzeń przeciągania. Ten fragment kodu to przykład reakcji na zdarzenia przeciągania:
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; });
Dostosowywanie cienia do przeciągania
Możesz zdefiniować niestandardową wartość myDragShadowBuilder
, zastępując metody w
View.DragShadowBuilder
Ten fragment kodu tworzy mały,
prostokątny, szary, przeciągnij cień dla obiektu 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); } }