Reputation: 507
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
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
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
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