title: Canvas - zooming in, shifting, and scaling on Android
body: <p>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:</p>
  
  <p><img src="https://i.stack.imgur.com/NB5Ui.png" alt="Normal"></p>
  
  <p><img src="https://i.stack.imgur.com/eGJnX.png" alt="enter image description here"></p>
  
  <pre><code>@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;
      }
  }
  </code></pre>
  
  <p><strong>Edit</strong>: 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. </p>
  
  <p>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:</p>
  
  <pre><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() &gt; 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;
      }
  }
  </code></pre>