Android drag/drop framework allows your users to move data from one View to another View in the current layout using a graphical drag and drop gesture.
Although the framework is primarily designed for data movement, you can use it for other UI actions. For example, you could create an app that mixes colors when the user drags a color icon over another icon.
NOTE: You will need to set a minimum SDK version of 11 to use the drag and drop processing.
There are basically four steps or states in the drag and drop process −
The DragEvent represents an event that is sent out by the system at various times during a drag and drop operation. This class provides few Drag Events and important methods which we use during Drag/Drop process.
Following are all events integers available as a part of DragEvent class.
Drag Event | Meaning |
---|---|
ACTION_DRAG_STARTED | Signals the start of a drag and drop operation. |
ACTION_DRAG_ENTERED | Signals to a View that the drag point has entered the bounding box of the View. |
ACTION_DRAG_LOCATION | Sent to a View after ACTION_DRAG_ENTERED if the drag shadow is still within the View object's bounding box. |
ACTION_DRAG_EXITED | Signals that the user has moved the drag shadow outside the bounding box of the View. |
ACTION_DROP | Signals to a View that the user has released the drag shadow, and the drag point is within the bounding box of the View. |
ACTION_DRAG_ENDED | Signals to a View that the drag and drop operation has concluded. |
In this tutorial, we will implement a drag-and-drop operation with TextView, ImageView and Button.
1. Create a new project in Android Studio by navigating to File ⇒ New ⇒ New Project and fill required details. By default my activity is MainActivity.java.
2. Open your activity_main.xml layout and place the below code to it. The layout contains three Linear Layout which will received dragged view.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.dragdrop_demo.MainActivity"> <LinearLayout android:id="@+id/top_layout" android:layout_width="fill_parent" android:layout_height="match_parent" android:layout_weight="1" android:background="@color/blue" android:gravity="center" android:orientation="vertical"> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="@string/draggable_text" android:textColor="@android:color/black" android:textSize="20sp" /> <ImageView android:id="@+id/image_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/ic_launcher" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/draggable_button" /> </LinearLayout> <LinearLayout android:id="@+id/yellowLayout" android:layout_width="fill_parent" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:orientation="horizontal"> <LinearLayout android:id="@+id/left_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:background="@color/colorPrimary" android:gravity="center" android:orientation="vertical" /> <LinearLayout android:id="@+id/right_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" android:background="@color/colorAccent" android:gravity="center" android:orientation="vertical" /> </LinearLayout> </LinearLayout> |
3. This point will shows step-by-step how to start a drag, how to respond to events during the drag, how respond to a drop event, and how to end the drag and drop operation.
The user starts a drag with a drag gesture, usually a long press, on a View object. In response, you should do the following:
As necessary, create a ClipData and ClipData.Item for the data being moved. As part of the ClipData object, supply metadata that is stored in a ClipDescription object within the ClipData. For a drag and drop operation that does not represent data movement, you may want to use null instead of an actual object.
If you want to learn about ClipData then refer to this link.
For example, this below code snippet shows how to respond to a long press on a View by creating a ClipData object that contains the tag or label of an View.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@Override public boolean onLongClick(View view) { // 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) view.getTag()); // Create a new ClipData using the tag as a label, the plain text MIME type, and // the already-created item. This will create a new ClipDescription object within the // ClipData, and set its MIME type entry to "text/plain" String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN}; ClipData data = new ClipData(view.getTag().toString(), mimeTypes, item); // Instantiates the drag shadow builder. View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view); // Starts the drag view.startDrag(data//data to be dragged , shadowBuilder //drag shadow , view//local data about the drag and drop operation , 0//no needed flags ); //Set view visibility to INVISIBLE as we are going to drag the view view.setVisibility(View.INVISIBLE); return true; } |
All drag events are initially received by your drag event method or listener. The following code snippet is a simple example of reacting to drag events in a listener:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
// This is the method that the system calls when it dispatches a drag event to the // listener. @Override public boolean onDrag(View view, DragEvent event) { // Defines a variable to store the action type for the incoming event int action = event.getAction(); // Handles each of the expected events switch (action) { case DragEvent.ACTION_DRAG_STARTED: // Determines if this View can accept the dragged data if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { // if you want to apply color when drag started to your view you can uncomment below lines // to give any color tint to the View to indicate that it can accept // data. // view.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);//set background color to your view // Invalidate the view to force a redraw in the new tint // view.invalidate(); // returns true to indicate that the View can accept the dragged data. return true; } // Returns false. 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 YELLOW or any color tint to the View, when the dragged view entered into drag acceptable view // Return true; the return value is ignored. view.getBackground().setColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_IN); // Invalidate the view to force a redraw in the new tint view.invalidate(); return true; case DragEvent.ACTION_DRAG_LOCATION: // Ignore the event return true; case DragEvent.ACTION_DRAG_EXITED: // Re-sets the color tint to blue, if you had set the BLUE color or any color in ACTION_DRAG_STARTED. Returns true; the return value is ignored. // view.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN); //If u had not provided any color in ACTION_DRAG_STARTED then clear color filter. view.getBackground().clearColorFilter(); // Invalidate the view to force a redraw in the new tint view.invalidate(); return true; case DragEvent.ACTION_DROP: // Gets the item containing the dragged data ClipData.Item item = event.getClipData().getItemAt(0); // Gets the text data from the item. String dragData = item.getText().toString(); // Displays a message containing the dragged data. Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_SHORT).show(); // Turns off any color tints view.getBackground().clearColorFilter(); // Invalidates the view to force a redraw view.invalidate(); View v = (View) event.getLocalState(); ViewGroup owner = (ViewGroup) v.getParent(); owner.removeView(v);//remove the dragged view LinearLayout container = (LinearLayout) view;//caste the view into LinearLayout as our drag acceptable layout is LinearLayout container.addView(v);//Add the dragged view v.setVisibility(View.VISIBLE);//finally set Visibility to VISIBLE // Returns true. DragEvent.getResult() will return true. return true; case DragEvent.ACTION_DRAG_ENDED: // Turns off any color tinting view.getBackground().clearColorFilter(); // Invalidates the view to force a redraw view.invalidate(); // Does a getResult(), and displays what happened. if (event.getResult()) Toast.makeText(this, "The drop was handled.", Toast.LENGTH_SHORT).show(); else Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_SHORT).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 OnDragListener."); break; } return false; } |
4. Finally open up your MainActivity.java and place the below code to it. This class will contain all the drag and drop steps that we learned from the above step,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
package com.dragdrop_demo; import android.content.ClipData; import android.content.ClipDescription; import android.graphics.Color; import android.graphics.PorterDuff; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.DragEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements View.OnDragListener, View.OnLongClickListener { private static final String TAG = MainActivity.class.getSimpleName(); private TextView textView; private Button button; private ImageView imageView; private static final String IMAGE_VIEW_TAG = "LAUNCHER LOGO"; private static final String TEXT_VIEW_TAG = "DRAG TEXT"; private static final String BUTTON_VIEW_TAG = "DRAG BUTTON"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViews(); implementEvents(); } //Find all views and set Tag to all draggable views private void findViews() { textView = (TextView) findViewById(R.id.label); textView.setTag(TEXT_VIEW_TAG); imageView = (ImageView) findViewById(R.id.image_view); imageView.setTag(IMAGE_VIEW_TAG); button = (Button) findViewById(R.id.button); button.setTag(BUTTON_VIEW_TAG); } //Implement long click and drag listener private void implementEvents() { //add or remove any view that you don't want to be dragged textView.setOnLongClickListener(this); imageView.setOnLongClickListener(this); button.setOnLongClickListener(this); //add or remove any layout view that you don't want to accept dragged view findViewById(R.id.top_layout).setOnDragListener(this); findViewById(R.id.left_layout).setOnDragListener(this); findViewById(R.id.right_layout).setOnDragListener(this); } @Override public boolean onLongClick(View view) { // 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) view.getTag()); // Create a new ClipData using the tag as a label, the plain text MIME type, and // the already-created item. This will create a new ClipDescription object within the // ClipData, and set its MIME type entry to "text/plain" String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN}; ClipData data = new ClipData(view.getTag().toString(), mimeTypes, item); // Instantiates the drag shadow builder. View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view); // Starts the drag view.startDrag(data//data to be dragged , shadowBuilder //drag shadow , view//local data about the drag and drop operation , 0//no needed flags ); //Set view visibility to INVISIBLE as we are going to drag the view view.setVisibility(View.INVISIBLE); return true; } // This is the method that the system calls when it dispatches a drag event to the // listener. @Override public boolean onDrag(View view, DragEvent event) { // Defines a variable to store the action type for the incoming event int action = event.getAction(); // Handles each of the expected events switch (action) { case DragEvent.ACTION_DRAG_STARTED: // Determines if this View can accept the dragged data if (event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { // if you want to apply color when drag started to your view you can uncomment below lines // to give any color tint to the View to indicate that it can accept // data. // view.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN);//set background color to your view // Invalidate the view to force a redraw in the new tint // view.invalidate(); // returns true to indicate that the View can accept the dragged data. return true; } // Returns false. 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 YELLOW or any color tint to the View, when the dragged view entered into drag acceptable view // Return true; the return value is ignored. view.getBackground().setColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_IN); // Invalidate the view to force a redraw in the new tint view.invalidate(); return true; case DragEvent.ACTION_DRAG_LOCATION: // Ignore the event return true; case DragEvent.ACTION_DRAG_EXITED: // Re-sets the color tint to blue, if you had set the BLUE color or any color in ACTION_DRAG_STARTED. Returns true; the return value is ignored. // view.getBackground().setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN); //If u had not provided any color in ACTION_DRAG_STARTED then clear color filter. view.getBackground().clearColorFilter(); // Invalidate the view to force a redraw in the new tint view.invalidate(); return true; case DragEvent.ACTION_DROP: // Gets the item containing the dragged data ClipData.Item item = event.getClipData().getItemAt(0); // Gets the text data from the item. String dragData = item.getText().toString(); // Displays a message containing the dragged data. Toast.makeText(this, "Dragged data is " + dragData, Toast.LENGTH_SHORT).show(); // Turns off any color tints view.getBackground().clearColorFilter(); // Invalidates the view to force a redraw view.invalidate(); View v = (View) event.getLocalState(); ViewGroup owner = (ViewGroup) v.getParent(); owner.removeView(v);//remove the dragged view LinearLayout container = (LinearLayout) view;//caste the view into LinearLayout as our drag acceptable layout is LinearLayout container.addView(v);//Add the dragged view v.setVisibility(View.VISIBLE);//finally set Visibility to VISIBLE // Returns true. DragEvent.getResult() will return true. return true; case DragEvent.ACTION_DRAG_ENDED: // Turns off any color tinting view.getBackground().clearColorFilter(); // Invalidates the view to force a redraw view.invalidate(); // Does a getResult(), and displays what happened. if (event.getResult()) Toast.makeText(this, "The drop was handled.", Toast.LENGTH_SHORT).show(); else Toast.makeText(this, "The drop didn't work.", Toast.LENGTH_SHORT).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 OnDragListener."); break; } return false; } } |
5. Finally all done, now you can also add Drag and Drop functionality to your apps.
Thanks.
Subscribe to us and get the latest news.
3 Comments
Eltayeb
Thursday, October 5th, 2017Hi!
Thank you for the tutorial on drag and drop.
I am learning android programming. I am Sudanese but I live and work in KSA. I teach English in a private institute. I write programs that convert the English exercise in the textbook into interactive mobile apps to be installed on my students mobile phones. Sometimes I get stuck. That’s why I need a mentor. If you can help, let me know so that we can agree on a price and a method of payment. If you are not interested, please recommend someone who can help me when I get stuck.
Dr. Droid
Friday, October 6th, 2017Hi Eltayeb,
Yes, I am interested. Can you drop me a mail at [email protected]. So that we can discuss the things more comfortably.
Thanks
Taras
Tuesday, May 15th, 2018Hi|
I am trying to implement your code, but I want to drag an bitmap(cropped image) from imageview1 and drop it in imageview2 where is another bitmap(normal image). Can you give me some ideas? thanks