androidcanvasbitmapdraggestures

Strange behaviour in canvas dragging


I'm building an application which will have incorporated in it image dragging (a bitmap) on a canvas. I want to implement it so it works and acts well, can anyone give me a hand? Here's the code:

    public boolean onTouchEvent(MotionEvent event) {
    mScaleDetector.onTouchEvent(event);

    final int action = event.getAction();

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN & MotionEvent.ACTION_MASK: {
            final float xScale = event.getX();
            final float yScale = event.getY();

            mLastTouchX = xScale;
            mLastTouchY = yScale;
            mActivePointerId = event.getPointerId(0);
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            final int pointerIndex = event.findPointerIndex(mActivePointerId);
            final float xScale = event.getX(pointerIndex);
            final float yScale = event.getY(pointerIndex);
            if (!mScaleDetector.isInProgress()) {
                final float dx = x - mLastTouchX;
                final float dy = y - mLastTouchY;
                mPosX += dx;
                mPosY += dy;

                invalidate();
            }
            mLastTouchX = x;
            mLastTouchY = y;
            if (event.getX() < overlay.getHeight()) {
                x = (int) event.getX();
            }
            if (event.getY() < overlay.getWidth()) {
                y = (int) event.getY();
            }
            invalidate();


            break;
        }
        case MotionEvent.ACTION_UP: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            mActivePointerId = INVALID_POINTER_ID;
            break;
        }

        case MotionEvent.ACTION_POINTER_UP: {
            final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = event.getPointerId(pointerIndex);
            if (pointerId == mActivePointerId) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                mLastTouchX = event.getX(newPointerIndex);
                mLastTouchY = event.getY(newPointerIndex);
                mActivePointerId = event.getPointerId(newPointerIndex);
            }
            break;
        }
    }

    return true;
}

void init(Context context) {
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    serverResult = Bitmap.createBitmap(mask.getWidth(),mask.getHeight(), Bitmap.Config.ARGB_8888);
    result = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Bitmap.Config.ARGB_8888);
    width = mask.getWidth();
    height = mask.getHeight();
    int overlayHeight = overlay.getHeight();
    int overlayWidth = overlay.getWidth();
    int imgHeight = originalImg.getHeight();
    int imgWidth = originalImg.getWidth();

    int heightFactor = imgHeight / overlayHeight;
    int widthFactor = imgWidth / overlayWidth;
    if (overlayHeight > imgHeight) {
        heightFactor = overlayHeight / imgHeight;
    }
    if (overlayWidth > imgWidth) {
        widthFactor = overlayWidth / imgWidth;
    }
    int newWidth;
    int newHeight;
    if (widthFactor != 0) {
        newWidth = imgWidth / widthFactor;
    } else {
        newWidth = imgWidth / heightFactor;
    }
    if (heightFactor != 0) {
        newHeight = imgHeight / widthFactor;
    } else {
        newHeight = imgHeight / heightFactor;
    }


    config = Bitmap.Config.ARGB_8888;
    DST_IN = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
    scaledImg = Bitmap.createScaledBitmap(originalImg, newWidth, newHeight, false);
    scaledImgFilterd = Bitmap.createScaledBitmap(originalImg, newWidth, newHeight, false);
    tempCanvas = new Canvas(result);
    imageCanvas = new Canvas(result);
    image = new GPUImage(context);




}

@Override
public void onDraw(Canvas canvas) {

    if (FILTER_APPLIED == 100) {
    if (filter != null) {
        image.setImage(scaledImg);
        image.setFilter(filter);
        scaledImgFilterd = image.getBitmapWithFilterApplied();
        FILTER_APPLIED = 0;
    }
    }

    paint.setXfermode(DST_IN);
    if(color == 0) {
        imageCanvas.drawColor(Color.WHITE);
    } else {
        imageCanvas.drawColor(color);

    }
    imageCanvas.scale(mScaleFactor, mScaleFactor);
    imageCanvas.drawBitmap(scaledImgFilterd, mLastTouchX, mLastTouchY, null);
    tempCanvas.drawBitmap(mask, 0, 0, paint);
    paint.setXfermode(null);
    canvas.drawBitmap(result, 0, 0, null);
    canvas.drawBitmap(overlay, 0, 0, null);

}

