I'm currently implementing a draw function on a webview image (the elephant below). I don't have a problem drawing on it but the zoom function does some funky stuff (2nd image below) on the drawing. When I zoom in, the drawing doesn't zoom but rather shifts over. Drawing at zoomed also doesn't work. My code is below:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath, drawPaint);
canvas.scale(mScaleFactor, mScaleFactor);
canvas.restore();
clipBounds = canvas.getClipBounds();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
float touchX = event.getX() / mScaleFactor + clipBounds.left;
float touchY = event.getY() / mScaleFactor + clipBounds.top;
if (Deal.on)
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
else {
super.onTouchEvent(event);
}
invalidate();
return true;
}
public class ScaleGestureListener extends 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, 5.0f));
invalidate();
return true;
}
}
Edit: I managed to get the canvas to redraw with zooming using the GestureDetector's scale factor. Additionally, I have a switch that toggles from drawing to zooming/webview controls. A problem that I run into is that double tap on WebView doesn't trigger the onScale gesture. Which means the canvas doesn't get redrawn and shifts on the zoom in.
I need to implement a feature that detects how much the scale factor is affected by the double tap zoom in. If anyone can suggest a solution to that. Here's the update code:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
clipBounds = canvas.getClipBounds();
canvas.save();
drawPaint.setStrokeWidth(8/mScaleFactor);
canvas.scale(mScaleFactor, mScaleFactor, 0, 0);
canvas.drawPath(drawPath, drawPaint);
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
pointerCount = event.getPointerCount();
mScaleDetector.onTouchEvent(event);
float touchX = (event.getX() + clipBounds.left) / mScaleFactor;
float touchY = (event.getY() + clipBounds.top) / mScaleFactor;
if (Deal.on){
if (event.getPointerCount() > 1){
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
}
else {
super.onTouchEvent(event);
}
invalidate();
return true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Try for a width based on our minimum
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
int w = resolveSizeAndState(minw, widthMeasureSpec, 1);
//int minh = MeasureSpec.getSize(w) - (int)mTextWidth + getPaddingBottom() + getPaddingTop();
int h = resolveSizeAndState(MeasureSpec.getSize(w), heightMeasureSpec, 0);
setMeasuredDimension(w, h);
}
public class ScaleGestureListener extends SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
// Don't let the object get too small or too large.
if(Deal.on == false) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 20.0f));
}
invalidate();
return true;
}
}
Take a look at this tutorial. Here the author uses two Matrix objects to manage translations and scalling. I suggest you to use matrices, otherwise calculating new coordinates can be tricky and tedious. Your problem is caused by not managing translations correctly. Note: in the tutorial, inside OnDraw(), it misses the
canvas.concat(matrix)
, put it right aftercanvas.drawBitmap(...)
A good way to accomplish all the different effects you are trying to do in a very simple manner is making use of the canvas method
This method allow you, to take the whole source bitmap or just a small sample of it (Rect src), and project it as you wish (Rect dst) doing the scaling / translation of matrix calculations automatically for you as shown in the example below:
This example just scales / zooms the whole image...
And this example takes just a sample of the image and shows it "inner zoom effect"
By making use of these src/dst objects you can do pretty much any effect you want on android, is a simple and really powerful tool
Hope this helps.
Regards!
Can you try to draw paths only one time, and keep the zooming the responsibility of the canvas itself
I have implemented this behaviour, but in a slightly different way. I used a matrix to handle all the zooming and scrolling (and rotation, in my case). It makes for neat code and works like clockwork. I don't know what is causing your funky behaviour, though.
Store a Matrix and another path as class members:
Replace your onScale:
in onDraw; skip saving the canvas, instead:
Happy coding!