Monday, August 22, 2011

Handling Drag and Drop with different views using OnDragListener

Hello everyone,

it's been a while since the last time I blogged. The desktop summit was really interesting. I got to know new people and also saw some familiar faces (a desktop summit blog post should be on its way).
Anyways, this post is about something totally KDE unrelated. Since I am also an android developer I would like to give you some tips that helped me through little coding problems.

Here I will talk about  dragging and dropping. It is available since Android 3.0 (API version 10) and actually pretty straight forward. However I came across a little problem when using the
View.OnDragListener
interface. To make the problem description short:
every view that did not start the drag had to implement its own
View.OnDragListener.
All right less text, more code. Here is what works:
public class StuffActivity extends Activity implements OnDragListener {
   
    Button mDragView;
    Button mDropView;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mDragView = (Button)findViewById(R.id.dragger);
        mDragView.setOnDragListener(this);
        mDragView.setOnLongClickListener(new View.OnLongClickListener() {
      
            public boolean onLongClick(View v) {
                ClipData data = ClipData.newPlainText("text", mDragView.getText());
                v.startDrag(data, new MyDragShadowBuilder(mDragView), null, 0);
                return false;
           }
        });
        
        mDropView = (Button) findViewById(R.id.dropper);
        mDropView.setOnDragListener(new View.OnDragListener() {
   
   
    public boolean onDrag(View v, DragEvent event) {
        int action = event.getAction();
        switch(action) {
         case DragEvent.ACTION_DRAG_STARTED: {
          return true;
        }
        case DragEvent.ACTION_DRAG_LOCATION: {
          return true;
        }
        case DragEvent.ACTION_DROP: {
            // Gets the item containing the dragged data
            ClipData.Item item = event.getClipData().getItemAt(0);
            // insanity check
            if (item == null)
                return false;
            // Gets the text data from the item.
            CharSequence dragData = item.getText();
            mDropView.setText(dragData);

            return true;
        }
        default: {
            return false;
         }
        }
       }
      });
    }

    @Override
    public boolean onDrag(View v, DragEvent event) {
        int action = event.getAction();
        switch(action) {
        case DragEvent.ACTION_DRAG_STARTED: {
            Log.d(StuffActivity.class.getName(), "Starting from " + v.getClass().getName());
        return true;
    }
    case DragEvent.ACTION_DRAG_LOCATION: {
        return true;
    }
    case DragEvent.ACTION_DRAG_ENTERED: {
        Log.d(StuffActivity.class.getName(), "Entering " + v.getClass().getName());
        return true;
    }
    case DragEvent.ACTION_DROP: {
        v.invalidate();
        return true;
    }
    default: {
        return false;
    }
  }
 }

This activity has to Buttons. One is able to drag to the other Button. The other Button takes over the text of the first one.
This view implements the OnDragListener. As mentioned above there are two Buttons: mDragView and mDropView. Dragging and dropping for the mDragView field is handled by the implemented method
public boolean onDrag(View v, DragEvent event) { ... }
What is not working when you set:
mDropView.setOnDragListener(this);
instead of creating a new listener. Setting the listener to this does not contribute to the drag and drop event.

And that's it. Hope it helps. Thanks for reading.
The sourcecode can be downloaded here.

cheers

Please contact me if this post contains errors.