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 elementuViewdetektora.ACTION_DRAG_LOCATION: gdy detektor odbierze zdarzenieACTION_DRAG_ENTERED, otrzyma noweACTION_DRAG_LOCATIONzdarzenie 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_ENTEREDZdarzenie jest wysyłane, gdy punkt kontaktu cienia przeciągania przechodzi z ramki ograniczającej wViewdetektora 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_ENTEREDlubACTION_DRAG_LOCATIONdetektor może zmienić wyglądView, by wskazać, że jest to potencjalny spadek wartości docelowej. - Zdarzenie o typie działania
ACTION_DRAG_LOCATIONzawiera prawidłowe dane dlagetX()igetY()odpowiadające lokalizacji punktu styku. detektor może wykorzystać te informacje, aby zmienić wyglądVieww 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_EXITEDdetektor musi zresetować każdy wygląd zmiany, jakie ma zastosować w odpowiedzi na warunkiACTION_DRAG_ENTEREDlubACTION_DRAG_LOCATIONInformuje to użytkownika, że elementViewnie 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_ENDEDtofalse.
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śćtruew 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); } }