Drag-Drop for an Android GridView

(March 4, 2013 Updsate: This example has been revised for Android V4, See Drag-Drop for GridView V4.)


It’s time for another tutorial on drag-drop in Android. I have written several other articles on this topic (Part 1, Part 2, Part 3), but the last one was back in February. I have learned quite a bit since then.

This time I built a demo that allows you to add images dynamically to the screen and then drag them onto a GridView. You can then move the images around in the GridView or move them to the trash. The figures below show you what it looks like.

Figure 1 – Touching the “Add Image” button places an image below the 3 x 3 grid.

FIgure 2 – Touching the image for a second or two starts the drag-drop sequence. As you move over regions on the grid, the background color changes to green, indicating that the object can be dropped there.

Figure 3 – Image moves to cell when you remove your finger from the screen.

Figure 4 – A second image on its way to the middle cell in the grid.

 

Figure 5 – Three images on the grid.

Figure 6 – Images can be dragged to a trashcan to remove them from the screen.

Only one image can be in each cell, but the images can be dragged from one cell to another.

How It Works

I have already written extensively about the drag-drop framework in earlier blog articles. I refer you to them for full descriptions: (1) Moving Views in Android – Part 2, Drag and Drop; (2) Moving Views in Android – Part 3, Drop Zones. The first one tells the story of how I modified  the classes and interfaces I found in the Android Launcher to get to a demo program. In the second one, I developed the notion of DropZone as a place on the screen where objects could be dropped. So, given those two notes, I’ll just cover the some of the major differences and improvements.

The first thing that’s different about my latest demo app is that it demonstrates dragging and dropping within one of the standard ViewGroup subclasses: GridView. The earlier work used the deprecated class AbsoluteLayout. At the time, I felt a little bad about the decision to use AbsoluteLayout, but I was eager to achieve my goal of moving views around on the screen and there was a lot to deal with to pare the Android Launcher classes down to a demo program so I used AbsoluteLayout.

Modifying my past demo programs to support dragging to a GridView was not much work at all. The drag-drop framework gave me the object vocabulary. I just had to find how to apply it to a commonly used layout. In this case, I chose to use a FrameLayout as the outermost layout with a GridView nested within it. So I started with DragLayer as a subclass of FrameLayout. The views in the cells of the GridView would have to implement the DropTarget interface if they were to allow objects to be dragged onto them. I also decided that they would implement the DragSource interface so objects could be dragged from one cell to another. Rather than start with views already on the screen, as I had done in the Part 2 demo, I thought I’d make the whole thing dynamic. Images would get added when the user clicks an Add Image button. So that meant there would be one more DragSource object added to the screen with each click.

I ended up with only a few new classes in this demo app. One of them is the ImageCell class, which as I said above, implements both the DragSource and DropTarget interfaces. The other new class is a DeleteZone class, which I used to implement the trashcan that you see on the screen.

With that as background, here’s what’s involved with a drag-drop operation.

  • The user presses on an image on the screen.
  • The view receives that as a “long click” event and passes that to an event  handler via a call to onLongClick.
    (Note: If you do not want long click to be the way to start a drag, see the newer version of this demo.) 
  • The DragActivity object has already set itself up as a listener for long clicks for all the draggable views on the screen. So its onLongClick method is called.
  • Code in onLongClick arranges to start handling of a drag-drop operation by calling a DragController.
    A DragController is an object that handles all of the touch events on behalf of a DragLayer object.
  • The DragLayer, as a subclass of ViewGroup, has an implementation of onInterceptTouchEvent, thus ensuring that all touch events from any of its child views go through it. This is important because this is how the one object, the DragController, gets to work across the view boundary of any single child. (Be sure to read the description of onInterceptTouchEvent in class ViewGroup. As they say, it is a complicated interaction.)
  • When a DragController starts the drag, it gets a bitmap it can display and uses it to create a DragView. The bitmap is built from the view that fielded the original long click event. So what you see moving on the screen is not the actual view, but something that looks like the view.
  • As long as the user continues touching the screen, all the touch events go to the DragLayer, which passes them along to the DragController. The DragController tracks the movement and moves the DragView to keep up.
  • During this time, any ImageCell (DropTarget) objects that are located at that spot on the screen are called with the onDragEnter, onDragExit methods defined by the DropTarget.
  • When the user stops touching the screen, the DragController checks to see if there is a ImageCell (DropTarget) present and if that target is willing to accept a dropped view (via a call to acceptDrop).
  • If the target accepts the drop, the DragController calls the ImageCell onDrop method and also calls the method onDropCompleted of ImageCell where  the drag started, in order to to let it know that the drop completed. (I know that sounds a bit confusing but there are two methods, one for the DropTarget interface and the other for the DragSource interface. ImageCells have both.)

That’s the basic flow. In this demo, the ImageCell class is where most of the interesting code is. Let’s look more closely at that class and a few of its code segments. (Note: Link to full source code is at the end of this article.)

/**
 * This subclass of ImageView is used to display an image on a GridView.
 * An ImageCell knows which cell on the grid it is showing and which
 * grid it is attached to. Cell numbers are from 0 to NumCells-1.
 * It also knows if it is empty.
 *
 * Image cells are places where images can be dragged from and dropped onto.
 * This class implements both the DragSource and DropTarget interfaces.
 */

public class ImageCell extends ImageView
    implements DragSource, DropTarget

Two methods take care of changing the color of the cell as objects are dragged over it.

public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
        DragView dragView, Object dragInfo) {
    int bg = mEmpty ? R.color.cell_empty_hover : R.color.cell_filled_hover;
    setBackgroundResource (bg);
}

public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
        DragView dragView, Object dragInfo) {
    int bg = mEmpty ? R.color.cell_empty : R.color.cell_filled;
    setBackgroundResource (bg);
}

The onDrop method is responsible for filling the cell up with the dragged object. It grabs the drawable from the object being dragged and sets its own drawable to be the same thing.

public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset,
        DragView dragView, Object dragInfo) {
    // Mark the cell so it is no longer empty.
    mEmpty = false;
    int bg = mEmpty ? R.color.cell_empty : R.color.cell_filled;
    setBackgroundResource (bg);

    // The view being dragged does not actually change its parent 
    // and switch over to the ImageCell.
    // What we do is copy the drawable from the source view.
    ImageView sourceView = (ImageView) source;
    Drawable d = sourceView.getDrawable ();
    if (d != null) {
       this.setImageDrawable (d);
    }
}

In its role as a DragSource, an ImageCell, the one where the drag started, also gets its onDropCompleted called. In that method, the cell clears itself and sets it drawable to empty, thus erasing the previous contents.

public void onDropCompleted (View target, boolean success)
{
    // If the drop succeeds, the image has moved elsewhere.
    // So clear the image cell.
    if (success) {
       mEmpty = true;
       if (mCellNumber >= 0) {
          int bg = mEmpty ? R.color.cell_empty : R.color.cell_filled;
          setBackgroundResource (bg);
          setImageDrawable (null);
       } else {
         // For convenience, we use a free-standing ImageCell to
         // take the image added when the Add Image button is clicked.
         setImageResource (0);
       }
    }
}

For this to work, the DragLayer has to be set up correctly with the right DropTarget objects. The earlier demo programs defined all the DropTargets ahead of time in the layout xml file for the main activity. For this demo, the drop targets are not known ahead of time. They are ImageCell objects in a GridView. Those are allocated dynamically by an ImageCellAdapter in the onCreate method of DragActivity. It’s not particularly easy to get the elements on a GridView except by iterating through the child views of the GridView after they are all present on the screen. Since the DragLayer used by DragActivity is set up as a DragListener of the DragController, there is a perfect spot to set up all the drop targets that are found in the GridView.

public void onDragStart(DragSource source, Object info, int dragAction)
{
    // We are starting a drag.
    // Build up a list of DropTargets from the child views of the GridView.
    // Tell the drag controller about them.

    if (mGridView != null) {
       int numVisibleChildren = mGridView.getChildCount();
       for ( int i = 0; i < numVisibleChildren; i++ ) {
           DropTarget view = (DropTarget) mGridView.getChildAt (i);
           mDragController.addDropTarget (view);
       }
    }

    // Always add the delete_zone so there is a place to get rid of views.
    // Find the delete_zone and add it as a drop target.
    // That gives the user a place to drag views to get them off the screen.
    View v = findViewById (R.id.delete_zone_view);
    if (v != null) {
       DeleteZone dz = (DeleteZone) v;
       mDragController.addDropTarget (dz);
    }
}

All of the drop targets of the DragController are reset in the onDragEnd method.

Next Time

Overall, I think this demo worked out well. I wanted to address many of the comments that I had received from the earlier articles. People wanted to see an alternative for use of the deprecated AbsoluteLayout class, and they wanted to see an example that did not have as many limitations on where you could drag objects.

One thing that I still want to do is adapt this demo to the drag-drop framework described on the Android developers’ website: Dragging and Dropping. It looks like the DragLayer, DragController, DragSource, and DropTarget classes will work nicely with that. Look for more on that in a later article.

Source Code For This Demo

The source code for this demo application is available here:  (1) download from Wglxy.com.  If you find that the app does not build in Eclipse, be sure to do a clean build by using the “Clean” item on the Project menu.

This demo app was built on a MacBook running Eclipse Galileo and Android 2.2_r3. The app has been tested in the Android emulator and on an LG Optimus phone.

There is also an improved version of this example application. It allows you to drag objects immediately. You do not have to wait for a long click. (In fact, those changes have been incorporated into this example too.) 

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.

