Drag-Drop for Android GridView (V4)

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.

drag-griview-v4-05

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.

Source Code

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.

Potential Improvements

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.

About these ads

About Bill Lahti

Bill Lahti is a software engineer building mobile applications and knowledge management solutions. Two of his interests are writing for this blog and building Android apps, with strategy games being an area of particular interest.
This entry was posted in Android and tagged , , , . Bookmark the permalink.

34 Responses to Drag-Drop for Android GridView (V4)

  1. Pingback: Drag-Drop for an Android GridView | More Is Not Always Better

  2. Pingback: My Android Programming Tutorials By Topic | More Is Not Always Better

  3. Pingback: Moving Views In Android – Part 2, Drag and Drop | More Is Not Always Better

  4. Pingback: Improved Drag-Drop for an Android GridView | More Is Not Always Better

  5. Samuel says:

    Thanks a lot for a well articulated tutorial and write up. However I was wondering how and what can one do to make sure that this thing is adapted to Honeycomb 3.2. I have tried this on Ice Cream 4.0 and above and I have no problem but when I tried it on Honeycomb 3.0 it turns out not to run at all and on 3.2 there is a glitch of it running and crash later. I will be happy if you can tell me how this issue can be overcome with the current code.

    • blahti says:

      I have tested this on a Huawei tablet running on Android 3.2 and in the emulator, same level. Everything works as far as I can tell. I did not try earlier versions of Honeycomb. Drag-drop page suggests that the framework was added with API 11, but I did not test it there.

      What fails? The original example I provided or your own example based on it? In what way does it fail?

  6. Felix says:

    Hey Bill, do you offer any support for your Drag n Drop api? We want to integrate it in an app but it has to work smoother with all the fancy stuff like dragshadows. If so, please contact me via email and we can work out the details.

  7. Does it have Swap feature, when you drag and drop an image to a grid that alreday contains an image?

    • blahti says:

      I think you could add a swap feature easily. In onDrop of ImageCell, change the code to check the the cell number of what is in the DragSource. So then, at the point of drop, you know both the cell number of the drop target and the source. Be sure to cast DragSource, as in “ImageCell isource = (ImageCell) source;”. Then do what you need for your kind of swap.

  8. houssemzaier says:

    Excellent work thank you so much.

  9. Xavi says:

    Hey Bill, thanks for your tutos regarding drag and drop, I´m looking at them and trying to get familiar with it. I need to do something like this:
    I´ve got a pitch and players distributed in the layout according to the user selected formation.
    The idea of the layout I´ve implemented for the pitch is dividing the layout in cells, actually there are 30 cells (6 rows and 5 columns) and depending on the formation selected by user I hide or display the corresponding cells for that formation and bind the proper data. I´ve divided the screen in cells by using a LinearLayout and having inside multiple LinearLayouts with weights.
    Later, in each cell, I place another LinearLayout (representing a player) which has imageviews, textview, progressbar to show the conditions of the player…
    My question is how I can implement the drag and drop functionality for this layout, to be able to switch the players? Do you have any idea or could you guide me a bit?

    - I´ve tried also to simplify the previous layout by using a gridlayout (I´m implementing from API 8), so I needed to add there the Android support library v7 for gridlayout. But I´m more familiar with LinearLayout than with Gridlayout, as I never used it before, and even less with the support library.

    I would really appretiate if you could help me.
    Once again, thanks for your tutos!

    • blahti says:

      I was going to suggest that you consider the newer drag-drop framework that is supported in Android. I think it will do better at handling lots of drop targets within a complex layout, which is what you say you have. A problem is that does not work for API 8. That’s what you are using, right? If you want to have a look at a demo app for the newer drag-drop, I did one here: Drag-Drop Griview V4.

      If you are committed to API 8, you could keep using the drag-drop framework here but I think you would have to do some custom views. You could design a custom view that does the two things you want: support drag-drop and support your custom layout. Doing a custom view is not so difficult. What I have never tried is a custom layout. That looks challenging to me. I don’t have much to suggest for references on how to do custom views. I have written up only one so far: progress bars demo.

  10. Ashok says:

    Thank u for the tutorial :) I have a small query…. What should i do to make the drop target a listview? I want the list view to accumlate all the thumbs as i add them in order like a list… Thanks in advance :)

    • blahti says:

      With the newer support for drag-drop in V4, you have a lot of flexibility. Read the sections there to see how you indicate that your ListView wants to be part of the drag-drop sequence. The section is under “Drag-Drop Process” and “Started”. You might find it easier to use the framework without benefit of the interface classes I have added, or you might prefer to keep using. Studying my demo example to see how it fits in with the new drag-drop framework could give you some ideas.
      I’d also advise starting out with very simple actions. For instance, get your LisView hooked up so it is part of the drag-drop operation and do something for simple for one of the drag actions (ACTION_DRAG_ENTERED). It could be anything: write to the log, use toast, change a color on the screen. Then add to your handling from there. You should also figure out how to add to a ListView in a code and do that ahead of time. Debugging inserting into a list at the same time as debugging the drag-drop will likely be a bit difficult.

  11. maady says:

    pls help me the code in my application i need to drag the tab from screen to tablayout.

  12. NordicElf says:

    Hi

    I implemented this within a fragmentActivity with a ViewPAgeAdapter – 3 fragments each containing a gridView that have a dropZone and a DeleteZone each. So there is a lot going on! Everything works pretty well except I can’t scroll the gridview and I wanted to use the longClickListener but could only get onTouch working. I implemented my OnTouchClickListener and recreated a long click – works pretty well. I could scroll my gridview… however the viewpager seems to hold references to the latest view loaded (except the first time) and so when I do my long click it’s actually on a different fragment. My thought was to maybe restrict the drag, so it only allows the user to drag diagonally or horizontally and leaves the straight up and down for scrolling? But I’m not sure where to do this and or whether its a good idea or not. Do you have any suggestions?
    Thanks!
    I think your drag ‘n drop is awesome!

    • blahti says:

      I think there are two variations of ViewPageAdapter, one that retains its views and another that doesn’t. It’s possible that changing that might help. See http://developer.android.com/training/implementing-navigation/lateral.html#horizontal-paging where it talks about FragmentPagerAdapter and FragmentPagerStateAdapter.

      As for keeping the touch events straight, I’ve not had to do that too often. I know the original code which I studied for drag-drop actually detected the need to scroll left and right while a drag-drop was happening. So going back to look at that might help. See http://source.android.com/source/index.html and see if the code for the old Launcher is still there. At the time I started, it was in Workspace in package com.android.launcher2.

      Recently, I did a little work where I had to tell the difference between pinch zoom gestures and other touches. I don’t know if that will help you sort out the difference between scrolling and dragging, but you could check it out. See Multitouch Panning and Zooming. Somewhere in there is a ScaleListener that builds on the listener provided by ScaleGestureDetector. The point is that the detector has a method isInProgress that other parts of the code use. You might be able to come up with a reliable way of detecting whether a drag has started or a scroll has started and then have the parts of your code that might otherwise get confused check with the one reliable authority. It might work.

  13. Sir can you please make a video tutorial regarding to that application?? I learn faster through watching tutorials, could you help me with that?i really need to learn this application,

    • blahti says:

      I wish there was time to do videos. Perhaps some of the other articles on my blog related to drag-drop will help. See http://blahti.wordpress.com/tag/drag-drop/.

      • Sir is it possible that on the gridview there will be already items arranged on it and those items are the draggable objects, so there is no need for the add image button because the items are already placed on the gridview but still they can be arranged

      • blahti says:

        Yes, it is possible. I’d suggest breaking the problem into two parts. First figure out how to get a GridView set up the way you want. Do that as a separate activity. Then do an Activity that supports dragging from one cell to another (like in my demo app). Then create a third activity that combines the two. The reason I do this is so I always have something to look back on when the more complicated thing fails.

        ImageCellAdapter is where the image cells are filled in. You could modify it so it works with a list of drawable ids so it sets itself up with images rather than starting with null drawables. Check method getView.

      • Sir, i followed your tutorial, i created a gridview which serves as the droptarget, and i slightly modify it, instead of adding images on a framelayout, i created another gridview which serves as the list of objects that can be dragged on the drop target which is the other gridview..But when i run it, and start dragging one of the object on the drop target, i keep getting a forced close, What i did is i have a gridviewadapter and there the images are the new ImageCell, but it doesnt work,, can you give me an example sir?sorry for asking too much, i really just need this project, i hope you can give me some example..

    • Thank You sir, because of your tutorials i finally solve my problems, I’am half way through in finishing my app,Just one question though,how can i save the changes of position in my app??-_- thank you sir in advance :)

      • blahti says:

        If you want to save the current state of all the information in the grid objects, you might consider using SharedPreferences.

        Also check my July 10, 2012 comment on the first Drag-Drop GridView article.

      • Sir i i still dont get how to save the positions of the gridview..i used the sharedpreferences but it only save the position of the last i tem i changed…can you please help me?

      • blahti says:

        If you have N elements in a GridView, you need to be able to access the N elements in the activity. Save an array of whatever you are using to indicate what image is in the cell. If you are displaying drawables that you have in your drawable folders, you should save the drawable ids. If you are displaying images that you have downloaded, you would save the path of the file. It really depends on how you set the images of the cells.

        One problem might be that you are not saving the drawable id anywhere. Whenever you assign an image to an ImageCell, save the resource id of the drawable you are using. You will probably need to add a new field to ImageCell to store the id for the drawable.

      • can i send my project to you? i really don’t know how can i save the locations of the images..

      • blahti says:

        I am sorry to say that I simply do not have time to do additional projects right now.

  14. soni says:

    hi
    how to develope chess game in android using gridview to display chessboard?

  15. andybuki says:

    Hi. I tried to use your code to realize following task:
    http://stackoverflow.com/questions/16588962/android-ui-views-slider
    I need an android component that will hold two views (one on top, and the second one ont he bottom). There will also be a slider between views.

    It will look something like this:
    View 1 is bigger than shown and user can expand it by moving down the separator (dark grey rectangle on the picture above). While expanding, View 2 will go down and will not be covered by View 1 (not like in android’s notification panel).

    Is there some kind of ready to use library for this? If not – could you point me in the right direction how to achieve this?

    But i realized only vertical line, that can move up and down, but i can’t integrate two views between this line.

    Thanks
    Andrey

    • Bill Lahti says:

      I assume you are using a FrameLayout with 3 views in it: View 1, View 2, and the dark bar. I wonder if something like the following would work. View 1 is first in the frame. It uses all the height available. View 2 is second. It is positioned down a bit as you show in your picture. (I am not sure what the xml is for that, or maybe you have to set its x-y origin in code.) And I guess the bar is there too. When the user presses on the bar, code in the touch listener runs. It takes the x-y drag position and adjusts the x-y position of View 2 up or down. In doing that, more or less of View 1 is revealed. I have not checked this so I don’t know if it will really work.

      I base this idea on another app I did. I have short animations I want to run on top of another view, and I want them to appear on top of another but not totally obscure it. I do some math to calculate a position in the larger view and then I create and attach the animation view and set its bounds and position. It took awhile but I eventually got it working. Most of it was in code rather than xml layouts. From that, I believe you should be able to move and slide View 2 up and down inside a touch listener.

      So that’s one idea. If what you want is like a navigation drawer, there might be some ideas here: https://plus.google.com/108967384991768947849/posts/hekWTCFaybk.

  16. prinz says:

    Hats off man.Thank you very much for your very good explanation.

  17. Gustavo says:

    Excelent work.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s