Raevik
Raevik

Reputation: 1991

How to drag and drop while preserving the original android View?

I'm working on an Android game.

I'm wondering if drag and drop is simply the wrong approach to tackle this. The effect I'm looking for is to have a button that when the user long-presses, will initiate a drag and drop effect with the drop-shadow being a target.

This custom drag shadow would persist until the user releases the target shadow. I have the custom shadow working and am responding to drag events. What I am not sure about is how to make this whole thing work without actually moving the button they are initiating the drag from.

Is it as simple as somehow not passing the originating View (button) to the shadow builder?

EDIT


Added definition of touch listener I'm using to initiate the drag.

private final class MyTouchListener implements OnTouchListener {
    public boolean onTouch(View view, MotionEvent motionEvent) {
      if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
        ClipData data = ClipData.newPlainText("", "");
        //DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
        view.startDrag(data, new TargetDragShadowBuilder(view), view, 0);
        view.setVisibility(View.INVISIBLE);
        return true;
      } else {
        return false;
      }
    }
  }

I am setting the visibility here, though to be honest I'm frankly trying to follow the tutorial here:
http://www.vogella.com/tutorials/AndroidDragAndDrop/article.html

Perhaps this is the wrong approach. I'm basically looking to do the following steps:

1) Detect a drag event starting with a button

2) Without altering the button in any way, I want a custom drop shadow of a picture I specify (with a transparency) to appear under the user's finger as they drag across the screen.

Eventually I need to figure out how to make the shadow flash, or linger for a few seconds before disappearing and also to get the location where the user released their finger. It looks like from the docs I can get that from ACTION_DRAG_LOCATION...?

Upvotes: 1

Views: 3519

Answers (3)

Raevik
Raevik

Reputation: 1991

Issue was that I was setting the view invisible (facepalm) in response to the touch listener. Furthermore my shadow was passing the view to super() making the view come along for the ride. Combined effect was the appearance of the view being dragged away and lost forever. Fixed by removing visibility call and removing the view from super class in customer dropshadowbuilder.

Upvotes: 0

stealth
stealth

Reputation: 381

There is an official API Guide on how to implement Drag and Drop in Android. Unfortunately solution proposed by bonnyz does not comply to this guideline - adding a temporary view to indicate a drag operation is somewhat a heavy-weight solution.

Answering your question:

Is it as simple as somehow not passing the originating View (button) to the shadow builder?

The documentation says:

View.DragShadowBuilder(View)

This constructor accepts any of your application's View objects. The constructor stores the View object in the View.DragShadowBuilder object, so during the callback you can access it as you construct your drag shadow. It doesn't have to be associated with the View (if any) that the user selected to start the drag operation.

So, the view you pass there is not used to represent the shadow. The shadow is what you draw on Canvas within onDrawShadow() method implementation.

Basically, the shadow is just a graphics that follows the finger while being dropped. It is not a view and does not have to look like any view in the activity. It is arbitrary graphics that provides an idea to the user what is being dragged and dropped.

Upvotes: 2

bonnyz
bonnyz

Reputation: 13548

Here's my suggestion:

1) When user longPress on the button, get the absolute coordinates of the button:

 int coord[] = new int[2];
 button.getLocationOnScreen(coord);

2) Create the "ShadowView" which need to be shown under the finger while the user drags (as you like)

3) Attach the ShadowView to the Window (leaving others layout inalterated):

windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

View shadowView = /..shadow view.../

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_PHONE,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT);

params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = coord[0]; //add offset if needed 
params.y = coord[1];

windowManager.addView(shadowView, params);

4) Update params using the user dragging position.

params.x = // drag X - halfsize of the view's width
params.y = // drag Y - halfsize of the view's height
windowManager.updateViewLayout(shadowView, params);

5) When the user stop dragging, remove the ShadowView from the Window:

windowManager.removeViewImmediate(shadowView);

Upvotes: 1

Related Questions