Search code examples
androidcanvasdragshadow

How to position canvas in View.DragShadowBuilder.onProvideShadowMetrics


I have a requirement where a draggable view needs the shadow to be initially placed directly on top of the view - that is, it covers the entire view rather than relating to the touch point.

I've been trying all sorts to get this working with my subclass of View.DragShadowBuilder but with no luck. I hoped using drawRect would allow me to specify the rectangle points so that it would be drawn in the same rectangle as my view, but this results in no canvas being drawn.

However, just using canvas.drawColor(Color.CYAN) draws the rectangle, but relative to the touch point.

Here is my attempt using drawRect:

    private class VideoDragShadowBuilder extends View.DragShadowBuilder {

    public VideoDragShadowBuilder(View v) {
        super(v);
    }

    @Override
    public void onProvideShadowMetrics (Point size, Point touch) {
        int width, height;

        width = getView().getWidth();
        height = getView().getHeight();

        size.set(width, height);
        touch.set(width / 2, height / 2);
    }

    @Override
    public void onDrawShadow(Canvas canvas) {
        //canvas.drawColor(Color.CYAN);

        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setAlpha(45);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.CYAN);

        canvas.drawRect(getView().getLeft(), getView().getTop(), getView().getRight(), getView().getBottom(), paint);
    }
}

Here, the blue rectangle is slightly misaligned with the view underneath because of where I touched - I want it to totally cover the view regardless of where the user touches.

enter image description here


Solution

  • Here's the solution I ended up with using @pskink's suggestions. I didn't have to use drawRect at all, the key was setting the touch point which I previously didn't have as I wasn't using onTouchEvent. Adding this for anyone who might have the same problem.

    private Point lastTouch;
    
    public DraggableFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    
        dragListener = new View.OnLongClickListener() {
    
            @Override
            public boolean onLongClick(View v) {
                View.DragShadowBuilder videoShadow = new DragShadowBuilder(v);
                v.startDrag(draggableData, videoShadow, null, 0);
                return true;
            }
        };
    }
    
    @Override
    public boolean onTouchEvent (MotionEvent ev)    {
        lastTouch = new Point((int) ev.getX(), (int) ev.getY()) ;
        return super.onTouchEvent(ev);
    }
    
    private class DragShadowBuilder extends View.DragShadowBuilder {
    
        public VideoDragShadowBuilder(View v) {
            super(v);
        }
    
        @Override
        public void onProvideShadowMetrics (Point size, Point touch) {
            super.onProvideShadowMetrics(size, touch);
    
            // The touch point must be set in order for the canvas to properly fit the view
            if (lastTouch != null) {
                touch.set(lastTouch.x, lastTouch.y);
            }
        }
    
        @Override
        public void onDrawShadow(Canvas canvas) {
            super.onDrawShadow(canvas);
            canvas.drawColor(Color.CYAN);
        }
    }