Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set custom anchor when dragging a View

I am using Android Drag&Drop API and am trying to set the anchor of the drag shadow to the point where the touch was made in the View. The dafault behavior is to have the anchor the middle of the View.

I did some research and it seems this can be done by overriding the onProvideShadowMetrics (Point shadowSize, Point shadowTouchPoint) method in the DragShadowBuilder class. From what I understood, if I change the x,y coordinates of shadowTouchPoint it should modify the coordinates of the drag anchor.

What I did was to extend the DragShadowBuilder class like so:

class EventDragShadowBuilder extends DragShadowBuilder {

    int touchPointXCoord, touchPointYCoord;

    public EventDragShadowBuilder() {
        super();
    }

    public EventDragShadowBuilder(View view, int touchPointXCoord,
            int touchPointYCoord) {

        super(view);
        this.touchPointXCoord = touchPointXCoord;
        this.touchPointYCoord = touchPointYCoord;
    }

    @Override
    public void onProvideShadowMetrics(Point shadowSize,
            Point shadowTouchPoint) {

        shadowTouchPoint.set(touchPointXCoord, touchPointYCoord);

        super.onProvideShadowMetrics(shadowSize, shadowTouchPoint);
    }
}

In the Fragment where I use drag&drop I created two listeners to start a drag event for a View:

mEventLongClickListener = new OnLongClickListener() {

    @Override
    public boolean onLongClick(View view) {

        EventDragShadowBuilder shadowBuilder = new EventDragShadowBuilder(
            view, mEventTouchXCoord, mEventTouchYCoord);

        view.startDrag(null, shadowBuilder, view, 0);

        return true;
    }
};

// We need this listener in order to get the corect coordinates for the
// drag shadow
mEventTouchListener = new OnTouchListener() {

    @Override
    public boolean onTouch(View view, MotionEvent event) {

        final int action = event.getAction();

        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
            mEventTouchXCoord = (int) event.getX();
            mEventTouchYCoord = (int) event.getY();
            break;
        }
        }
        return false;
    }
};

And I set the tow listeners:

itemView.setOnLongClickListener(mEventLongClickListener);
itemView.setOnTouchListener(mEventTouchListener);

Everything ok up to here. But when I test the app and start the drag process, the drag shadow is centered under the touch point. So it uses the default behavior. I tried debugging and I see that mEventTouchXCoord and mEventTouchYCoord are set correctly. The method shadowTouchPoint.set(touchPointXCoord, touchPointYCoord); gets the correct coordinates but still it centers the shadow.

I don't know what I'm doing wrong. Maybe I misunderstood the API. Any help with or hint would be much appreciated.

like image 205
Bandreid Avatar asked Mar 17 '14 15:03

Bandreid


1 Answers

Ok, so like Scoup said the problem was in the onProvideShadowMetrics() method. The fact is, if I remove the super constructor, super.onProvideShadowMetrics(shadowSize, shadowTouchPoint);, it will not show the drag shadow anymore.

Taking a closer look at the API method this is what I found:

public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
    final View view = mView.get();
    if (view != null) {
        shadowSize.set(view.getWidth(), view.getHeight());
        shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
    } else {
        Log.e(View.VIEW_LOG_TAG, "Asked for drag thumb metrics but no view");
    }
}

So indeed, it resets the shadowTouchPoint to the middle of the dragged View. But it also initializes the drag shadow to the correct dimensions. While I want the drag shadow dimensions to be set correctly I don't want to reset shadowTouchPoint.

The easiest way to acheive this is to call the super constructor before initializing the shadowTouchPoint with the custom values, like so:

@Override
public void onProvideShadowMetrics(Point shadowSize,
    Point shadowTouchPoint) {

    super.onProvideShadowMetrics(shadowSize, shadowTouchPoint);

    shadowTouchPoint.set(touchPointXCoord, touchPointYCoord);    
}

Another solution is to deal with the drag shadow View yourself and skip the super constructor altogether. I will get back with a detailed update.

like image 66
Bandreid Avatar answered Sep 28 '22 09:09

Bandreid