public void setColor(int color) {
    this.color = color;
    invalidate();
}

public void setFilter(GPUImageFilter filter) {
    this.filter = filter;
    FILTER_APPLIED = 100;
    invalidate();
}


private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        mScaleFactor *= detector.getScaleFactor();

        // Don't let the object get too small or too large.
        mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));

        invalidate();
        return true;
    }
}

When I drag it drags according to the top-left corner and everything is a bit sluggish, can someone can help me please?


Solution

  • I can't see why it would be sluggish (tbh, it's too much code) but below is an explanation for the top-left corner dragging

    In order to counteract the dragging in the top left corner, you need to add some offset variables depending on where on the image you clicked (touched).

    Let's say you touched at (touchX,touchY), you need to check how far away from the top-left corner of your image that was.

    1. You first need to declare two new variables, for example touchOffsetX and touchOffsetY.
    2. Then you define your new variables when the screen is touched.

      touchOffsetX = touchX - image.x;

      touchOffsetY = touchY - image.y;

    3. Now when you're moving the image, you set its x- and y-coordinates are set according to below

      image.x = touchX - touchOffsetX;

      image.y = touchY - touchOffsetX;

    You of course need to change the variable names for what you have in your code.

    Below is a working example in HTML5 canvas to kind of further explaining the idea.

    var c = document.getElementById("canvas");
    var ctx = c.getContext("2d");
    c.width = 300;
    c.height = 200;
    
    var myRectangle = {
        x: 50,
        y: 130,
        w: 100,
        h: 50,
        drag: false
    }
    
    var mouseX = 0;
    var mouseY = 0;
    var mouseOffset = {x: 0, y:0}; // How far from the rectangle corner are we clicking?
    var background = "#0099CC"; 
    
    c.addEventListener("mousemove",mouseMove,false);
    c.addEventListener("mousedown",mouseDown,false);
    c.addEventListener("mouseup",mouseUp,false);
    
    //Getting the 
    function getMouseCoordinates() {
        event = event || window.event;
        mouseX = event.pageX - c.offsetLeft,
            mouseY = event.pageY - c.offsetTop;  
    }
    
    //This is what should be done while moving the mouse
    function mouseMove(event) {
        getMouseCoordinates();
        if (myRectangle.drag) {
            myRectangle.x = mouseX-mouseOffset.x;
            myRectangle.y = mouseY-mouseOffset.y;
        }
    }
    
    // This is what should happen when mouse button is pressed down
    function mouseDown() {
        if (mouseX > myRectangle.x &&
            mouseX < myRectangle.x+myRectangle.w &&
            mouseY > myRectangle.y &&
            mouseY < myRectangle.y + myRectangle.h) {
            if (document.getElementById("offset").checked) {
                mouseOffset.x = mouseX-myRectangle.x;
                mouseOffset.y = mouseY-myRectangle.y;
            }
            else {
                mouseOffset.x = 0;
                mouseOffset.y = 0;
            }
            
            myRectangle.drag = true;
            background = "#66FF99";
        }
    }
    
    // This is what should happen when mouse button is released back up
    function mouseUp() {
        myRectangle.drag = false;
        background = "#0099CC"; 
    }
    
    function draw() {
        // Clear the canvas before painting.
        ctx.fillStyle="#F0F0F0";
        ctx.fillRect(0,0,c.width,c.height);
        
        //Drawing the rectangle
        ctx.fillStyle=background;
        ctx.fillRect(myRectangle.x,myRectangle.y,myRectangle.w,myRectangle.h); 
        ctx.strokeRect(myRectangle.x,myRectangle.y,myRectangle.w,myRectangle.h);
        
        // Loop the drawing
        requestAnimationFrame(draw);
    }
    
    draw(); //we need to initiate the drawing to start it.
    <canvas id="canvas"></canvas><br />
    <input type="checkbox" id="offset">Check to use offset values.