mariaines
mariaines

Reputation: 507

OnDragListener not receiving DRAG_STARTED or DRAG_ENDED, but does get ACTION_DROP

I'm running into a weird problem with an OnDragListener. My target view gets the ACTION_DROP event fine and handles it; but it never receives the ACTION_DRAG_STARTED or ACTION_DRAG_ENDED events (in fact it never receives any events besides drop).

What could be causing this? It's an issue because I can't handle the case when the drop happens outside of the target.

I found this question but the answer was not clear to me. Any ideas greatly appreciated.

My draggable view has this OnTouchListener:

@Override
public boolean onTouch(View v, MotionEvent ev) {
  switch (ev.getAction() & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
      startPointX = ev.getX();
      startPointY = ev.getY();
      isOnClick = true;
      break;
    case MotionEvent.ACTION_CANCEL:
    case MotionEvent.ACTION_UP:
      if (isOnClick) {
        isOnClick = false;
        // handle single click
      }
      break;
    case MotionEvent.ACTION_MOVE:
      if (isOnClick && movePassesThreshold(ev)) {
        isOnClick = false;
        draggableView.startDrag(...);
      }
      break;
    default:
      break;
  }
  return true;
}

And the target view has this OnDragListener:

@Override
public boolean onDrag(View v, DragEvent event) {
  switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
      Log.v(TAG, "drag started");
      break;
    case DragEvent.ACTION_DRAG_ENTERED:
      break;
    case DragEvent.ACTION_DRAG_EXITED:
      break;
    case DragEvent.ACTION_DROP:
      Log.v(TAG, "drop");
      // handle drop
      break;
    case DragEvent.ACTION_DRAG_ENDED:
      Log.v(TAG, "drag ended");
      break;
    default:
      return false;
  }
  return true;
}

Upvotes: 7

Views: 11271

Answers (3)

Mike
Mike

Reputation: 5572

This is a known issue with the ViewGroup:

https://code.google.com/p/android/issues/detail?id=25073

Overriding the dispatchDragEvent function as suggested at that link worked for me:

@Override
public boolean dispatchDragEvent(DragEvent ev){
    boolean r = super.dispatchDragEvent(ev);
    if (r && (ev.getAction() == DragEvent.ACTION_DRAG_STARTED
            || ev.getAction() == DragEvent.ACTION_DRAG_ENDED)){
        // If we got a start or end and the return value is true, our
        // onDragEvent wasn't called by ViewGroup.dispatchDragEvent
        // So we do it here.
        onDragEvent(ev);
    }
    return r;
}

Upvotes: 6

mariaines
mariaines

Reputation: 507

My solution is this workaround: when my OnTouchListener gets ACTION_UP, set a 1-second delayed runnable to check if the view is still dragging. If the OnDragListener received the drop, the runnable does nothing; but if it didn't the runnable calls stop drag to clean up and reset the view to its previous position.

case MotionEvent.ACTION_UP:
  if (isOnClick) {
    isOnClick = false;
    // handle single click
  } else if (draggableView.isDragging()) {
    uiHandler.postDelayed(new Runnable() {
      @Override
      public void run() {
        if (draggableView.isDragging()) {
          // Drop never got received, so call stopDrag
          draggableView.stopDrag();
        }
      }
    }, 1000);
  }
  break;

Upvotes: 2

Android2390
Android2390

Reputation: 1150

This is how i am doing it. I dont know if it will work for you or not but still posting.

@Override
public boolean onTouch(View v, MotionEvent event) {
         final ClipData data = ClipData.newPlainText("position",position + "");
         v.startDrag(data, pieceDragShadowBuilder, v, 0);
         return true;
}

then in my Listener its :

                @Override
                public boolean onDrag(View pV, DragEvent pEvent) {
                  final int dragAction = pEvent.getAction();

                  View dragView = (View) pEvent.getLocalState();
                  final FrameLayout container = (FrameLayout) pV;

                            if (dragAction == DragEvent.ACTION_DRAG_STARTED)
                            {

                            } 
                            else if (dragAction == DragEvent.ACTION_DRAG_ENTERED)
                            {

                            }
                            else if (dragAction == DragEvent.ACTION_DRAG_EXITED) 
                            {

                            } 
                            else if (dragAction == DragEvent.ACTION_DROP) 
                            {

                            }

Now the only difference i see from your code is that i am calling the startDrag() method in my onTouch() regardless of the specific events of MOVE or UP or CANCEL.

Upvotes: 0

Related Questions