294 Responses to Drag-Drop for an Android GridView

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

  2. cbee says:

    Hi thank you for your demo. Is it possible to replace the grid view with table row? If so, please guide me I’m new to android.

    • blahti says:

      Now that I have done drag-drop for a GridView, it seems like it will be easy to adapt the demo to other views — TableLayout, for example.

      Rough outline:
      1. Replace GridView with a TableLayout in demo.xml.
      2. In DragLayer, update onDragStart to get the child views of the TableLayout and set them as drop targets for the drag controller.
      Probably you would ignore the TableRow views since only the views within the rows are drop targets.
      3. Figure out what kinds of views go in your table.
      Define a subclass like ImageCell for each distinct type.
      4. Each subclass implements the DropTarget interface so it can accept dragged objects.
      5. If objects can be dragged from within the table, those classes also need to implement DragSource.
      Depending on what is being dragged and what it means in your application, the code might be more complicated than what is in Image Cell, but that class would give you some ideas of what you have to write.

      Be sure to think through your design so you know all the places where dragging starts and where it stops.

      • cbee says:

        Thank you.. Is it possible to increase the drop targets dynamically? I have a linked list where i can drop images . The size of the linked list increases as we drop the images.Can we specify the drop targets after starting a drag operation ?(when we select an image to drag, a drop sot must be specified in the linked list, the size of the linked list must be increased and it must be defined as a drop target).Waiting for your reply.

      • blahti says:

        Drop targets can be added as needed. See onDragStart in DragLayer for where I chose to add them. If your code ends up with something similar to mine, where DragLayer implements the DragListener interface, you might not have to do anything. My example does not add drop targets until the drag operation begins so whatever the child views are at that time are added. At the end of the drag, the old targets are removed. I found that that worked well for the grid view because all the child views would be in place and completely set up when the drag begins. If new views are added as a result of a drag operation, they would be in the list of drop targets when the next drag starts.

      • suresh says:

        hi blahti,

        i have three images in imageview can i know how to drag and drop in to the gridview one by one….

        thanks!

      • blahti says:

        I do not know exactly what you intend to do with your app. So let me point you to a few methods. If you are trying to control the order in which the objects are dragged, you might try putting something in the allowDrag method (of DragSource) so that it does not allow an image to move until you are ready. Or you can change acceptDrop to disallow dropping until you want it to happen.

  3. Fox88 says:

    Hi, great work! Thanks!

  4. christina says:

    Hi,

    I am a student and currently working on a project on android.
    I have a question here.

    Is there a way to modify the above sample with drag and drop image from slidedrawer?

    • benmonro says:

      hey Bill, this is great. fyi, i created a “launcher control” largely based on this blog post (with props to you sir).

      https://github.com/benmonro/PulseLauncherGrid

    • blahti says:

      @Christina
      I have not used SlidingDrawer in any of my work. I have only seen a few examples. I don’t see any reason why this drag-drop framework will not work with them. For this demo, I started with a GridView that displayed images and asked myself which classes would have to support DropTarget and DragSource. WIth a modified ImageView, I had most of what I needed. Then all I had to do was figure out how to get the DragController to know which DropTargets it was working with. I think you could do something very similar. Notice that in DragLayer I put code in onDragStarted to set up the list of drop targets. In your app, your DragLayer would have to go through all its children to find the drop targets. A SlidingDrawer might be a bit different than this example because it feels a bit more like an expandable list. My GridView is fixed in size so I did not choose to support dropping an object on the GridView and having it expand to hold one more item. If you want to have a drawer where the number of items can grow, you’d need a special DropTarget that reacts to a drop by adding to what is in the drawer. That would be a bit like the trash can in my example.

  5. steller says:

    Is your image cell class a modified version of drop target?

    I am trying to understand your image cell class. Couldnt just use the drop target interface instead?

    Thank you for the demo i love it and its helping me tremendously.

    • blahti says:

      ImageCell is a subclass of ImageView because it is used to display images in the GridView. In addition to all the standard behaviors of an ImageView, an ImageCell must also behave like a DropTarget. So it implements that interface by defining 6+ methods.

      So, in answer to your questions, ImageCell is a modified ImageView, not a modified DropTarget. DropTarget is an interface class, which makes it possible for you to choose which classes are actively part of the drag-drop protocol. If there were lots of different ways objects can be dragged in your app, you’d likely appreciate having an interface class, as opposed to having all DropTarget be one specific class.

      You wouldn’t necessarily have to do something like ImageCell, which is part ImageView and part DropTarget. Another way would have been to define a new class that implements DropTarget and simply use the standard ImageView in the grid. Your new class would then have to keep track which ImageView it was working with so it could update the view with the new image dropped onto it.

  6. steller says:

    I have a question regarding your xml gridview.

    Where are these specified? ( background and the num_colums)?
    I mean they are being set somewhere? in R? Can i add that to R?

    android:background=”@color/grid_background”
    android:gravity=”center”
    android:horizontalSpacing=”2dip”
    android:numColumns=”@integer/num_columns”

    Also, in your imagecell class you specify
    R.color.cell_empty : R.color.cell_filled;

    Can i edit the R file and add a reference to that?
    If not where is it specified that it will automatically be generated?

    Example:
    public static final class color {
    public static final int drop_target_color1=0x7f050008;

    or is that defined somewhere else and R generated it? If so where do i specify it?

    Great demo BTW..

  7. steller says:

    Is there a way i can change the package name to my own?

    • blahti says:

      If you highlight the package name in Eclipse and then right-click, you will find a Refactor menu. It has a Rename item that you use to rename a package. That works well, but some of the warnings and options can be a bit confusing if you are new to Java and Android. If that’s the case, you might want to start with a new Android project and copy over code from the demo into your own. Building it up from an empty project is a good way to learn how it works.

  8. steller says:

    Is there a way to change the background color on the grid?

    • blahti says:

      Look inside file res/layout/demo.xml. You will find a GridView definition that sets the background with a reference to “@color/grid_background”. Then check res/values/mycolor.xml to see the definition for grid_background. Change the color and watch as Eclipse rebuilds the generated R file. The next time you start the app, the new color is used.

  9. cbee says:

    How can i access my Activity class method from ImageCell class. Can you please help me? I got illegal state exception while trying to access it by using new keyword( MyActivity myactivity=new MyActivity() ).Waiting for your reply.

    • blahti says:

      You should be able to get to your activity with “Activity a = (Activity) getContext ()”. Generally speaking though, it is better to keep your views decoupled from the activities that use them. Usually you can do that through by setting up the activity to listen to clicks and other events in the view. In this demo, you might want to study ImageCellAdapter and see how it creates the ImageCell objects. Notice that each image cell knows what grid they are on. The grid knows its context so that’s another way to get to the activity to which all the cells are attached to.

  10. steller says:

    Thanks for all your replies. Another question is about the xml drag layer. I see you made a custom drag layer from the drag layer class? Are the images dropped onto the custom drag layer? Why are they not dragged onto the grid directly? What is the purpose of the xml layer? Does it sit ontop of the grid?

    Thank you so much! What a great demo you have…

    • blahti says:

      DragLayer is a ViewGroup that contains all the other views. You have to have a view that encloses all the others in order to support drag and drop. It has to do with how events work in Android.

  11. steller says:

    I have a question regarding your xml drag layer. Is the custom drag layer created from the drag layer class? Are the images dropped onto that custom xml layer? If so why are the images not dropped onto the grid itself? Does the layer sit on top of the grid?

    Thank you for all your replies..

    • blahti says:

      The DragLayer is there to handle all the events that occur within it. It turns over the work to a DragController. The DragController then interacts with DragSource and DropTarget classes. If you follow through what is in ImageCell, you will see code that does indeed copy the image into the cell. So I would say that the images are dropped onto the grid, if I understand your question correctly.

  12. steller says:

    Could i do away with the button and add like 3 image views and load the images as the program loads and then drag anyone of those images onto the grid? What would i have to do to those images to get them to drag as the ones that are loaded with the button?

    • blahti says:

      Have a look at Part 3 of my notes on drag-drop. It had some views that were already on the screen that could be dragged. Basically, all you have to do is arrange for existing views to be set up properly and they work just like the dynamic ones. I did that in the onCreate method of the activity.

  13. jim says:

    Once the pics are on he grid is there a way to see what grids are empty and which ones are not? example: If i drop pic1 on grid cell one could i return if the cell is not empty and maybe something about that pic? so if i save that info that if i drop the same pic in the same cell i could match it?

    I hope i made sense?

    • blahti says:

      Every ImageCell objects knows whether it is empty or not. See method isEmpty. To get your hands on all the cell objects, you could do something like what was done in the onDragStart method of DragLayer. It goes through all the child views, looking for drop targets.

      As for using the information about what’s in the cells, I think you should consider adding another data structure and have the ImageCellAgapter and GridView work with that. I built this simple example to illustrate the basic aspects of drag-drop for a grid. In a more complete application, I usually try for a MVP (model-view-presenter) separation between user interface and the data structures I want the ui to interact with. If you look at examples of various adapter objects, they usually have the adapter getting information from a database (or some other data structure) and building views of that data. So if you find you can make a few simple changes and get what you want, do it. However, if it starts to get very confusing, you might want to stop and come up with a separate data object.

  14. nick says:

    First of all awesome tutorial.
    Secondly if you could tell me how i can implement the dragging of a layout which includes an imageview and 2 textviews
    i have inflated my layout and have succeeded in showing it on the grid but when i start the drag it force closes when initializing dragsource with the error “java.lang.ClassCastException: android.widget.LinearLayout”
    If you could just point me in the right direction that would be great.

    • blahti says:

      At the start of every drag, the DragLayer sets itself up with the list of drop targets for the drag operation. That is in method onDragStart. You will notice in the loop there, the code assumes that all child views of the GridView are DropTarget objects. That works fine in my demo because I knew they would all be ImageCells and a trash can (DeleteZone). I made ImageCell be a subclass of ImageView. In fact, there is not much there at all — just enough to implement the DropTarget and DragSource interfaces. If you are going to be dragging around something more complex than an image, you are going to need to ask: What gets dragged and what happens when it is dropped on the grid? In my demo app, the image view does not actually move. It only looks like it is moving. Check the code in ImageCell and you will see that the action taken is to take the Drawable and assign to the target cell (see the call to setImageDrawable in method onDrop). At the same time, the source cell gets its Drawable erased (see method onDropCompleted). I know the names are a bit confusing because ImageCell implements both interfaces, but when the code is running it is two different cells that have those method calls so the Drawable transfers from one cell to the next.

      Given you want to have something with more detail dragged, I’d suggest making a subclass of LinearLayout that does what my ImageCell class does. You might call it “ImageCellWithText”. It implements DropTarget and DragSource. The code there knows exactly what to do when a drag ends. In your case, it sounds like you have three pieces of information that you want to transfer: a Drawable and two pieces of text. Put code in onDrop and onDropCompleted that does that. Then arrange it so your GridView is set up with a set of empty ImageCellWithText objects and figure out how you get them onto the grid initially (add button, for instance). Basically, what I’m saying is be sure you understand the interfaces and why ImageCell is a subclass of ImageView and why ImageView alone will not work. Then do similar things for a special subclass of LinearLayout.

      • nick says:

        ohh my god, u have no idea how big of a help you have been to me.
        I was going completely on the wrong path.
        My mistake was i was inflating your imagecell class to include 2 more textview, even though i succeded in doing that, the drag drop functionality was broken.
        “I’d suggest making a subclass of LinearLayout”…this line here just saved me hours of coding and debugging.
        Thanks to tutorials like these, we beginners can learn so much.
        Seriously thank a lot for replying.

      • blahti says:

        What I’ve always liked about Java and OOP in general is that sometimes the object model you are working with turns out to be fairly easy to extend and adapt. Maybe that’s the case here. In any case, happy to help.

  15. cbee says:

    Thank you for your guidance.
    In my project i have to drag both images and text. I need the ImageCell class to have the properties of both Text View and Image View( setText and setImageBitmap).Is this possible? If you have any idea please guide me.

    • blahti says:

      See the questions and responses between Nick and myself above. I think what he is asking is just about what you are asking. You should be able to come up with a LinearLayout subclass pretty easily that would support both an image and some text. Alternatively, you could work with the ImageCell class and extend it to support text (add getText, setText). With the second approach, you’d also be extending some of the standard ImageView methods so the text would show up where you want it to.

  16. Pingback: Revue de l’actu Android pro du 7 octobre 2011 | Paris Android User Group

  17. KenichiQ says:

    Thank you for your guidance.
    In my project i have to drag and drop images, and animate moveing of the images if image are flinging. Can you tell me how i can use GestureDetector and GestureListener in your application? If you have any idea please guide me.

  18. Miroslav says:

    Hi Bill,

    First of all thank you for a great article, it really helped me a lot.

    Now, I wanted to enhance this a bit and enable the vertical scrolling if the grid view is too big to fit the screen. I wanted to be able to scroll the grid automatically as you drag the cell near the top or the bottom of the screen, can you help me with this, maybe just point me in the right direction?

    Thanks a lot,
    Miroslav

    • blahti says:

      I have a feeling that will be a bit tricky. A place you can look is the code for Android Launcher. I studied that when I was building an earlier drag-drap demo. That demo is based on the Android Launcher. The original code triggered scrolling when the dragged object got to the edge of the screen. Perhaps that will help you. At the time I did that work, I think it was the Android 2.2 source code that I downloaded from http://source.android.com/.

  19. Priya Kathir says:

    Hi Bill,

    Thanks a ton for your amazing tutorial..

    Am new to Android and your tutorials helped me take a big leap in my work.. My sincere appreciations for your quick responses to queries too. Most of my doubts are also clarified in your answers to developer’s queries..

    Thanks again..
    -Priya Kathir

  20. Oddie says:

    How would you adapt this for let say 3 separate GridView’s on the screen. Each one representing a category. I have three separate categories two of them with 4 possibilities and 1 with just a single. Using 1 gridview would make it difficult, but using 3 would work fine.

    • Oddie says:

      Well it looks like I can only have 1 gridView as I tried to get three to work, but using the DeleteZone as a guide, I may be able to setup a custom class that will allow me to have additional drop targets. If anyone else has any idea, I welcome it.

      • blahti says:

        I don’t see why there can’t be more than one GridView on the screen. Since each is an independent view object, I do not see where actions in one would affect the others. That assumes too that there is separate adapter object for each view. Of course, I am just thinking about the visual side of things. We let users drag things around on a screen, but the underlying code is doing something else a little more long-lasting, like making entries in a database or updating the shared preferences data.

        I’ll give this a little more thought.

  21. Oddie says:

    I had all three gridviews on the screen, with their own adapters, but the setGridView can only be applied to one. I tried to apply all three but only the last one was active. Unless I am doing something completely wrong.

    • blahti says:

      The DragLayer is the view group in which all dragging occurs. For the purposes of my app, I needed only one GridView in my DragLayer. For what you are building, it sounds like you need three. You’d add them in the layout xml file and also to your modified version of DragLayer. When your activity starts, add all the grid views in onCreate. Then change your DragLayer’s onDragStart method so it adds all the DropTarget views from each of the three grid views. If you do this, I think objects will move just fine from GridView to GridView. The new GridView views will be no different than the trash can view, which is also set up in onDragStart.

      • Oddie says:

        I did manage to fix it. With your suggestion, I added an additional setGridView method to the Drag Layer. I added two additional Gridview variables to the Drag Layer class and attached them to the new method then repeated the code in the onDragStart method to add the additional drop targets and it now works. My next task is to determine which of the three gridviews my image is in. I have an idea on how to do it, but will probably tackle that tomorrow. Thx again.

  22. amjed says:

    Hi blahti,

    I want to have a table layout where i can move the columns\rows, If not entire column\row atleast the headers. Can you please suggest me how to go about it.

    Regards,
    Amjed

    • blahti says:

      Moving an entire row or column will be a bit tricky. This particular framework for drag-drop originated with code from the Android Launcher. It is biased toward moving a single View at a time. That is most evident in the code if you find the lines where the DragController starts the drag. It asks the View it is over to return a copy of its bitmap and that is what we see moving on the screen. When you lift your finger, the drop occurs. In this demo, I did not actually move a View object, but simply took the contents on one cell and moved it to the contents of another cell. (An earlier demo actually had the View move from one spot to another in the DragLayer.)
      Here are a few suggestions for you. Think about what visual feedback you want to give a user that an entire column is being moved. You’d want to have either a generic image for a column to be used instead of the bitmap that the DragController builds now. I’d also think you’d want to have the whole column show some new highlighting color to indicate that it is what you are moving. That won’t be too hard since you know which cell the drag is being initiated. Change the ImageCell code so each cell knows its column number besides knowing its cell number. When the drag starts, arrange for the DragSource to know it is starting. One possibility is to extend the DragSource interface and have the DragController call whatever new method you come up (perhaps, onDragStarted). Another option is to work with the existing methods in DragController.DragListener, which DragLayer implements. In the second case, the DragLayer gets informed that a drag is starting. It could look at which cell is being moved and highlight the whole column. At the end, the DragLayer would turn that highlighting off. Then you have work to do at the end so the whole column (or what’s in them) moves from one place to another. That’s tricky too, if you are doing an insert, because you might have to alter your GridView on the fly. Still, I hope you see that it might be possible to work all this in within this framework.

      I find the most important thing with frameworks like these is understanding the basic responsibilities of the different objects involved. When you come up with an extension idea, think of it as a new requirement, expressed as a responsibility for one of the existing objects. Ask: Which object is the most logical place to add the new responsibility? Then ask: what do the collaborating objects now have to do to support that? If it works out, you improve the framework and the code never gets out of control. I hope this helps. Let me know how it turns out.

  23. Rolf says:

    Congrats for the tutorial!
    I’m trying adapt it for a college project. I need to make something like that:
    http://dl.dropbox.com/u/13329356/example.png

    Can somebody help me saying where should I change the code in order to add to each piece of the gridview a text (and mantains also the dragSource)?

    • blahti says:

      Your picture looks like you are trying to drag an image onto a list. So I’d suggest something like this.
      Build a ListView app that has the appearance that you want for the end result. Then study this GridView example so you understand the DragSource interface, the DropTarget interface, and why there is a custom subclass of ImageView being used. Then go back to your ListView app and see what layout you inflated to go in the ListView. Replace it with a custom class that implements the DropTarget interface. You will also want to figure out what your top level view group is. You will want to replace that with a DragLayer of your own, just as this one has a DragLayer within which drag-drop occurs. Find the spot (onDragStart) in this demo app where the DragLayer sets up the set of drop targets as the drag begins. You will want to do something similar. In your case, you’d sweep through your list and add each view that is showing a list item.

  24. Chandra Sekhar Nayak says:

    Thanks a lot Blahti, you can’t even imagine this blog helped me.

  25. peter says:

    thanks somuch for yr share! but when i try it with gridview item only then i can’t run my app, i can’t drag item of gridview on longclick… can u help me make a demo just for gridview item… (No have add new image)… waiting for u… thanks so much… sr for my english (i’m vietnamese)

    • blahti says:

      Does the existing demo not work at all? Or does the drag start and something fails and shows up in the Logcat window? Please provide a little more information.

  26. peter says:

    Thanks Blahti!

    http://ni8.upanh.com/b5.s15.d1/5f0da83b233b82dc079c9ca49c66221d_41223318.untitled.jpg

    my problem like that:

    – I have a gridview with existing item
    -now i want to move item when i onlong in this to another places….

    do you have simple code like that???

    • blahti says:

      If you are asking how to move an item after it has already been placed on the grid, the current demo illustrates that. Each cell in the grid is both a DragSource and a DropTarget. Try it in the demo program. Put something in a cell. Then touch it again and move it to another cell. The thing to do is study ImageCell and see its methods for the two interfaces. Then check your code to see that your view does too.

  27. Chandra Sekhar Nayak says:

    Hello Blahti, I am using your code to develop my project. My project is a little bit extension of this, in which after adding images to all cells of the grid, when I drag further and drop it in a cell already with an image, then it should be dropped there and the image of the DropTarget cell should come to the DragSource. I have already removed the mEmpty variable of ImageCell class. Please help me where I need to change the code in order to resolve my issue.

    • blahti says:

      See my previous response above to Peter. I think he is asking the same thing you are: how to move something once it is on the grid. I think this demo program supports moving something again. When you see the problem, is it in new code of your own or in the original demo? If it is only in your code, you’ve left out something. Check what you have that corresponds to ImageCell.

  28. peter says:

    Thanks Blahti, i was understand and i was complete my app, thanks so much for yr help, :)

  29. C S P Nanda says:

    Hi Blahti
    Excellent post. I am trying to remove the border around the image after it is dropped on a cell. I tried to set the padding to zero, but that did not help. How can I make the image cells in the grid view without any space ?

    • blahti says:

      When I put images into views, I have to be very careful to match the aspect ratio of the image. Sometimes that cannot be done, and I have to use one of the different ways to scale the images. See scaling.

  30. Rolf says:

    Hi,

    Can somebody tell me what I should to do if I want that an image was dragged by a single touch and not by a longClick?

  31. gerrit says:

    Hi Blahti,

    Many thanks for your example.

    I was trying to implement a user configurable menu in one of my apps, so the user can place its own menu options on the screen.
    Took me a whole evening of work, but without succes ;-(

    Your code seems perfectly suitable to do the job.

    Thanks again.

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

  33. Oddie says:

    I ran into an interesting issue. I am not trying to not use the longClick method for the drag and drop, but use a onTouch method.. Works ok until you touch one of the empty cells on the gridview. Any idea how to fix that? Did it mainly because of not wanting to wait for the long click to take hold.

    • blahti says:

      Looks like I made a little mistake. It seems like the DragController should be asking the DragSource if it has anything to drag. On the other end of the operation, it asks the DropTarger if it is willing to accept the dropped object. I am extending the DragSource interface with an allowDrag method. Look for an update here shortly.

  34. Sameer Aziz Mirza says:

    Hi Bill,

    I’ve been using your code for a project but I’m a little stuck with something. I’m using the GridView to add letters to it. Now, I would like to drag letter-chains (or words) together. I can detect what cell the word starts and ends but how do I move the multiple views together and drop them correctly?

    Hope you can help :)

    • blahti says:

      This drag-drop framework relies on there being a single view that can be dragged around. Clearly, that is not the case if you have cells filled with individual letters. I wonder if you have to do something like in the image gallery app. There, you have to first indicate which pictures are of interest before you can do something with them. What if you had users first indicate which set of letters they were interested in. If you did that, you might have the opportunity to treat the next drag operation in a special way. You could make it so the next drag appears to take more than a single cell to drag around on the screen. Then when the drop completes, your DropTarget implementor would grab all the “checked” letters and do the right thing so the user sees the desired effect.

      A problem is that the current DragController does not allow for the DragSource to substitute a custom view for the DragView. The DragController just builds one on its own. Take a look at startDrag in DragController. See how it asks the DragSource if there is anything to drag, via a call to allowDrag. What if, after that, it asked the DragSource if it has a custom DragView image to use? That would allow your custom DragSource to substitute a view you construct as the drag begins to show the letter chain.

      This is far from a complete solution, but I hope it gives you something to think about. Think in terms of the responsibilities that the various classes have now and then decide which object(s) should be enhanced to take on the new responsibilities of moving a set of letters around. Saying that gives me another idea. You do not actually have to move multiple views around the screen. Your user does not care if views actually move; your user expects to see a set of letters moving together. Make it appear to be so. That might be possible without too much warping of the underlying framework.

      • blahti says:

        One more thing: please see the comments of Amjed above (Feb 14). He says that he inflates a view so there is something draggable that holds all elements of a row. You could do something similar. Inflate a view that holds however many letters you need and then let that be the DragView.

  35. rangezero says:

    Hi blahti.I managed to create drag and drop activity successfully but the only problem i faced now is if I drag my imageView more than about 30 times, the drag event will stop initiate and I am unable to drag any view anymore. What is the problem?

    • blahti says:

      I don’t know your exact situation and whether you are talking about the demo app or your variation on it, but there are a few things I’d suggest looking for. Whenever images are involved, there is always the possibility of having too many of them at once and triggering an “out of memory” exception. That’s happened to me often enough that the first thing I do is check the Logcat window to see if that exception has occurred. For a real application, we have to make sure that we are managing the images correctly and releasing them when we are done with them. I think my demo does this, but I might have missed something. I will check.

      • rangezero says:

        This is the logcat for my problem:

        04-05 14:55:28.455: E/View(2484): Unable to initiate drag
        04-05 14:55:28.455: E/View(2484): java.lang.IllegalArgumentException
        04-05 14:55:28.455: E/View(2484): at android.view.Surface.lockCanvasNative(Native Method)
        04-05 14:55:28.455: E/View(2484): at android.view.Surface.lockCanvas(Surface.java:335)
        04-05 14:55:28.455: E/View(2484): at android.view.View.startDrag(View.java:11210)
        04-05 14:55:28.455: E/View(2484): at learning.apps.utm.bajupage.onTouch(bajupage.java:84)
        04-05 14:55:28.455: E/View(2484): at android.view.View.dispatchTouchEvent(View.java:4596)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1494)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1227)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1494)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1227)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1494)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1227)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1494)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1227)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1494)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1227)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1494)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1227)
        04-05 14:55:28.455: E/View(2484): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1700)
        04-05 14:55:28.455: E/View(2484): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1270)
        04-05 14:55:28.455: E/View(2484): at android.app.Activity.dispatchTouchEvent(Activity.java:2277)
        04-05 14:55:28.455: E/View(2484): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1680)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewRoot.deliverPointerEvent(ViewRoot.java:2272)
        04-05 14:55:28.455: E/View(2484): at android.view.ViewRoot.handleMessage(ViewRoot.java:1958)
        04-05 14:55:28.455: E/View(2484): at android.os.Handler.dispatchMessage(Handler.java:99)
        04-05 14:55:28.455: E/View(2484): at android.os.Looper.loop(Looper.java:126)
        04-05 14:55:28.455: E/View(2484): at android.app.ActivityThread.main(ActivityThread.java:3997)
        04-05 14:55:28.455: E/View(2484): at java.lang.reflect.Method.invokeNative(Native Method)
        04-05 14:55:28.455: E/View(2484): at java.lang.reflect.Method.invoke(Method.java:491)
        04-05 14:55:28.455: E/View(2484): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
        04-05 14:55:28.455: E/View(2484): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
        04-05 14:55:28.455: E/View(2484): at dalvik.system.NativeStart.main(Native Method)

        The application I doing involve drag and drop something like this:
        http://fupeg.blogspot.com/2011/03/honeycomb-drag-and-drop-basics.html

        The image I using is small in size. I just keep repeating dragging the same thing without putting in the container for about 30 times and i get this error and I unable to drag it anymore. Android version is 3.0.Hope you can guide me on this error.I have studied for this problem for whole week.

      • blahti says:

        I tried starting a drag about 20-30 times in my original demo app. I see nothing unusual. I thought maybe there would be something worth seeing in the logcat, but no. What I see at the end is about the same as what I see as I start.

        04-09 06:53:59.590: D/SntpClient(71): request time failed: java.net.SocketException: Address family not supported by protocol
        04-09 06:54:02.170: D/dalvikvm(379): GC_EXTERNAL_ALLOC freed 90K, 51% free 2651K/5379K, external 1679K/1709K, paused 68ms
        04-09 06:54:11.640: D/dalvikvm(379): GC_EXTERNAL_ALLOC freed 45K, 51% free 2647K/5379K, external 1812K/1816K, paused 80ms
        04-09 06:54:23.960: D/dalvikvm(379): GC_EXTERNAL_ALLOC freed 51K, 51% free 2648K/5379K, external 1765K/1777K, paused 69ms
        04-09 06:54:33.500: I/dalvikvm(71): Jit: resizing JitTable from 2048 to 4096
        04-09 06:54:40.470: D/dalvikvm(379): GC_EXTERNAL_ALLOC freed 62K, 51% free 2650K/5379K, external 1736K/1741K, paused 78ms
        04-09 06:55:15.420: D/dalvikvm(71): GC_CONCURRENT freed 674K, 45% free 4363K/7815K, external 1316K/1828K, paused 15ms+16ms

        Memory seems to grow as I add images. Dragging does not show anything that I would interpret as a memory leak. You might want to try using the Android performance analysis tools. There might be something in your code that is not releasing objects once allocated. Then after enough times, you run out of memory. Do you see anything odd in your logcat prior to the error.

        Links to performance tools information are in the References section at the end of my notes on performance tuning.
        Sorry I don’t have much more to suggest right now. Let me know if you find anything.

  36. rangezero says:

    Hi blahti,Thank you so much for noticing my problem.Actually I implement finish() in the onPause() to end each activity and release the objects in activity.Is this method correct to release all the objects like images?Or finish() does not release the object?I keep on seeing this error line in the logat:

    Surface.finalize() has work. You should have called release() (1720472, 0)

    If this method is wrong, can you show me the correct way to end each activity and release all the objects of that activity?So sorry for this because I am just a beginner and have a lot to learn.

    • blahti says:

      If you are making a call to finish() in your onPause or onDestroy method, that’s not correct. If you want to have an Activity end early, you’d have the call elsewhere. For example, you might have a button that the user touches that does something and then ends the activity, in order to go back to the Activity that started it. If you have objects to release, it would make sense to see those in onPause or onDestroy, if I understand your question.

      If you are new to Android and want something to go through related to activities and the activity lifecycle, read my tutorial on activities.

  37. cathy says:

    hi Bill,

    i would like to use your code for my project, but have 2 problems
    – i include demo.xml in my main.xml but when run application grid does not appear and when ‘AddImage’ boutton clicked – application crashes with NullPointerException on onInterceptTouchEvent(MotionEvent ev)
    – i would also like to know how to modify DragActivity -> i’ve got a list of buttons in my application and i want to add their’s image to a grid when dragged & dropped (i.e use my buttons instead of images created by addNewImageToScreen method)

    would be very glad if you could help me

    • blahti says:

      It sounds like you are starting to copy pieces from my demo program to yours. While doing that, it’s easy to miss some of the pieces. You will want to take a look at the Logcat window that is of the DDMS part of Eclipse. DDMS is under “Window – Open Perspective”. In the Logcat window, you should see an explanation of where the null pointer exception occurred.

      As for moving buttons, you should study the parts of the code in this demo that have to do with which touch events initiate a drag sequences. For buttons, click means something. So you will want to have it so that dragging starts with a long click (press). In the code, you would not want to do the part that says v.setOnClickListener.

      • cathy says:

        hi, thanks for your answer
        not copying, i’d like to integrate and adapt your demo as a part of my project
        i think i didn’t explain well what i’d like to do: i have an expandable list with buttons, and a GridView where i wish to be able to add copie of buttons from my list (if i drag and drop a button, it’s a copy of button that’s added to a GridView but button in my list stays )

        i wanted to to something like
        childButton.setOnTouchListener(new OnTouchListener () {
        @Override
        public boolean onTouch(View view, MotionEvent me) {
        childButton.setDrawingCacheEnabled(true);

        if (me.getAction() == MotionEvent.ACTION_DOWN) {
        status = START_DRAGGING;
        image = new imageView(childButton.getContext());
        image.setImageBitmap(childButton.getDrawingCache());

        ….
        });

        i’ve got a imageView of my button, but i don’t know what do afterwards to add it to a grid like you do for image that is in image_source_frame
        regards

  38. Monika says:

    hey, how could I keep information about ImageCell, especially id ? In DragActivity i setId() with random value, but when i move picture in GridView i lost this id. I know that i should set new value in ImageCellAdapter, but i need set that same number what is in DragActivity.
    Hope you can help

    • blahti says:

      I may not fully understand what you are trying to do. If you want to save additional information besides the id, add extra instance variables and methods in ImageCell. Then use those during and after a drag-drop operation. If you want to save the current state of all the information in the grid objects, you might consider using SharedPreferences.

      • Monika says:

        I have a few images on screen, but I want move it to a GridView, and then check whether they are in the right cells, like a puzzle.

      • blahti says:

        Perhaps you could assign a puzzle piece id number to each image. When a piece is dropped on the grid, store its puzzle piece number in the cell. Then check to see if the pieces in the cell are in the right order. Something like that?

      • Monika says:

        Yes, something like this. In class ImageCell in method onDrop i check
        View v=(View) source;
        if(mCellNumber==v.getId()) trace(“OK”);
        It’s works ok until i don’t move between GridViev cell.

  39. MAGA says:

    I have one question, I made the gridbigger bigger by adding name=”num_images”>30… so I will have to scroll to add more images, but when I scroll it the images that I droped in the gridview start dissapearing and appearing in random spaces. Do you know why is this happening?

    Thanks

    • blahti says:

      I see what you mean. It looks like I did not handle the case where the grid is too large to fit on the screen. The problem is in ImageCellAdapter’s getView method. It is not recycling views correctly. When I have a correction, I will post a fix.

      • MAGA says:

        Thanks, I will be waiting for it.

      • MAGA says:

        Any news so far?

      • blahti says:

        Part of a fix requires adding “v.setImageDrawable (null);” to the getView method of ImageCellAdapter. That keeps the images from seeming to jump around from one cell to another. To fully support a scrolling GridView of images, there will have to be another data structure to keep track of which images are in which cells. getView needs access to that so it can reset the image drawable to the drawable that is already in the cell.

  40. Harry says:

    Hi There, thank you for your tutorial, how could I start another activity after the cell has dropped into position on the gridView?

    • blahti says:

      Find the place in ImageCell that handles the end of a drop. Since that class implements both the DragSource and DropTarget interfaces, you have two choices: onDrop or onDropCompleted. If you need more information about activities and how to start them, I have a few examples here: activities.

  41. Marius says:

    Hello,

    Did you made the drag-drop framework described on the Android developers’ website?

  42. Sameer Aziz Mirza says:

    Hey Bill. How would I go about scaling up the DragView a bit? I want the view which is being dragged to be scaled up a bit (I think that’s the DragView). But the DragView class seems to be implemented in a way where its scaled to fit the bitmap size??

    • blahti says:

      In DragView there is a variable mAnimationScale. Its value is 0.9f. Changing that value makes the DragView larger. There is also a constant: DRAG_SCALE. It adds padding around the drag view. Experiment with those two values and see if that gives you the control you want. When you are in that class, you will see notes about what the original Launcher code did. That’s where this code originated. For more on that, see Part 2.

  43. MSM says:

    Hey, thanks for this amazing tutorial. Its working awesome!! But I have one question, I want to add a Id to each Image added so I can access another par of the App. I managed to set a Id to the images, but the Id added is the last image clicked, for example.

    I set in the DragActivity.java

    In the addNewImageToScreen(int resourceId) I added newView.setId(m)
    Where m for hello = 1, photo1 = 2 and photo 2 = 3;

    Then in the onClick(View v) I added a toast so I can see what id it have, something like this:

    fotoId = newView.getId();

    toast1(“Item clicked have an Id of ” + fotoId);

    But the Id I’m getting is from the last image added. If I added hello and then photo1 and click hello it says: Item clicked have an Id of 2. Instead of 1.

    Could you be able to help me out here?

    Many Thanks

    • blahti says:

      Two suggestions:
      1. Most adapter objects have something built in to recycle views. Whenever I have had a problem with values in views not being what I expect them to be, it has almost always turned out to be the case that I forgot to clear out or reset one of the extra fields in a recycled view. However, that gets me when an orientation change forces the the list or grid view to reset itself. That might not be your situation.

      2. You might want to check the onDrop method of ImageCell. It does not take the actual view object and add it to the grid. It takes information from the view being dragged and transfers it to the target ImageCell. If you have added another field for id, its value is set in only the view that is moving. You would need to do a call to setId inside onDrop so the ImageCell gets your new information.

      • MSM says:

        I set the orientation to not change, so I will not have problem with that.

        Ok I have been trying to add the setId to the onDrop in ImageCell and I get the same results of getting the Id of the last Image added or I can’t get nothing else but errors. I just don’t understand or know how to set the Id so my ImageCell gets it. Can you please be a litle more specific?

        Thanks

      • blahti says:

        In addNewImageToCell, you take a new view and set its id with a call to newView.setId. The original code also called setImageResource. So there are two key pieces of information. The drop handling code that transfers that information to a cell on the grid is in ImageCell’s onDrop method. It has a line that takes care of the image resource: “this.setImageDrawable (d)”. If you want to carry forward any new information from the drag source view, that seems like the place to do it. Try adding something like: “this.setId (sourceView.getId ())”. (I am assuming that setId is defined with ImageCell.)

  44. MSM says:

    Ok I did it in the ImageCell now the Id is added only when the image is dropped, but I’m still getting in the toast the Id of the last Image added. Like before. Is just like the Id is rewrited with the last Image added Id, any other idea? I can send you part of my code if you want to check it out.

    • blahti says:

      I’d suggest running in the debugger and looking very closely at the view where you set the id and the view where you are having the toast message displayed. In the debugger you should be able to see a print string for the view. That comes from the internal object address. My guess is that you will see that you are looking at two different views.

      • MSM says:

        Ok I solve it I have to ise setTag not setId, I don’t know why but setId didn’t work

  45. azerto00 says:

    Hi, thanks a lot for this tutoriel !
    Is there a way to make image in the gridview next to each other ?
    I’d like to make a puzzle (for images) and i don’t want to see margin between them.

    Thanks

    • blahti says:

      Look in the demo.xml file in the res/layout folder. There is a definition there for a GridView. Horinzontal and vertical spacing can be controlled in there.

  46. 衛斯里 says:

    Great tutorial, sir. If you’d be so kind as to help out a complete Android newbie, I have q question: I know that to transfer data with drag and drop, the data to be transfered will be stored in the Image Cell. But if I want to, say, read the data in each ImageCell and print them onto the screen, then where should I put the code?

    • blahti says:

      It’s actually a bit more complicated than that. I would not necessarily say that you store data in the Image Cell. An image cell is a view. It should store whatever it needs so the views the user sees are right. Connected to those views, usually via the Activity, would be other data structures that store the stuff that your app is modifying for the user. Generally, I suggest putting the code to do things to the data structures in the Activity. It might not be all the code. It might be that you want a separate class to do the work, but still the activity is a good place to make the connection between what you see on the screen and the data structures being updated. For printing, you might find that it is good to have Activity 1 provide the user the control (button, perhaps) that gets the print started. The code attached to the button might start Activity 2 to display what’s printed on the screen.

      Some references that might help you: intro to activities (mine, Android developer website), Model-View-Presenter. Sorry, that last link is just a Google search. I don’t have a favorite reference yet for MVP related to Android.

  47. HongWoo Jin says:

    Good tutorial, I’m new android develpoer.
    Is it possible to drag and drop on the gridview which have alreay set 18 images? if so , please guide me

    • blahti says:

      Check my comments (May 24) on this related post. It talks about how you have to change your existing GridView so it uses a custom view for the images. The custom view must implement the DragSource and DropTarget interfaces. Everything has to be inside the DragLayer too. Study the code here on this tutorial to see where the adapter adds view to the grid. Note that it sets up click handlers and touch event handlers. You will need to do the same thing.

      • HongWoo Jin says:

        Thank you for your explanation, but I’m junior android developer, could you give me make examples , if so could you mind creating full source for reference. I’m working study this code , but it’s difficult to me. sorry to bother you

      • blahti says:

        My tutorials should help you. This one shows very clearly how to set up a GridView so views within in can be dragged. Work through that example, running in the debugger and follow the flow from the moment you touch the screen to the point where control passes to the DragController. Then go through and understand how DragSource and DropTarget methods work. Breakpoints in some of those methods help there. Then understand how the GridView adapter creates views to show up on the screen. See how it uses ImageCell objects. Then go back and study your existing GridView, the one that already has elements in it. You will want to make that GridView lie inside of the DragLayer and have it use something like ImageCell objects too. Work through it all carefully. Drag-drop is a bit difficult until you understand the roles and purposes of each of the different objects in the framework.

        One more thing: Following touch events in the debugger can get confusing once the drag starts. To see what’s going on then, add Log.d statements and check the Logcat.

  48. HongWoo Jin says:

    Drag-Drop for an Android GridView tutorial explains if click buttons specific image shows on imageview then drag and drop to gridview, but I want to make gridview which already have images then I try to drag and drop, but even if I added ImageCellAdapter.java to “v.setImageResource(mThumbIds[position]);” , then it shows gridview had set images but it cannot select then move as drag and drop, if so please guide me.

  49. Sameer Aziz Mirza says:

    Hi Bill,

    Thanks for your help earlier. I have another question, Is it possible for a gridview to group cells of uneven width and not stretch them? For example, I would like to show letters which are not of the same width and yet I dont want any spacing between them. I dont even want the columns to be of the same width. Could you, please, point me in the right direction for this?

    Many thanks!

    • blahti says:

      I don’t know about GridView. My guess is the answer is no. I wonder if TableLayout might work for you. It says in Common Layout Objects that you have some control over cell widths. I have not tried that layout either, but perhaps it has features like tables in HTML where you can set up some cells to span several columns.

  50. HongWoo Jin says:

    Hi, Bill

    No matter how I debug this source , It makes me frustarted. I think it need to be handling Eclipse more freely in my opinion
    Even if I changed ImageCellAdapter ‘s getview , to show image on gridview from “v.setBackgroundResource (R.color.cell_empty);” to “v.setImageResource(mThumbIds[position]);” it shows pictures , but it doesn’t work.
    please can you explain more in detail? where to debug, which method?
    If you explain more in detail , I think you and I help each other.
    No matter how I debug DragController , even if I touch gridview image or add button, it works onInterceptTouchEvent method continually,
    thank you

    • blahti says:

      Debugging touch events is a bit difficult. It’s best to experiment a bit to see what works. I usually start out with one or two breakpoints to verify that the right code is getting the events. You won’t be able to proceed in the debugger because you are brought into the middle of the drag-drop sequence. Still you know it gets where it is supposed to. So remove those breakpoints and then add a couple more a bit further along. Repeat until you feel like your flow is right. Calls to Log.d are also worthwhile since you can check those after the fact to see if the code you expect is running. Once you are sure that the touch events are going as expected, you might try breakpoints inside the various methods of the DragSource and DropTarget interface. Look for the ones that do the work — like reacting to something being dropped on them. When you get back to this point, you can use the debugger again because the drag has completed. So add a breakpoint and single-step through your code from that point on.

      My other debugging tip is to compare a working example to your own project. I do this all the time. I find a working example from the Android Developers website (or elsewhere on the web) and I study it thoroughly to understand the code and the flow. When I write my own, even if I am copying large parts of the working example, I leave the example project in Eclipse so I can run it in the debugger and verify an assumption about how it works. Then I look at my own project and figure out what’s different in my project. It takes some time, but eventually I figure out the essential thing I missed when I copied the parts of the working example. So, in your case, you have a working example of drag-drop for a GridView from this blog. Study it, add breakpoints, add Log.d statements, etc. You have your project, which is a variation or extension of the example. Add breakpoints and Log.d in it in similar places. Run them one after the other and compare.

  51. John says:

    Hi bill, what is the code that allow only one image can be in each cell in this example

  52. Omar Chaari says:

    Salut ,Merci pour le tutoriel , mais est ce que on peut glisser des images de GridView à un autre GridView ?

    • blahti says:

      You can drag images from one GridView to another if you set up the framework objects correctly. The DragLayer would have to be the ViewGroup that holds both GridView objects. Cells in each of the GridViews would have to implement both the DragSource and DropTarget interfaces. Look in method onDragStart of DragLayer. There is a comment that says “Build up a list of DropTargets from the child views of the GridView. Tell the drag controller about them.”. The code right below that comment goes through the ONE Gridview and adds the DropTarget implementors to the list the DragController maintains. It seems to me that if it set up more than TWO or more GridViews there, things would work out fine. Or, if one of your GridViews was a source for objects but did not allow drops that would work fine too. As long as DragSource objects set themselves up correctly and relay their click and touch events to an activity that passes them on the the DragController, the framework works.

  53. HongWoo Jin says:

    Hi, bill
    I just added ImageCellAdapter.java to
    public Integer[] mThumbIds = {
    R.drawable.business_order, R.drawable.calendar, R.drawable.commuter_bus, R.drawable.dailynews,
    R.drawable.diet_menu, R.drawable.ecim, R.drawable.electronic_approval, R.drawable.employee_info

    };
    then getView(int position, View convertView, ViewGroup parent) method changes v.mEmpty = true to false and add v.setImageResource(mThumbIds[position]);
    and comment v.setBackgroundResource (R.color.cell_empty);
    then drag and drop on gridview is good to work.
    Before this operation , I try to change DragActivity.java but there are problem about cast error between gridview and view. Normally listener operates on View , but in case of me tried to operate GridView.. :)

    and I have one more question
    If num_images value’s count in string.xml different ImageCellAdapter’s mThumbids’s count , it happens ArrayIndexOutOfBoundsException , on the getview method,

    I just want to make grid view 4*4 , and there are just images about four gridview cell, other cell is empty.

    • HongWoo Jin says:

      Hi, bill
      I changed getview method
      v.setImageResource(mThumbIds[position]);
      to
      if(position < mThumbIds.length) {
      v.setImageResource(mThumbIds[position]);
      }
      else{
      v.setBackgroundResource (R.color.cell_empty);
      //v.setImageResource(R.color.cell_empty);
      }
      then if position's length execeeds to mThumbIds 's count , it shows cell_empty , but those cell_emtpy part cannot drag and drop , how can I drag and drop cell_empty part

      • blahti says:

        If you have a cell that is empty, you could put a completely transparent image in the cell. Then the code would work to some extent, though the user would not get great visual feedback while the drag of an empty image is going on. Perhaps you could handle the special case of dragging empty around in the place that generates the DragView image. Have it substitute an image that the user can see, but make it say something like “empty cell”.

  54. HongWoo Jin says:

    Hi, Bill
    Plz ignore previous my question

    I make adapter for showing images on gridview.

    if position is lower than mThumbIds.length , it shows only images which declared on adapter without any empty cell.

    so as to freely drag and drop like android, it needs to have empty cell, but if I use
    public Integer[] mThumbIds = {
    R.drawable.business_order, R.drawable.calendar, R.drawable.commuter_bus, R.drawable.dailynews,
    R.drawable.diet_menu, R.drawable.ecim, R.drawable.electronic_approval, R.drawable.employee_info };
    it cannot show any empty cell, so I make condition like
    if(position < mThumbIds.length) {v.setImageResource(mThumbIds[position]);}
    then it shows empty cell as bg , but I cannot drag and drop to bg, how can I resolve this issue?

    even if I drag and drop to bg , it shows Log.d messages but it cannot work onDrop method.

    • blahti says:

      I am not sure I understand your question. If a cell is empty, acceptDrop returns true, indicating that it can accept something dragged there. onDrop takes empty into account too. So I must not be understanding your question. When you say “so as to freely drag and drop like android”, what do you mean? Do want to be able to drag an image from the grid onto the some other open space in the app? Something like the little trash can I put in the demo app, perhaps?

      • HongWoo Jin says:

        Hi,
        Sorry to bother you
        I want to be able to drag an image from the grid onto the some other open cell space in the gridview.

      • blahti says:

        The original demo app does this. Press and hold and drag to an empty cell. I assume that you have adapted this to your own app, and it’s not working there. Right? If that’s the case, you left something out as you copied from the demo to yours.
        Or am I still not understanding?

  55. HongWoo Jin says:

    Hi, Bill
    I think your demo is so amazing. I did it as I want it. and I need to more analyze and memorize it. It takes time about 1 month.. hehehe :)
    thank you so much

  56. HongWoo Jin says:

    Hi, Bill

    If I want to use Shared Preferences for storing user dragged and dropped grid view, which value am I using?

    • blahti says:

      This is only a demo app. I have not thought through what it might take to preserve its state. A few suggestions for your app: (1) look at what your grid is holding and what actions you took to get the grid into that state; (2) figure out a data structure that represents that; it could be something as simple as an array of resource ids or image paths; (3) figure out how to save that in SharedPreferences; (4) restore that data structure from shared preferences and reset the grid to match its earlier state. In a game app I did, I always have the activity check shared preferences data first. If it is present and valid, the code does the restore; otherwise, it starts as if it is the first time. It might also help to start out simple with restoring from shared preferences. For instance, save the contents of a single cell and see if you can get that to restore. Expand from there.

  57. HongWoo Jin says:

    If I use ImageCell’s onDrop method, which value am I saving?
    and Is it possible to use Shared Preferecnes on ImageCell?

    • blahti says:

      ImageCell is a subclass of ImageView. It defines a few extra things to record whether it is empty and what its cell number is. However, the most important thing is what image it is holding. In the demo, that is just the resource id of the image that is in it. If you save all of those, you could restore it to its original state. The problem might be in discovering what image it is holding. You might want to keep track of that by adding a new instance variable or by using the tag field that already exists for ImageView. Some people have extended this work by allowing images other than those already in the resource folders. Those would not have resource ids. If your work is like that, you’d want to record the path of where the image comes from.

  58. Andy says:

    Hi blahti,

    Thanks for it wonderful example. I stuck at one place, can you please tell me how I will give click functionality of the each image item of the GridView. I tried to give setId() for imageview during rendering inside addNewImageToScreen (int resourceId) method but I am unable to retrieve it. When image is moving then I am unable to recognize the particular image item. Please give the some piece of code at which method I need to modify ? Also I tried to implement it using position of ArrayAdapter but the position is changing not stable.

    I am waiting for your valuable response .

    Thanks,
    Anil

    • blahti says:

      Try using setTag for the view and storing the integer resource id there.
      Also note the comments I made to Maga above. This demo was designed with a fixed size, non-scrolling GridView. If you have expanded the size of your grid to be larger than the screen size, you will see that scrolling the grid does not work too well. A partial fix is described in my earlier comment.

      • Andy says:

        Hi blahti,
        Thanks for your fast response. I tried what you have suggested. I am using setTag(resourceID) into getView(…) of the ImageCellAdapter class. But the setTag() is assigned for empty slot of GridVew but not the view which one is moving from one slot to another slot. I want to set ID of the ImageView so, that I can recognize it even if it go any slot of the GridView and it should be unique and stable.

        Modified Code :
        ———————
        public View getView (int position, View convertView, ViewGroup parent){
        mParentView = parent;
        ImageCell v = null;
        if (convertView == null) {
        // If it’s not recycled, create a new ImageCell.
        v = new ImageCell (mContext);
        v.setLayoutParams(new GridView.LayoutParams(85, 85));
        v.setScaleType(ImageView.ScaleType.CENTER_CROP);
        v.setPadding(8, 8, 8, 8);
        } else {
        v = (ImageCell) convertView;
        }

        v.setTag(slots[position]); //Added here

        v.mCellNumber = position;
        v.mGrid = (GridView) mParentView;
        v.mEmpty = true;
        v.setBackgroundResource (R.color.cell_empty);
        v.setOnTouchListener ((View.OnTouchListener) mContext);
        v.setOnClickListener ((View.OnClickListener) mContext);
        v.setOnLongClickListener ((View.OnLongClickListener) mContext);
        return v;
        }

        Where,
        int[] slots = {
        R.drawable.tile1,
        R.drawable.tile2,
        R.drawable.tile3,
        R.drawable.tile4,
        R.drawable.tile5,
        R.drawable.tile6,
        R.drawable.tile7,
        R.drawable.tile8,
        R.drawable.tile9

        };

        Please correct it if I am missing something. I am waiting for your positive response.

        Thanks in Advance,
        Andy

      • Andy says:

        Hi blahti,
        I used setTag() according to your suggestion inside of getView() of ImageCellAdapter class, but I am getting the item tag(slot tag) of GridView but not moving view id. Can you please tell me how to give the click event of the particular view among all.

        For example,
        public View getView (int position, View convertView, ViewGroup parent)
        {
        mParentView = parent;
        ImageCell v = null;
        if (convertView == null) {
        v = new ImageCell (mContext);
        v.setLayoutParams(new GridView.LayoutParams(85, 85));
        v.setScaleType(ImageView.ScaleType.CENTER_CROP);
        v.setPadding(8, 8, 8, 8);

        v.setTag(slots[position]);

        convertView = v;
        } else {
        v = (ImageCell) convertView;
        }

        return v;
        }

        I need urgent so, please give some more ideas or modify this code to make it workable. Please don’t remove this post code.

        Thanks and Regards,
        Andy

      • blahti says:

        Look in the onDrop method of ImageCell. There is a comments that says “What we do is copy the drawable from the source view”. The view being dragged does not actually change its position on the grid. It simply creates the appearance of that by copying the drawable from one place to another. At the point the drawable is copied, you’d have to copy any other information associated with the source view. In your case, that includes the tag of the view. Probably this: setTag (sourceView.getTag ()). Put a breakpoint there and verify this, though. At the point of transfer, does the source view have what you think should be there? Is there target cell the one you think it should be?

  59. Mahesh says:

    Hello Bill,

    Your article helped me a lot. Thanks.

    I have made a demo using this reference and worked perfect.
    the only thing i want to know is
    I have used the alphabets instead of images to design the crossword like app. it looks great. i can drag drop the alphabet in grid to form words.
    in coding i also have assigned the ids to each alphabet. Only problem is i am not able to get the reference of the alphabet in particular cell so that i can check whether the word formed is correct or not.
    is there any way to find this id of the individual ImageCell object from grid.
    i tried to get the referance of grid by getGridVIew() method but failed to get the id. Kindly suggest some methods by which i can get that id.

    Thanks once again.

    • blahti says:

      I don’t know if you are still using ImageCell or not. If you are, or if you have some new class like ImageCell, you could add a new instance variable that stores the letters that are being displayed. A string field would work fine or a character. Another thing you could do is use the tag field that is available in every View in Android. Here is a setTag explanation. With something like this, you’d have to make sure that information was copied from cell to cell. That would be in the methods of the DropTarget and DragSource implementation classes. In the demo, that’s ImageCell again.

  60. HongWoo Jin says:

    Hi, Bill
    If I want to use onItemClick on gridview with drag and drop, how can I set of this?
    If I make onitemclick on DragActivity then add comment to ImageCellAdapter’s getview about below code,

    // v.setOnTouchListener ((View.OnTouchListener) mContext);
    // v.setOnClickListener ((View.OnClickListener) mContext);
    // v.setOnLongClickListener ((View.OnLongClickListener) mContext);
    it works onitemclick on activity, but If I add comment this source, it cannot drag and drop at all on image cell.

    please help

    • blahti says:

      Commenting out that code might not be a good idea. It seems like that alone will keep the events from getting where they need to.
      Did you notice that there is an improved version of this demo app? I mention it in the last section of this note. Read the blog post on the improved Drag-Drop for GridView. I think that will help you with how to set up to do drags on click alone rather than long click.

  61. HongWoo Jin says:

    and more question
    how can I adapt to longclick and touch event at same time on the View?

    • blahti says:

      Read the blog post on the improved Drag-Drop for GridView.
      I had another app where I supported left-right (horizontal) scrolling so I had to make it so a simple touch meant that scrolling was to occur so the only choice I had left for zooming in on the details behind the image was to use long-press. You might have to make a similar choice, depending on what you want to go on in your app. It’s all under your control. Sometimes it is a bit hard to follow the flow of which pieces of code get the touch events. I’d suggest adding Log.d statements and verifying your understanding of when things get to the DragLayer, DragController, and your views.

  62. Sameer Aziz Mirza says:

    Hiya again Bill,

    I’m trying to add an animation to the drag view when it is dropped on a delete zone. Not quite sure where to put that animation. Tried adding it in the “onDrop” of DeleteZone.java … also in “onDragExit”. I think the dragview imediately hides/delets itself when successfully dropped somewhere. How can I delay that so that it hides/deletes itself AFTER the animation.

    In my words/letters app. I want the letters to animate back to their original place when dropped on the delete zone. I have the positions. Just not sure how to animate the dragview.

    Thanks for your help!
    – Sameer

  63. HongWoo Jin says:

    Is it able to add setOnItemListener on getView Method?

    • blahti says:

      Yes, you can have a listener for clicks on the grid view. A design decision for you: What does click mean? It cannot mean both click the cell and start a drag.

  64. HongWoo Jin says:

    Hi, Bill
    At last I made a solution about onClick to work.
    and I understood a little about onDrop method in case of if I use shared preferences.
    If I check onDrop method , it shows like that

    ImageView sourceView = (ImageView) source;
    Drawable d = sourceView.getDrawable();

    this.setImageDrawable(d);

    if I use shared preferences , which method am I using? this class doesn’t have any setDrawable, or getDrawable
    please advise me

    • blahti says:

      If you want to save the state of a grid so you can come back in later, you have to come up with a data structure that you can write and then read back from SharedPreferences. If you are using nothing but images in the resource drawable folders of your app, you could save the resource ids of those drawables. If images come from elsewhere, you’d have to save the path of the image or a uri of some sort.
      I’d also make sure that the code to save is outside the code handling the drag-drop. Study the activity lifecycle methods: onPause, onStop, onResume, etc.

  65. anusha says:

    Hi Bill,
    how to create another gridview and how to add it as drop target so that i can drag and drop items between two grid views….

  66. anusha says:

    Hi Bill,
    I acheived drag and drop between two grid views finally….

    I cant imagine how it could be without the help of your code…

    Thankyou sooooooooo much for such a great help…

    • blahti says:

      Sorry for my slow response. I assume you found where information is transferred from one cell to another in the onDrop method of ImageCell. Glad to hear that it worked out for you.

      • anusha says:

        yes Bill, achieved it through slight changes in your code..it was dragging and dropping of normal image view…that was successfull..

        But now what i wanted is drag n drop of image cells of grid view which come from back end dynamically…

        my image cells have an hash map adapter…that is image view along with text below…

        when i try to implement your logic in my code…it is crashing and showing error at startDrag() method….

        Can you please help me out!! Thanks in advance……

      • blahti says:

        The starDrag method of DragController does a little bit of work and then calls onDragStart of the drag listener, which is DragLayer in the demo app. There is code there to set up the drop targets that are in effect for the drag. I have often had problems there in my demo apps. Is that where your code is blowing up?

  67. anusha says:

    The code is throwing null pointer exception….giving you the error list just have a look!!!!

    08-22 17:32:13.668: E/AndroidRuntime(441): FATAL EXCEPTION: main
    08-22 17:32:13.668: E/AndroidRuntime(441): java.lang.NullPointerException
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.cmc.dmc.MyAdapter.startDrag(MyAdapter.java:199)
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.cmc.dmc.MyAdapter.access$1(MyAdapter.java:194)
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.cmc.dmc.MyAdapter$2.onTouch(MyAdapter.java:104)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.View.dispatchTouchEvent(View.java:3762)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:863)
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1671)
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.app.Activity.dispatchTouchEvent(Activity.java:2086)
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1655)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.view.ViewRoot.handleMessage(ViewRoot.java:1785)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.os.Handler.dispatchMessage(Handler.java:99)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.os.Looper.loop(Looper.java:123)
    08-22 17:32:13.668: E/AndroidRuntime(441): at android.app.ActivityThread.main(ActivityThread.java:4627)
    08-22 17:32:13.668: E/AndroidRuntime(441): at java.lang.reflect.Method.invokeNative(Native Method)
    08-22 17:32:13.668: E/AndroidRuntime(441): at java.lang.reflect.Method.invoke(Method.java:521)
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
    08-22 17:32:13.668: E/AndroidRuntime(441): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
    08-22 17:32:13.668: E/AndroidRuntime(441): at dalvik.system.NativeStart.main(Native Method)

    • blahti says:

      One thing that looks unusual to me is “MyAdapter”. Is that class really an adapter class for the GridView? I think that would be a very unusual place to have a call to startDrag. Normally, I’d expect to see that originating from a ViewGroup of some kind.
      To find what’s causing the null pointer exception, you have to look at the line that is blowing up. Somewhere on that line will be a call to a method of the form “object.method (…)”. One of those object is null. Find that object and then figure out why it is null. Keep my first comment in mind. Perhaps startDrag is trying to locate another view, the drag controller, or something and it fails.

      • anusha says:

        “My adapter” is adapter class for grid view….so should i put start drag method in my main activity?

      • blahti says:

        I like to think in terms of responsibilities. Each class should be responsible for providing some set of behaviors in an application. I think adapters have the responsibility for filling up lists or filling up grids. Managing drag operations does not fit, in my opinion. Read part 2 of my drag-drop notes if you have not done so. It has an overview description of the classes in this drag-drop framework. I tend to think that the Activity is a better place for these kinds of things.

  68. Magakahn says:

    Great tutorial, but I do not manage to make the DragLayer programmatically in my java class. When I do I get the drag to start, but it does not move anyway. And after I have lifted my finger it is still in “dragmode”. The code I am using is:

    mDragLayer = new DragLayer(this, null);
    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(FILL_PARENT, FILL_PARENT);
    mDragLayer.setLayoutParams(params);

    I have also removed the DragLayer in the xml. Can you please help me?

    • blahti says:

      Does your DragLayer still define onInterceptTouchEvent and onTouchEvent? And it needs to still do work like in onDragStart, where it adds the set of drop targets.
      Something else to check is whether you are adding the layer correctly to its parent. Substitute another class (ImageView, perhaps) for one test run and verify that the image shows up where you expect. If it does, you know that is working. I suggest that because a mistake I make fairly often is adding a view dynamically with the wrong class of layout params. The other thing this might uncover is that it is being added but not where you expect it.
      Those are a couple of ideas. Let me know what happens.

      • magakahn says:

        Made it work by using a xml file with only a DragLayer. It all works now, but since I have multiple ImageCells in a ScrollView, an Empty ImageCell “Bounces back” into the ScrollView even though the drop was successfull. This leaves a nasty empty hole in my ScrollView. I have tried modifying the DragController by changing where the mOriginator visibility is changed. This eighter does the same thing, or removes the ImageCell on a nonsuccessfull drop.

        How do I make it remove the ImageCell on a valid drop, and “refund” the ImageCell if the drop is not successfull?

      • blahti says:

        I wonder if what you are seeing is the problem with recycling views in the getView method of the adapter. See the comments above for April and May. When I did this demo app, I concentrated on getting drag-drop working on a grid. I never made the grid large enough that it would have to scroll. WHen a few people tried it, they found that images would appear in empty spots in the grid as they scrolled up and down. Is that what you are experiencing?

        Regarding question about removing an ImageCell on a valid drop, are you asking about removing the ImageCell or are you asking how do you clear its image?

  69. anusha says:

    “My adapter” is adapter class for grid view….so should i put start drag method in my main activity?

    I have 3 grid views in my xml, but i want drag and drop between 2 and 3 grid views only..so where should i include drag layer in my xml file.??

    Help me out, Thanks in advance!!!!

    • blahti says:

      The DragLayer should be your outermost ViewGroup. All dragging and dropping occurs within it. It turns over the responsibility for managing drag-drop to a DragController. All your views are inside it.

      • anusha says:

        ok thank you bill…i will learn ur article and will start using it in my code..

        will come to u if i get any doubt….thank you…have a nice day

  70. Harry says:

    Hi Blahti, I’m working on a app based on your great tutorial, however I’m really stuck on the last part. I have a number of images available at the bottom of the grid and I only want to be able to drag and drop one of these onto the gridview. All the other images should snap back to position (beneath the gridview) when the user tries to drop onto gridview . I’m at the point where I’m literally desperate for your help and advice. What kind of code could I stick into acceptdrop method? What’s the best way to go about this?
    Thank you in advance.

    • blahti says:

      I have two things for you to consider. There is a method in the DragSource interface named allowDrag. If you want all of the images at the bottom to be DragSource objects, arrange with an instance variable or something so that only the one you want to move responds true to that method. The other thing is to make only the moveable object be a DragSource. In terms of code to insert, you have to have something in the state of the objects being dragged or in the activity itself that indicates whether the drag is allowed. Often, I prefer to use an instance variable in the draggable object. That makes it readily available to methods like allowDrag and acceptDrop.

  71. Harry says:

    Thank you for your prompt response, I’ll go ahead and try your suggestions, hopefully I’ll be able to get to the bottom of this. Many thanks

  72. anusha says:

    why my application is crashing when i touch on image??

    error list is showing null pointer exception and error at onInterceptTouchEvent…

    • blahti says:

      For any null pointer exception, you have to go through the walkback information and find the line in your own code that triggered the exception. For an earlier question you asked, there was a line like “08-27 13:14:24.193: E/AndroidRuntime(339): at com.cmc.dmc.DMC.setupViews(DMC.java:714)”. I am guessing that com.cmc.dmc is your package. The number is the line number (714) in the DMC class with the null pointer. Go to that line and see which of the objects does not have a value. Often, it’s good to use the debugger with a breakpoint at that line. Once you find the null object, ask yourself why that object does not have a value yet.
      The same approach applies to onInterceptTouchEvent. What might confuse you is all the other levels in the walkback. Still, all you have to do is look for lines that for your classes and methods.

  73. harry says:

    Hi Blahti, Could you please advise me on how I could include Imageviews that are not part of the Gridview. I would still like to be able to drag the Imageviews but do not want them to snap onto the gridcells when you drag over as the Imagecell objects currently do. I have 3 images at the bottom of the screen, the first Imageview (Imagecell dragsource), the other two as normal Imageviews that just snap back to default position. I’ve tried creating a new Imagecell class naming it different i.e

    if (imageHolder2 != null) {
    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT,
    Gravity.CENTER);

    ImageCellFarmfake newView2 = new ImageCellFarmfake(this);

    newView2.setImageResource(R.drawable.bull);
    //newView2.setImageResource (resourceId );

    newView2.setId(R.id.image_source_frame_farm2);
    imageHolder2.addView(newView2, lp);
    newView2.mEmpty = false;
    newView2.mCellNumber = -2;

    but I got errors and had to declare these as dragsource objects for me to be able to drag them.

    I also tried:

    if (imageHolder3 != null) {
    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT,
    Gravity.CENTER);

    ImageView newView3 = (ImageView) findViewById(R.drawable.goat);
    imageHolder3.addView(newView3, lp);
    newView3.setOnClickListener(this);
    newView3.setOnLongClickListener(this);
    newView3.setOnTouchListener(this);

    I got errors again with this approach and now I’m stuck. Do i need to create dditional drag layer & dragcontroller classes?

    Thanks in advance

    • blahti says:

      I’m not sure what you mean by “ImageViews that are not part of the GridView”. If you mean you want to add images from that are not stored in the resource folders of the app, it should not be too hard to do. Study the code in method addNewImageToScreen in DragActivity. That shows getting a new image from resources. If you rework that some, you should be able to add an image from outside the application. For an object to be draggable, it must implement DragSource. So make sure the image you bring in is an ImageCell (or some other subclass of ImageView that implements DragSource).

  74. HongWoo Jin says:

    I did have the trash icon be invisible until the drag stars, and if I longclick on image then icon be visible , that is so easy, but I wonder if longclick event stops then I want to have icon be invisible like android launcher, which method can I add it to those functions

  75. Ofer Schonberger says:

    Nice! Thanks!

  76. Supy says:

    Give me the code of how to drop the images across the screen not only in gridview without using imagecell in outside the gridview.And where i write that code .

    • blahti says:

      For convenience I had the trash can object use the ImageCell view I already had. What you have to do is define your own view or subclass an existing one. In your subclass, you have it implement the DropTarget interface. Then you arrange to have your new view be one of the targets that the DragLayer adds to it is list of targets in onDragStart.

  77. Harry says:

    Hi Bill,

    How could I start a drawable animation on the gridview imagecell once the image is dropped? So instead of a copy of the source appearing on the imagecell, I want a completey different animation to start..

    Thanks in advance.

    • blahti says:

      I have not done a lot with animations yet. I have studied the animation examples in the API Demos of Android and then done a few simple tests based on what I learned. For those, I found that I could attach a new view within which the animations are drawn. So, for this GridView example, you could replace the change the ImageCellAdapter so it inflates a layout that includes an ImageCell and another view that you use for your animations. A FrameLayout works for that. Both the ImageCell and the animation view end up with the same dimensions.

  78. Thank you for this great drag and drop example. I am using this in a little board game i am developing. I few questions remain, hopefully you can give me some advice.

    First: for each ImageView i am using a background image, so in the ImageCellAdapter i am setting a drawable i.s.o a color, i.e.:
    v.setBackgroundResource (R.drawable.hello);
    Whilst dragging i would like the background to stay in place, however it goes away. How to solve this?

    Secondly when drag/drop has finished i would like to perform an additional action (basically the game’s AI turn to make a move). To my understanding this could be done at ACTION_UP inside the onTouch event in the DragActivity. However this action doesn’t get called unless i touch again; somehow the onTouch doesn’t end first time round. How can i make this happen?
    Thanks in advance!

    • blahti says:

      The demo app does not do anything with the background. If you want to preserve it as information moves around within the grid, you will have to keep track of the background resources. You could do that easily by adding another field to the ImageCell class and then modifying it at the time a drop completes.

      This demo builds on the framework described in an earlier blog post (see Moving Views, Part 2: Drag and Drop). Take a look at that for descriptions of the methods in DragSource and DropTarget. I think you will find that you can perform your AI action by adding code in one of those methods (onDrop in ImageCell or onDragEnd in DragLayer, perhaps) rather than changing the handling for touch events. Those methods are there to make it easy to add custom code.

      • I have moved the start of the AI’s turn into the DragLayer.onDragEnd event. Works like a dream! Thank you!

        However no luck using the background resource id as field in the ImageCell. I actually clear the background onDragStart event (as otherwise it acts as background in the dragable object) and reset the background somewhere during the startDrag event of the DragController. This just after the following line:
        Bitmap b = getViewBitmap(v);
        Where would be the best spot?
        Thanks in advance!

      • blahti says:

        Check these methods in ImageCell: onDrop, onDropCompleted. See where the background resource is set. If you are doing something different for background, consider doing it in the same place. If the background color is not a global value, you’d have to store it with the ImageCell being dragged so it will be available when the drop completes. Also note that the background resource is set in getView of ImageCellAdapter.

  79. hvdaedalus says:

    Hey blahti,
    Good example you have here.

    I am now trying to modify it to fit into my app.
    The only problem I need to figure out yet is how to do this with another View than an ImageView. Could you give me a hint on how to do this? I’m a little stuck in here.

    • hvdaedalus says:

      Looks like I solved it. To whoever is trying to do this with layouts, you need to create your own copy of “ImageCell” and “ImageCellAdapter”. It can be used for any View/Layout combination.

    • hvdaedalus says:

      Hey blahti,
      It looks like I got stuck again. Now the drag and drop part works, but the gridview is behaving somewhat weird. It looks like it is adding Views offside the screen into the gridview. Yes, these Cells are even filled. Do you have any idea why the gridview could behave that way?

      • hvdaedalus says:

        And I solved it again. It was an issue with views in the Gridview getting recycled. An info for where to look: The Adapter. It handles the reappearance/recycling for the views.

      • blahti says:

        I am happy to hear it is working.

      • blahti says:

        Yes, there are problems in this example. I did not want to deal with grids where scrolling is involved so I deliberately made the grid fit on the screen with no scrolling. If you build a larger grid, the views do not recycle correctly. Check some of the earlier comments and responses for more on this.

      • hvdaedalus says:

        Guess what: I’m stuck again. Due to a weird behaviour of a friends phone (running 2.3.3 on Galaxy Ace) onDragExit is called inside onDragOver, although it shouldn’t be.

  80. android_learner says:

    Blahti,

    Thanks for your previous reply they have helped me a lot.
    Instead of ImageView in addItem in arcMenu I am using relativelayout which consists of image and textview.

    Now the dragging is not happening it gives error:

    12-04 05:22:19.364: E/AndroidRuntime(1547): FATAL EXCEPTION: main
    12-04 05:22:19.364: E/AndroidRuntime(1547): java.lang.ClassCastException: android.widget.RelativeLayout
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.example.draggridview.DragLayer.onDragStart(DragLayer.java:156)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.example.draggridview.DragController.startDrag(DragController.java:220)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.example.draggridview.DragController.startDrag(DragController.java:179)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.example.draggridview.DragActivity.startDrag(DragActivity.java:408)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.example.draggridview.DragActivity.onLongClick(DragActivity.java:339)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at android.view.View.performLongClick(View.java:2556)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.example.draggridview.ImageCell.performLongClick(ImageCell.java:278)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at android.view.View$CheckForLongPress.run(View.java:9128)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at android.os.Handler.handleCallback(Handler.java:587)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at android.os.Handler.dispatchMessage(Handler.java:92)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at android.os.Looper.loop(Looper.java:130)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at android.app.ActivityThread.main(ActivityThread.java:3687)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at java.lang.reflect.Method.invokeNative(Native Method)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at java.lang.reflect.Method.invoke(Method.java:507)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
    12-04 05:22:19.364: E/AndroidRuntime(1547): at dalvik.system.NativeStart.main(Native Method)

    Please Help

    • blahti says:

      Is this still the original DragLayer code? You might have picked up existing code where the layout is assumed to be one class and you have switched to another.

      What’s going on on line 156? Would it happen to be a call to findViewById?

  81. android_learner says:

    Blahti,

    This code has helped me a lot in learning drag and drop.

    Thanks a lot.

  82. magakahn says:

    Thanks again for the example! Really helped me with Drag and Drop pre 3.0. I have two more questions.
    Is there a way to make an ImageCell that takes up two or more pieces using this code. I think more like a puzzle where one of the pieces take up two pieces over two or more drop target. Is this possible with this example?

    Secondly, I have been around every site without finding a proper way of saveing the state of the GridView. Do you have an example with this included?

    • blahti says:

      The thing that takes up two or more drop targets could be done, but I bet you will have to add quite a bit. This framework makes it easy for a single thing to be dropped onto a single target. I guess you would have to put a variable into the cell object so it would know that it is part of a pair and know which other cell it is paired with. Then, at the moment of drop (method onDrop), it could take two pieces of information from the DragSource and place them in two cells. Something like that.

      I have not written anything to save the state of a GridView, but I have written several apps where I store things to SharedPreferences. For those, I start with determining what makes up the state of the object I am saving and how to represent that and store that using SharedPreferences. Most of the time, I have chosen to write out that saved state after each operation rather than waiting for the user to click a Save button. Each time the activity starts, it checks to see if there is saved state and restores it. Of course, that’s the real challenge. Did I save enough of the state to get the activities views back to a functioning activity? So for the GridView, ask yourself what you have in the grid and think through how you’d reset an activity. If it’s images, you might save a list of resource ids if all of the images are in the app. If the images are external to the app, you’d have to save the path of where they are on the sd card.

  83. Joanne says:

    Hi Blahti,

    I have a gridview. I want to drag an item that contains textview and drop to another item. and on drop their text will swap.

    The dragged text will be transferred to the item where the text is dropped and the text where the item is dropped will be transferred to the item where the text is dragged.

    please help. i am confused on how to start a drag from an item in a gridview on long click.

    • blahti says:

      This demo uses an ImageCell, which is subclass of ImageView that supports the drag-drop framework by implementing the methods of DragSource and DropTarget. You should define a new subclass of TextView that does the same. Study the current demo to see where information is transferred from the moving cell to the target cell in the grid. You would do analogous things in your TextView subclass. So in onDrop you would set the text of one view with the text of the other. I advise starting with a copy of this demo app for your new work and a copy of the original. Study how the old one works in some small part (e.g. laying out the grid of views) and then doing that in your new code. Then move to the next thing. Do not try to do too much in one step because there is a lot to learn and much that could go wrong if you make a little error.

  84. Pingback: PagedDragDropGrid - AndroidViews

  85. Sören says:

    Hello,

    at first happy new year!

    Very great work, but I have got a question. Where does the integer array “mCoordinatesTemp” gets his values? I don’t found the part in the code.

    • blahti says:

      That is a bit hard to see. There is a line in the drop method that reads “final int[] coordinates = mCoordinatesTemp;”. Right after that findDropTarget is called with coordinates as the third argument: “findDropTarget((int) x, (int) y, coordinates)”. Inside of findDropTarget, values are assigned to coordinates[0] and coordinates [1]. This code originated with the Android Launcher code of API 2.2, and that might be one section where a word of explanation is needed.

      • Sören says:

        Okay thanks for your answer. But in my code this mCoordinatesTemp always return “0”. I only changed, that you can directly drag the objects in the gridview. Do you have an idea why this int Array doesn’t work?

      • blahti says:

        The only place it changes is that one spot. Perhaps you could some Log.d calls in that method to see if it changes at all.

      • Sören says:

        Okay, I found it out. Thank you!

  86. nayoso says:

    Hi bill

    Thank you for sharing this amazing example of drag and drop
    I want to use your example in my application but when I’m trying to add it to a dialog fragment the imageview I want to be dragged will be thrown into the main view (not the dialog fragment) and the error will shown

    01-20 20:49:07.116: E/AndroidRuntime(31525): FATAL EXCEPTION: main
    01-20 20:49:07.116: E/AndroidRuntime(31525): java.lang.IllegalArgumentException: Given view not a child of niko.buzoo.mybadge.dragview.DragLayer@425371f0
    01-20 20:49:07.116: E/AndroidRuntime(31525): at android.view.ViewGroup.updateViewLayout(ViewGroup.java:3252)
    01-20 20:49:07.116: E/AndroidRuntime(31525): at niko.buzoo.mybadge.dragview.DragLayer.onDrop(DragLayer.java:144)
    01-20 20:49:07.116: E/AndroidRuntime(31525): at niko.buzoo.mybadge.dragview.DragController.drop(DragController.java:450)
    01-20 20:49:07.116: E/AndroidRuntime(31525): at niko.buzoo.mybadge.dragview.DragController.onTouchEvent(DragController.java:428)

    I’m using callback interface in the main fragment dialog to be passed into the first child and then passed to the second child (to be dragged view) then I set ontouch to call the callback from the main fragment and pass the draglayer and dragcontroller that I declared in the first child and passed it to the second child. Am I doing it wrong? or can I just use it in the ontouch normally (without callback interface) ?

    Thank You for your time

    Sincerely, Niko

    • blahti says:

      Looking at that trace, it seems like you made your DragLayer be an implementor of the DropTarget interface. That can work. Then, it looks like you are trying to take the object being dragged and assign it to the DragLayer. I found that does not work. You have to remove the view from its old parent and add it to the DragLayer, I think. I have usually tried to avoid that situation because it’s not been clear to me how to that in Android. So I avoid it by looking at the information in the dragged view (DragSource), and rather than changing the view’s parent, I create a new view (and whatever objects go with the view), attaching it in the new place, and then setting values in the new view using the data from the dragged view. To the user, it looks like a view just changed its position, but behind the scenes, it was one being created and another being removed.
      I hope I have understood your situation and that this comment helps.

  87. Swati says:

    Hi blahti,

    I am making a project in which i am taking the help of this sample project of yours. Can you please help me with a problem. In the DragActivity, I have a grid view on the right side of the screen and some ImageCell objects on the left side. I am trying to drag and drop these images to the Grid View (has a single column). I am able to achieve that easily with your code, but when i scroll the grid view, the images that i place on the top of grid view get repeated at the bottom of gridview. I tried using a viewHolder but i am unable to implement it in the ImageCellAdapter class’s getView() method. Please help me on how i should do this. Any sample code you can provide me would really be appreciated. Thanks

    • blahti says:

      The scrolling problem has come up quite a bit. I simplified this example from the original code on which it was based so I left out scrolling. There are a few points to consider to fix scrolling. Read through the comments here.
      I don’t have a quick fix to suggest, but the comments may help.

  88. Joanne says:

    hi! is it possible to drag a view from a gridview where the view contains an image and textview?

    • blahti says:

      Technically, yes. I would advise against it because changing the parent of a view is pretty confusing. I suggest copying information from the dragged cell to the target cell. So, in the onDrop method, do not try to change the view’s owner. Instead, pick up the text from the dragged object and use it to assign the text in the target. Similarly, for the image, set the image view part of the target to match the drawable of the image in the source object.

  89. doni says:

    Hi!
    I’m new in Android development and I’m trying to reorder elements in a GridView. I found your solution to be excellent and thanks a lot.

    Do you by any chance have a solution of “reordering element within GridView items” by using Android Drag and Drop framework ?
    If not can u give me any guidance on this problem?
    Thank you again for your tutorial! is great :)

    • blahti says:

      Who is doing the reordering? Do you mean the user by dragging and dropping? The current example does allow moving items even after they are added to the grid.
      Or was the reordering done in code?

  90. Pingback: Drag and drop items in a GridView : Android Community - For Application Development

  91. Pingback: Drag-Drop for Android GridView (V4) | More Is Not Always Better

  92. sri says:

    hiii….i am new to android…i am trying to create a drag and drop functionality ….i am able to do drag and drop anywhere but i am not able to get the idea how to write the code to drag and drop in particular positions…(in certain coordinates)…can u help me with this…
    Thank You.

    • blahti says:

      I would suggest first building layouts by hand using the layout design tool. Gain an understanding of how they work and how you would make something that looks like the end result you are trying to achieve. Then think about how you would place empty views on a layout that could be a target for a drag-drop operation.
      You could go back and look at some of the earlier tutorials I posted about moving views and drag-drop to see how AbsoluteLayout work, which gives you very precise control over where things go, but I would not encourage this. AbsoluteLayout is no longer supported. Find a way with the current set of views that are supported. GridView, TableLayout, and RelativeLayouts might work for you.

  93. pb says:

    Thanks for the awesome tutorial . I am working on something where i drag a image and try to drop to another fragment . ( when i drag the other fragment slides on the right ) and i drop it . The drag works but drop does not work . Any ideas on how to solve this issue ? ( Note : the container of both the fragments is the draglayer ) .

    • blahti says:

      One possible explanation: the DragLayer does not know about the view that you expect to be a DropTarget. You have to make sure that the View is known to the DragLayer when the drag operation starts. See method onDragStart in DragLayer.
      That’s how it works in this version of the drag-drop demo. You might also look at the newer version for V4 Android. That might make it easier for your Fragment’s view to be seen.

      • pb says:

        Appreciate your reply . Unfortunately i need to support API 2.2 and above and the drag and drop API is available only for honey comb . SO my only choice is looking into launcher code :(

      • blahti says:

        I still think the DragLayer code could work. At the time the drag operation starts, the Fragment is visible, right? That means you could get to the view or views that the fragment sets up. If you can get them and if they implement DropTarget, DragLayer onDragStart could add them to the list of potential drop targets — and that might get you to where the drag could drop where you want it.

    • pb says:

      Yes , you are right . I got it working tiday . I had messed up the on Drop method . I see that image (which is being dragged) is full width and i am resizing the drag view to the same dimension as my drop target . But the dragview does not ovelay on touch of finger but it appears away from the finger .

      If i click on beginning of view , the touch in dragview is ok but if i click and drag from the end of image the dragview is right to finger touch . I think there is some issue with offset .

      • pb says:

        To precisely understand the behavior change the dragview width and height

        from startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(),
        source, dragInfo, dragAction);
        to
        startDrag(b, screenX, screenY, 0, 0, b.getWidth()/2, b.getHeight()/2,
        source, dragInfo, dragAction);

        Touch the image view at the end and drag , then the touch is not on the image but appears in the right of the image .

      • blahti says:

        Check the DragView class and its onDraw method. There might be some assumptions about size and scaling that are in conflict with what you are trying to do.

      • pb says:

        Yes , i kind off gave me an idea . Thanks !!! Is there particular reason onItemClickListener does not work on gridView with draglayer? I am trying implement onItemClickListener and it does not work . are the touch events intercepting with onItemClickListener .

      • blahti says:

        This drag-drop framework requires that a ViewGroup class (DragLayer, in this case) intercept all touch events so it can decide if they important for drag-drop. Look in DragLayer and you will see that it defines onInterceptTouchEvent and sends all events to the DragController object. If want the GridView’s regular handling of click, you will have to put that in DragController as a special case.

        Read the Android doc and this post on StackOverflow.

  94. Preethi says:

    Awesome tutorial and thanks for that ! ANy insight on how to drag a image from one fragment , while dragging the other fragment slides on right and i need to drop there . Note : The drag layer is the container which holds both the fragments . I am able to drag but unable to drop the image .

  95. Salman Ashraf says:

    hii,
    when I click on gridview cell it doesn’t return cell postion. can you tell me how to get postion of cell from gridview.

    • Uri says:

      Hi

      Thanks for the great tutorial. I do have an issue though… I’m using a different layout for my purposes, so no grid, but the basics are the same.
      I’ve used the DragLayer as the parent for the entire activity, yet i’m still unable to perform the drag outside of the DragSource’s parent bounds (its parent is still a child of the DragLayer). it drags fine within it, but the moment i drag outside of it’s bounds a motionEvent Action_Cancel is being called, which cancels the drag.

      I’m not sure why this is happening, can you help out?

      • Uri says:

        Never mind… had a very convoluted XML hierarchy … missed that i had a scrollview way up there above everyone. It intercepted the touch events and messed this up.

    • blahti says:

      If you want to know the cell number, you can get it from the ImageCell. Each cell stores it number in the mCellNumber variable.

  96. sri says:

    hieee blahti,i have seen ur example but i need a specific image to be dropped in specific box only not in every box …can u suggest me any example with this issue??

    • blahti says:

      You need something like an ImageCell that knows what image it is willing to accept. You could then code its acceptDrop method to return true under just the right conditions. Determining those conditions from the DragSource or DragInfo objects that are arguments to acceptDrop is what I would suggest. In other words, the ImageCell (DropTarget) knows what it is willing to accept and knows how to ask the DragSource what it is dragging.

  97. henber says:

    Hi and thanks for sharing your great work!

    I’ve made some changes to your code and instead of adding images I add random created numbers to a grid.
    When I drag and drop a number to grid I would like to be able to change it’s grid position (ok today) but when I create a new random number I don´t want the previous added numbers to be touchable anymore.

    I guess I have to keep track of latest droptarget in the activity somehow.
    Any ideas?

    Regards,
    HenBer

    • blahti says:

      You are looking for a place to mark numbers as “not touchable”. I suggest that you capture that information at the point of drop. If you still have an ImageCell object, you could add a instance variable there and set it in the onDrop code of ImageCell. The allowDrag method would check the same variable and return false when not touchable. Something like that might work for you.

  98. srinivas says:

    Hi I saw your comment, but i need drag and drop grid items from one gridview to another gridview for api 8 or 10.

    we can drag items in one gridview to another gridview. send me code plz

    • blahti says:

      Take a look at onDragStart of DragLayer. It sets up all the drop targets it finds in the one GridView. You could try setting up your second GridView and doing the same. As long as both GridView objects are part of the DragLayer, drag-drop between the two grids should work.

  99. Keturah says:

    Hi Blahti.

    Can you give a tutorial on how to implement drag and drop functionality on differing images making them target specific?

    By this I mean, If you have say, 4 draggable images; image_drag_1, image_drag_2, image_drag_3, and image_drag_4 and 4 drop_target images; image_drop_1, image_drop_2, image_drop_3, and image_drop_4.

    image_drag_1 should be matched to image_drop_1 and any attempt to drop image_drag_1 on any other drop image or location in the screen layout, makes image_drag_1 snap back to its original position.

    I tired modifying your code to suit my purpose but it yielded no result. Thanks in advance! :-)

    • blahti says:

      In my example, I moved an image from one place to another. That happens in onDrop of ImageCell. To impose more constraints on what can move, I think you could add more logic in onDrop.

  100. Dhaval says:

    Thanks Sir Thank u very Much for this Demo..

  101. Dhrupal says:

    Hello Sir,

    Your all demos are THE fantastic.

    I want one help in this demo, I want this situation,
    If i Drag any image in grid view area (any item column of 9) then it should be added to 1st position, then if i drop 2nd then it should after first one and so on.

    Please help!!!!

    • blahti says:

      In the current demo, the only place information is maintained is in the ImageCell. Each cell knows its position on the grid. It seems to me that if you are going to do things where rows and columns matter, you are going to need a data structure to indicate which image cells are in which row and column. When you have that, you can change what goes on in the onDrop method of ImageCell. Instead of simply setting the image, you’d do something to look up the current column, based on the cell number that is already there. Once you have the column, you could locate the first row in that column that is empty. Something like that perhaps.

  102. Kofi says:

    Hello Bill,

    I was wondering if there is any sample code that where a person can drag “multiple” images into “one” cell in the gridview?

    The idea that I have is that the user of the app can drop and drop multiple items (or in this case, images) into one of many cells in the gridview. So it will be like organizing images into different containers/buckets where each container/bucket represents a cell in the gridview.

    So I was wondering if this is possible? Right now, your code is able handle “one” image for “one” cell. I want to see if i can do “multiple” images for one cell. If you have any sample source code or any advice/suggestions/other helpful code, it would be greatly appreciated.

    • Kofi says:

      Hello Bill,

      I actually got an idea that seems to work. Instead of linking a gridview to gridview in the layout xml (findViewById), I think i’m going to dynamically create the gridview programmatically (MyGridView = new GridView(this)). In other words, each container/bucket would be represented by a gridview.

      So I thought about having a for loop that would dynamically create the gridviews and then add the gridview to a vertical linear layout. So it would be something like this:

      Let’s say I want to organize a bunch of images into 2 containers/buckets.

      LinearLayout TheGridViews = (LinearLayout)findViewById(R.id.GridViewLinear);

      for(int i = 0; i < 2; i++)
      {
      ThisGridView = new GridView(this);
      ThisGridView.setNumColumns(2);
      ThisGridView.setTag(i);
      ThisGridView.setAdapter(new ImageCellAdapter(this, mDragController));
      TheGridViews.addView(ThisGridView);
      }

      I tried the code and it seems to work. I was wondering if you can help me out in terms figuring out which image was placed in which container/bucket? As you can see, the only thing I have that can differentiate between gridviews is the tag that I set.

      Right now, I am able to move images between gridviews (aka containers/buckets). But i'm not sure how to track which images were placed in which gridviews or the list of images that are contained in each gridview. If you have any source code, advice, suggestions, etc, it would be greatly appreciated.

      • blahti says:

        That sounds like good progress. I see that you are storing an integer (i) as the tag view of each GridView element. Are you aware that you can store any object as the tag? For instance, it could be v.setTag (new GridViewCellData ()), where your custom GridViewCellData class could be whatever extra information you need to have drag-drop work the way you want. The same thing might be useful for the ImageCell objects too.

    • blahti says:

      That sounds like it will be tricky to implement. You could try a custom view that displays multiple images. You could make it a DropTarget and have its onDrop code do whatever you need to position an image when it is dropped.

  103. Rohit Mishra says:

    Hello Blahti,

    You did a wonderful job, The example given by you is really very useful for me. But The problem is I wanted to drag copy of text from a TextView, and drop it not to the dropspot but to the another empty TextView so that its text will be replaced by the TextView dropped to it. Please provide me the simplest solution to do this because I am very new in android application development.

    Thank you

    • blahti says:

      Study the methods related to dropping in ImageCell and see how ImageCell subclasses ImageView. You could try something similar for a TextView (or whatever view you have that includes the text you moving). Have your class implement the DropTarget interface. Then in the onDrop method, arrange to transfer the text from the drag source to the target.

      If you have not already read it, longer descriptions of the drag-drop framework are in my old note on drag-drop. If you are using the newer version of Android, also look at the new version of Drag-drop for a GridView.

      • Rohit Mishra says:

        Thank you so much Bill…

        As you replied I got it and did it successfully, But now I am facing a new problem,
        Actually I defined TextView inside custom view named as com.blahti.example.drag2.DropSpot defined by you in example.

        Now while I drop a TextView defined in our another main view named as com.blahti.example.drag2.DragLayer which is also parent of all that views. so i need to update text of particular textView of DropSpot where I dropped it. Sorry I am posting this question in part 3 whereas this question belongs to the part2.

        Please tell me I am doing it in right way or not if not please tell me the right way too. my onDrop method of DropSpot class is something like following. In which I am statically defining the textView id and it is updating correctly what is my need. I want to get it update dynamically.

        //same code as u defined

        View v = (View) dragInfo;
        int txtArr[] = {R.id.drop_spot6,R.id.drop_spot9,R.id.drop_spot5,R.id.drop_spot8,R.id.drop_spot7,R.id.drop_spot10,R.id.drop_spot11,R.id.drop_spot12,R.id.drop_spot13};
        View v1 = (View)source;
        TextView t23=(TextView)v1.findViewById(R.id.Text19);
        TextView t2=(TextView)v;
        t23.setText(t2.getText().toString());

        //same code as u defined

        Once again Thanks a lot

      • blahti says:

        I am not too sure about the way you set up your drop spots. In my demo code, I have done that in a method that runs when the drag starts. That method is onDragStart of DragLayer. It is the object that knows that dragging goes on within it so it seems like the logical place to set up the list of drop targets.

        The other thing I think could be improved is the access to the TextView objects. You have hard-coded a lot of id references. I wonder about that. If you define MyTextView as a subclass of TextView (like where I defined ImageCell as a subclass of ImageView), you have a place for an onDrop method that would get the view being worked with as an argument rather than having to find it by name.

        Hope that helps. I might have misunderstood how you have structured your app.

  104. srinivas says:

    post the code for drag and drop gridview items from one gridview to another gridview

    • blahti says:

      I don’t have that example at this time. I urge to give it a try.
      I suggest trying the following steps with the existing demo: (1) figure out how to get two grid views on the screen; (2) make the two grid views have the same shape and built from the same classes (e.g. ImageCell) as in the original; (3) find the spot in the code where the list of drop targets is set up as a drag operation starts; (4) modify that to go through both grid views, adding all the cells in both to the list of drop targets. Do not do too much at once. Go step by step. Chances are good you will end up where dropping an image works, whether the drop target is in one grid or the other.

  105. Pingback: How to drag and drop between different GridViews? | BlogoSfera

  106. Mahatva Gurung says:

    Hello Sir!
    I’m new in Android and i found this article very helpful, explained in easy technical vocabulary. A big THANKS for this.
    I am currently working in an app that uses similar functionality, although instead of adding random images by pressing add button, it takes several variable number of images in ImageCell by hitting a server and fills the grid. And, in Drag and Drop functionality, when a view drag is started for a view(v1)and dropped over another view (v2), v1 replaces v2 and v2 goes in place of v1. Can you please guide me through this?

    • blahti says:

      There is a good article on the Android developers website that you should read:
      http://developer.android.com/training/displaying-bitmaps/index.html
      I learned a lot about moving images in and out of memory and reading from files. That should get you started.

      Then study the code in my app where an ImageCell updates itself with a new image. If you change your ImageCell so it keeps track of where the image came from — and I am assuming at that point you have copied the image from your server to somewhere local — you should be able to use the bitmap or the drawable to update the ImageCell, which is itself a subclass of ImageView.

  107. Wytas says:

    Hello blahti,

    Your example is very helpful. I manged to adopt it for my project. It works very well on my devices and all emulators. But from some users (~1% of all the users) I receive NullPointerException crash reports on this line: return mDragController onInterceptTouchEvent(ev)

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    return mDragController.onInterceptTouchEvent(ev);
    }

    I have no clue what could be wrong. As I wrote the code works perfectly for me and all my test devices. It is hard to fix something that really works for you :) Maybe you could have a feeling how the NPE could occur in this case ?

    Best regards,
    Wytas

    • blahti says:

      The null pointer exception must mean that the drag controller object in mDragController is null. I would guess that the view is set up and active before the rest of the code has been able to set up the object. I would suggest changing the line to:
      if (mDragController == null) return true;
      else return mDragController.onInterceptTouchEvent (ev);
      Once that is in place, I’d guess that there will be no more exception. Still, there could be something else wrong so you could be looking for other odd behavior in the apps at that point.

  108. manh hoang says:

    Dear blahti,
    thank you for sharing a great lib.
    I have implemented this lib for my grid app (support from api 8)
    I use DragViewV3-120308 version and I have only a problem.
    I would like change background color of source view in onDragEnter but I only can change target background color.
    My code here:
    ImageCell sourceView = (ImageCell) source;
    if(sourceView.mCellNumber != this.mCellNumber)
    {
    swap(sourceView, this);
    }

    public void swap(ImageCell soucrce, ImageCell target)
    {
    int tmpId = soucrce.resId;
    if(target != null)
    {
    soucrce.resId = target.resId;
    soucrce.setBackgroundResource(target.resId);
    }
    target.resId = tmpId;
    target.setBackgroundResource(tmpId);
    }

    Thanks in advance!

    • blahti says:

      Sometime, and I do not completely understand the conditions, I have to to call invalidate, as in “source.invalidate ()”. Try that. The other thing that goes wrong for me is passing an invalid resource id, so check to make sure that target.resid is what you think it should be. An easy way to do that, since you say that the code above changes the target correctly is to the source temporarily to something like R.color.TestColor. If you see a change, you know its the value in target.resid.

      • manh hoang says:

        Dear Blahti,
        I was try with source.invalidate() but it’s not working. The target is change but source is still not change
        My demo apk here: https://www.dropbox.com/s/lgnxxvh8rcb4seb/DragViewDemo.apk

        I was successful with swap method in onDrop
        My code in onDrop
        ImageCell sourceView = (ImageCell) source;
        swap(sourceView, this);

        Thanks!

      • blahti says:

        Perhaps I do not understand. WHen you say “source”, what do you mean? The image cell where the drag started? Or the image that shows up on the screen while the dragging is going on?
        If you mean the image cell, I’d start by trying to get the background resource to change at any time — not in the drag operation. Is there any chance that the background is not visible because the image is too large?

  109. manh hoang says:

    Dear Blahti,
    That’s right, The image cell where the drag started? (Yes)
    I would like change this image cell by follow code in onDragEnter but it’s only work for drop target (image cell target)
    ImageCell sourceView = (ImageCell) source;
    if(sourceView.mCellNumber != this.mCellNumber)
    {
    int tmpId = sourceView.resId;
    if(target != null)
    {
    sourceView.resId = target.resId;
    sourceView.setBackgroundResource(target.resId); (Not OK)
    sourceView.invalidate()
    }
    this.resId = tmpId;
    this.setBackgroundResource(tmpId); (OK)
    }

    could you try intall my apk above for detail
    https://www.dropbox.com/s/lgnxxvh8rcb4seb/DragViewDemo.apk

    Please help me,
    Thanks in advance!

    • blahti says:

      There are several places in the original ImageCell code where the background color of the source cell is set. They are always calls to setBackgroundResource and the argument is always a resource id (like R.color.empty_hover). In some apps, I have gotten confused about what the argument to setBackgroundResource is. It is an integer, but so are colors and resource ids. If I use a Color integer by mistake nothing changes. That’s what I suspect is going on in your case.
      I suggest added a Log.d statement and write out the actual value of target.resId at the point you call setBackgroundResource. I hope that you will find it is an integer rather than a resource id.

      • manh hoang says:

        Dear Blahti,
        I was implemented successfully
        In my demo, It’s not show changed because sourceview is gone
        This code instartDrag of class DragController which gone souceview
        if (dragAction == DRAG_ACTION_MOVE) {
        v.setVisibility(View.GONE);
        }

        Thank you very much for helping me
        Best regards!

  110. rigel says:

    Sir thank you for your project,i download it and i want to use the source code for compleating my android application. But now i have a problem, after i use your source code in my application i can’t call DragActivity too my Main Activity. Sudenly it’s stoped and back to main menu. Can you help me what is wrong with my code..?
    Here i include my logcat :

    12-19 23:26:22.853: E/InputEventReceiver(1089): Exception dispatching input event.
    12-19 23:26:22.853: E/MessageQueue-JNI(1089): Exception in MessageQueue callback: handleReceiveCallback
    12-19 23:26:22.982: E/MessageQueue-JNI(1089): java.lang.SecurityException: Requires VIBRATE permission
    12-19 23:26:22.982: E/MessageQueue-JNI(1089): at

    Thanks.

    • blahti says:

      Do you see the part in your error log that says “Requires VIBRATE permission”? That is saying you did not add the permission in your AndroidManifest.xml file in your application. GO back to the demo app and you will see that permission line.

  111. Tomek says:

    How to get position of column where i dropped my ImageView? I know it’s in mCellNumber, but i don’t know how to get it from my activity.

    • Bill Lahti says:

      DragActivity implements the DragDropPresenter interface. There is a method onDropCompleted. When that method is called the DropTarget argument is really the ImageCell. So cast the target to ImageCell and then access the cell number.
      I think that would get you what you want.

  112. Hi Sir,

    Could you explain me about swapping images using drag and drop solution given by you. Please Sir. Waiting for your reply.

    • Bill Lahti says:

      Notice that in onDrop of ImageCell there is call to set the image of the cell: “this.setImageDrawable (d)”. If you cast the drag source argument to an ImageCell, you have access to where the drag started: “sourceCell = (ImageCell) source”. So now all you need to know is what is the image of that sourceCell. ImageCell does not have an instance variable for the drawable so you’d have to add one. For both cells, you should call the invalidate method if the changes do not show up.

      If it does not work right away, you might also want to add a button you can click for an easier debugging scenario. When the button is clicked, try a simple swap of two cells (pick any two by hard-coding index values). Debugging will be easier because you can add breakpoints more easily outside of drag-drop. Once you get a swap to work there, apply what you learned to the onDrop method.

  113. Rishikesh says:

    How to rearrange the grid after fully filling all the grid with images.

  114. Hello thank you very much for giving this tutorial.. i am developing a game app in android so its need drag and drop. I am implementing your code but i have some problem
    My logcat:

    E/InputEventReceiver(1903): Exception dispatching input event.
    E/MessageQueue-JNI(1903): Exception in MessageQueue callback: handleReceiveCallback
    E/MessageQueue-JNI(1903): java.lang.ClassCastException: android.widget.ImageView cannot be cast to com.Adpter.DragSource

    so please help me asap..
    Thanks…

    • Bill Lahti says:

      The drag-drop framework works with classes that implement the different interfaces, such as DragSource. You cannot use an ImageView directly. What my examples show is that you have to use a subclass of ImageView that implements the DragSource interface. In the examples, look at the ImageCell class. From looking at the walkback in your log, it looks like you have used a plain ImageView.

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