Shubham Shubham - 13 days ago 6
Android Question

Touch screen is not adjusting according to zoomed image

I am doing image annotations in android and I've added zooming functionality into it. So now when I am zooming the image and after that doing any annotation, its not on that point where I touched the screen rather than its doing on as that of original image (Unzoomed Image).

onDraw Method

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
onDrawReady = true;

imageRenderedAtLeastOnce = true;
canvas.concat(matrix);

for (DrawObject d : paths) {
if (d.getType() == MODE_DRAWING) {
canvas.drawPath(d.getPair().first, d.getPair().second);
} else if (d.getType() == MODE_TEXT) {
canvas.drawText(d.getText(), d.getX(), d.getY(), d.getPair().second);
} else if (d.getType() == MODE_ARROW) {
canvas.drawLine(d.getStartX(), d.getStartY(), d.getX(), d.getY(), d.getPair().second);
fillArrow(canvas, d.getStartX(), d.getStartY(), d.getX(), d.getY(), d.getPair().second);
} else if (d.getType() == MODE_CIRCLE) {
RectF oval2 = new RectF(d.getStartX(), d.getStartY(), d.getX(), d.getY());
canvas.drawOval(oval2, d.getPair().second);
} else if (d.getType() == MODE_RECTANGLE) {
canvas.drawRect(d.getStartX(), d.getStartY(), d.getX(), d.getY(), d.getPair().second);
} else if (d.getType() == MODE_ERASE) {
canvas.drawPath(d.getPair().first, d.getPair().second);
}
}
canvas.save();
}


onTouch Method

@Override
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
mGestureDetector.onTouchEvent(event);
PointF curr = new PointF(event.getX(), event.getY());

mX = event.getX() + mDistX;
mY = event.getY() + mDistY;

if (state == State.NONE || state == State.DRAG || state == State.FLING) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawFlag = false;
mStartX = mX;
mStartY = mY;

if (mMode == MODE_ARROW) {
mPath = new Path();
mLineDrawObject = new DrawObject(MODE_ARROW, new Pair<Path, Paint>(mPath, getLinePaint()));
drawFlag = true;
} else if (mMode == MODE_TEXT) {
drawFlag = true;
} else if (mMode == MODE_CIRCLE) {
mPath = new Path();
mCircleDrawObject = new DrawObject(MODE_CIRCLE, new Pair<Path, Paint>(mPath, getCirclePaint()));
drawFlag = true;
} else if (mMode == MODE_RECTANGLE) {
mPath = new Path();
mRectangleObject = new DrawObject(MODE_RECTANGLE, new Pair<Path, Paint>(mPath, getRectanglePaint()));
drawFlag = true;
} else if (mMode == MODE_DRAWING) {
mPath = new Path();
mDrawingDrawObject = new DrawObject(MODE_DRAWING, new Pair<Path, Paint>(mPath, getDrawPaint()));
drawFlag = true;
} else if (mMode == MODE_ERASE) {
mPath = new Path();
mEraseObject = new DrawObject(MODE_ERASE, new Pair<Path, Paint>(mPath, getErasePaint()));
drawFlag = true;
}
if (drawFlag == true) {
touch_start(mX, mY);
invalidate();
}

last.set(curr);
if (fling != null) {
fling.cancelFling();
}
setState(State.DRAG);
break;

case MotionEvent.ACTION_MOVE:
if (state == State.DRAG && drawFlag == false) {
float deltaX = curr.x - last.x;
float deltaY = curr.y - last.y;
float fixTransX = getFixDragTrans(deltaX, viewWidth,
getImageWidth());
float fixTransY = getFixDragTrans(deltaY, viewHeight,
getImageHeight());
matrix.postTranslate(fixTransX, fixTransY);

fixTrans();
last.set(curr.x, curr.y);
}
if (drawFlag == true) {
touch_move(mX, mY);
invalidate();
}
break;

case MotionEvent.ACTION_UP:
if (drawFlag == true) {
touch_up();
}
break;
case MotionEvent.ACTION_POINTER_UP:
setState(State.NONE);
break;

}
}

setImageMatrix(matrix);
//mCanvas.concat(matrix);

//
// User-defined OnTouchListener
//
if (userTouchListener != null) {
userTouchListener.onTouch(v, event);
}

//
// OnTouchImageViewListener is set: TouchImageView dragged by user.
//
if (touchImageViewListener != null) {
touchImageViewListener.onMove();
}

//
// indicate event was handled
//
return true;
}
}

Answer

I finally found an answer to the problem I asked. It was happening because of the pixel mapping difference between the Touch Coordinates and the Canvas Coordinates.

Adding this to the onTouch Method Solved my problem

PointF curr = new PointF();
//Add this line
curr = transformCoordTouchToBitmap(event.getX(), event.getY(), false);
mX = curr.x;
mY = curr.y;

And define this method in your code.

private PointF transformCoordTouchToBitmap(float x, float y, boolean clipToBitmap) {
    matrix.getValues(m);
    float origW = getDrawable().getIntrinsicWidth();
    float origH = getDrawable().getIntrinsicHeight();
    float transX = m[Matrix.MTRANS_X];
    float transY = m[Matrix.MTRANS_Y];
    float finalX = ((x - transX) * origW) / getImageWidth();
    float finalY = ((y - transY) * origH) / getImageHeight();

    if (clipToBitmap) {
        finalX = Math.min(Math.max(finalX, 0), origW);
        finalY = Math.min(Math.max(finalY, 0), origH);
    }

    return new PointF(finalX, finalY);
}