After we get the translation information, we actually want to translate the canvas. We can do this in the onDraw(Canvas canvas)
method. Recall that earlier when we had only implemented zooming, we called invalidate()
inside the onScale(ScaleGestureDetector detector)
listener. The problem is that the onScale(...)
method is called only when zooming happens and not when panning or dragging happens. So any calculations we make inside the onTouchEvent(...)
method during panning won't get reflected inside the onDraw(...)
method. So what we need to do first, is to remove the call to invalidate()
out of the onScale(...)
method. When we do that, the method ends up looking like this:
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
This is not a huge deal because all we really care about is the value of scaleFactor
; we can call invalidate()
whenever we deem it convenient. So now that we have both the scaling and translating information at hand, we can add code to the onDraw(...)
method to take this into account. We will also add code to the onTouchEvent(...)
method that will call invalidate()
:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
...
...
}
//This will set the value of scaleFactor
detector.onTouchEvent(event);
//The only time we want to re-draw the canvas is if we are panning (which happens when the mode is
//DRAG and the zoom factor is not equal to 1) or if we're zooming
if ((mode == DRAG && scaleFactor != 1f) || mode == ZOOM) {
invalidate();
}
return true;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//We're going to scale the X and Y coordinates by the same amount
canvas.scale(scaleFactor, scaleFactor);
//We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
//because the translation amount also gets scaled according to how much we've zoomed into the canvas.
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
canvas.restore();
}
I appreciate how clear you tried to explain everything. Thank you!
I want to do a “zoomable paint”, I mean a paint that I can zoom/zoom out and pan/drag the canvas and then draw on it.
I have a problem that I can’t solve: when I draw while the canvas is zoomed, I retrieve the X and Y coordinate and effectively drawing it on the canvas. But these coordinates are not correct because of the zoomed canvas.
I tried to correct these (multiply by (zoomHeigh/screenHeight)) but I can’t find a way to retrieve where I must draw on the original/none-zoomed screen
Whenever I try to implement above method the screen jitters on account of zoom effect but does not zoom essentially. I am drawing circle points on canvas and on zooming I want them to scale further apart. I don’t know if I tend to custom draw those circle points based on dimensions of zoomed canvas why is the overall effect a mere jitter ?