About a year and a half ago, I built an example Android app that allows you to drag images from one spot in a GridView to another. I have had several requests to show how the drag-drop framework used in that example could be adapted to the current Android support (V4+) for drag-drop operations. I have done that work and I am making the source code available in this post.
The sample app works the same as the one described in “Drag-Drop for An Android GridView“. There is a grid displayed on the screen. An “Add Image” button allows you to add images to the screen. Those images can be dragged onto the grid. Any images in the grid can be dragged around. There is also a trash can icon on the screen. Images can be dragged there to remove them from the screen. The image below shows what the app looks like on a Nexus 7 tablet.
How It Works
Usually, I do an extensive write-up to explain how my demo apps work. However, this time, I am starting with a very short description in this section and posting the source code. I will add a another longer post in the coming months.
If you read my earlier article about dragging views onto a GridView, you know that the drag-drop framework I use originates with the Android Launcher code for API 8. With a combination of interface classes for DragSource, DropTarget, a DragLayer, and DragController, the Launcher defined a useful framework for handling drag-drop operations. How I adapted those classes from the Launcher code is described in my earlier blog post. In the newer version of my GridView demo app, I have modified the classes a little bit.
DragActivity – This is the main activity of the demo app. It sets up a grid of images, defines a listener for drag events, and connects it to the views displayed on the screen.
DragSource – A drag source is a view where drag operations start. There is a DragSource interface class that defines the methods that a view must implement to be a source.
DropTarget – A drop target is a view that accepts dragged objects. An interface class describes the methods every DropTarget must implement.
DragController – This object is the controller that does most of the work to support dragging and dropping. It receives the drag events and interacts with DragSource and DropTarget objects. It also sends information back to the activity that created it. That activity is expected to implement the DragDropPresenter interface.
DragDropPresenter – This interface defines the set of methods an activity must support in order to work with a DragController.
ImageCell – An ImageCell is a subclass of ImageView. It is used to display an image on the grid. Since objects can be moved from one spot on the grid to another, an ImageCell is both a DragSource and a DropTarget.
ImageCellAdpater – The object that is used by the GridView to place ImageCell objects on the grid.
DeleteZone – This object is a DropTarget. It allows objects to be dragged onto it for disposal. It is used to implement the trash can that you see on the screen.
In this object model, here is how an object moves on the screen. The user touches a view on the screen. The view touched signals an event, onClick, to its listener, which is the DragActivity. The activity passes the event on to its DragController object. If the view is a DragSource, the controller initiates a drag operation by asking the source for its ClipData and the view to display as the drag shadow. It then has the view start sending drag events (via a call to v.startDrag). As the user moves the view around on the screen, all the events associated with drag-drop are sent to the DragController. The DragController makes everything happen from there. When the shadow view is over a drop target, the view, if it is accepting dropped objects, is informed (via method calls to onDragEnter and onDragExit) so it can provide visual feedback to the user that a potential drop spot has been found. If the user releases the object over a drop target, the target object is informed (via onDrop) so it can perform the operation. There are also method calls made back to the DragActivity so it knows when dragging begins, when it ends, and whether it was successful.
When you study the code for DragController, you will find that there are a lot of conditions being checked. That’s because of the DragController working with two kinds of views (sources, targets) and because of the way I wanted the GridView to work. You can only put one image in a cell. The cell must be empty to accept an image. If you drag an object and drop it somewhere that does not accept drops, it goes back to the cell it started in.
You can download the source code for this demo from the Wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.
This demo app was compiled with Android 4.2 (API 17). It works in all API levels from API 11 on up.
This sample app is an adaptation of the drag-drop framework from my earlier sample apps. The first of these (Moving Views In Android – Drag-Drop) goes back to January 2011. That one is the result of studying the Android Launcher code from API Level 8. I simplified the drag-drop framework a bit, but basically maintained its set of classes (DragSource, DropTarget, DragController, etc.).So the current example is a continuation of that framework. As described in the earlier section, you see that it has basically the same classes and interfaces.
It did not take much work to adapt to the Android drag-drop framework, which was introduced in API 11. However, it’s not clear to me that having a set of interface classes and defining View subclasses (e.g. ImageCell) is the easiest way to support drag-drop. In fact, there is a note to that effect in the “Drag and Drop” article. It says “You will probably want to use the listener in most cases. When you design UIs, you usually don’t subclass View classes …”. What I intend to try in the next month or so is another variation of this GridView example. In it, I will support dragging onto the grid, using only a listener. It will be interesting to see if that approach turns out to be easier.