title: How do I modify TouchImageView with double tap to zoom in and out?
body: <p>I am modifying the TouchImageView (<a href="https://github.com/MikeOrtiz/TouchImageView/issues" rel="nofollow noreferrer">https://github.com/MikeOrtiz/TouchImageView/issues</a>) to have zoom in and out for when you double tap. I have started as per this post - <a href="https://stackoverflow.com/questions/7491519/how-does-touchimageview-works">How does TouchImageView works?</a> and have added the gesture detection.</p>
  
  <p>Now I have to implement the zooming in and zooming out which I am not sure how to do. Here is the code I have so far with the zoomIn and zoomOut methods unimplemented. Anyone know how to do this? In addition I noticed that the pinch zoom doesn't really zoom to the location where you pinch, so I was hoping that this could be made to behave more like the gallery3D pinch zoom. Thanks.</p>
  
  <pre><code>import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Matrix;
  import android.graphics.PointF;
  import android.util.AttributeSet;
  import android.view.GestureDetector;
  import android.view.GestureDetector.SimpleOnGestureListener;
  import android.view.MotionEvent;
  import android.view.ScaleGestureDetector;
  import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
  import android.view.View;
  import android.widget.ImageView;
  
  public class TouchImageView extends ImageView {
  
  private static final String TAG = TouchImageView.class.getSimpleName();
  
  Matrix matrix = new Matrix();
  
  // We can be in one of these 3 states
  static final int NONE = 0;
  static final int DRAG = 1;
  static final int ZOOM = 2;
  int mode = NONE;
  
  // Remember some things for zooming
  PointF last = new PointF();
  PointF start = new PointF();
  float minScale = 1f;
  float maxScale = 3f;
  float[] m;
  
  float redundantXSpace, redundantYSpace;
  
  float width, height;
  static final int CLICK = 3;
  float saveScale = 1f;
  float right, bottom, origWidth, origHeight, bmWidth, bmHeight;
  
  ScaleGestureDetector mScaleDetector;
  
  private GestureDetector gestureDetector;
  
  Context context;
  
  public TouchImageView(Context context, AttributeSet attrs) {
      super(context, attrs);
      init(context);
  }
  
  public TouchImageView(Context context) {
      super(context);
      init(context);
  }
  
  public void init(Context context) {
      gestureDetector = new GestureDetector(new DoubleTapGestureListener());
  
      super.setClickable(true);
      this.context = context;
      mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
      matrix.setTranslate(1f, 1f);
      m = new float[9];
      setImageMatrix(matrix);
      setScaleType(ScaleType.MATRIX);
  
      setOnTouchListener(new OnTouchListener() {
  
          @Override
          public boolean onTouch(View v, MotionEvent event) {
  
              if (gestureDetector.onTouchEvent(event)) {
                  return true;
              }
  
              mScaleDetector.onTouchEvent(event);
  
              matrix.getValues(m);
              float x = m[Matrix.MTRANS_X];
              float y = m[Matrix.MTRANS_Y];
              PointF curr = new PointF(event.getX(), event.getY());
  
              switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
                  last.set(event.getX(), event.getY());
                  start.set(last);
                  mode = DRAG;
                  break;
              case MotionEvent.ACTION_MOVE:
                  if (mode == DRAG) {
                      float deltaX = curr.x - last.x;
                      float deltaY = curr.y - last.y;
                      float scaleWidth = Math.round(origWidth * saveScale);
                      float scaleHeight = Math.round(origHeight * saveScale);
                      if (scaleWidth &lt; width) {
                          deltaX = 0;
                          if (y + deltaY &gt; 0)
                              deltaY = -y;
                          else if (y + deltaY &lt; -bottom)
                              deltaY = -(y + bottom);
                      } else if (scaleHeight &lt; height) {
                          deltaY = 0;
                          if (x + deltaX &gt; 0)
                              deltaX = -x;
                          else if (x + deltaX &lt; -right)
                              deltaX = -(x + right);
                      } else {
                          if (x + deltaX &gt; 0)
                              deltaX = -x;
                          else if (x + deltaX &lt; -right)
                              deltaX = -(x + right);
  
                          if (y + deltaY &gt; 0)
                              deltaY = -y;
                          else if (y + deltaY &lt; -bottom)
                              deltaY = -(y + bottom);
                      }
                      matrix.postTranslate(deltaX, deltaY);
                      last.set(curr.x, curr.y);
                  }
                  break;
  
              case MotionEvent.ACTION_UP:
                  mode = NONE;
                  int xDiff = (int) Math.abs(curr.x - start.x);
                  int yDiff = (int) Math.abs(curr.y - start.y);
                  if (xDiff &lt; CLICK &amp;&amp; yDiff &lt; CLICK)
                      performClick();
                  break;
  
              case MotionEvent.ACTION_POINTER_UP:
                  mode = NONE;
                  break;
              }
              setImageMatrix(matrix);
              invalidate();
              return true; // indicate event was handled
          }
  
      });
  }
  
  @Override
  public void setImageBitmap(Bitmap bm) {
      super.setImageBitmap(bm);
      bmWidth = bm.getWidth();
      bmHeight = bm.getHeight();
  }
  
  public void setMaxZoom(float x) {
      maxScale = x;
  }
  
  private class ScaleListener extends SimpleOnScaleGestureListener {
      @Override
      public boolean onScaleBegin(ScaleGestureDetector detector) {
          mode = ZOOM;
          return true;
      }
  
      @Override
      public boolean onScale(ScaleGestureDetector detector) {
  
          LogUtil.i(TAG, detector.getScaleFactor() + " " + detector.getFocusX() + " " + detector.getFocusY());
  
          float mScaleFactor = (float) Math.min(Math.max(.95f, detector.getScaleFactor()), 1.05);
          float origScale = saveScale;
          saveScale *= mScaleFactor;
          if (saveScale &gt; maxScale) {
              saveScale = maxScale;
              mScaleFactor = maxScale / origScale;
          } else if (saveScale &lt; minScale) {
              saveScale = minScale;
              mScaleFactor = minScale / origScale;
          }
          right = width * saveScale - width - (2 * redundantXSpace * saveScale);
          bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
          if (origWidth * saveScale &lt;= width || origHeight * saveScale &lt;= height) {
              matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
              if (mScaleFactor &lt; 1) {
                  matrix.getValues(m);
                  float x = m[Matrix.MTRANS_X];
                  float y = m[Matrix.MTRANS_Y];
                  if (mScaleFactor &lt; 1) {
                      if (Math.round(origWidth * saveScale) &lt; width) {
                          if (y &lt; -bottom)
                              matrix.postTranslate(0, -(y + bottom));
                          else if (y &gt; 0)
                              matrix.postTranslate(0, -y);
                      } else {
                          if (x &lt; -right)
                              matrix.postTranslate(-(x + right), 0);
                          else if (x &gt; 0)
                              matrix.postTranslate(-x, 0);
                      }
                  }
              }
          } else {
              matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
              matrix.getValues(m);
              float x = m[Matrix.MTRANS_X];
              float y = m[Matrix.MTRANS_Y];
              if (mScaleFactor &lt; 1) {
                  if (x &lt; -right)
                      matrix.postTranslate(-(x + right), 0);
                  else if (x &gt; 0)
                      matrix.postTranslate(-x, 0);
                  if (y &lt; -bottom)
                      matrix.postTranslate(0, -(y + bottom));
                  else if (y &gt; 0)
                      matrix.postTranslate(0, -y);
              }
          }
          return true;
  
      }
  }
  
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      width = MeasureSpec.getSize(widthMeasureSpec);
      height = MeasureSpec.getSize(heightMeasureSpec);
      // Fit to screen.
      float scale;
      float scaleX = (float) width / (float) bmWidth;
      float scaleY = (float) height / (float) bmHeight;
      scale = Math.min(scaleX, scaleY);
      matrix.setScale(scale, scale);
      setImageMatrix(matrix);
      saveScale = 1f;
  
      // Center the image
      redundantYSpace = (float) height - (scale * (float) bmHeight);
      redundantXSpace = (float) width - (scale * (float) bmWidth);
      redundantYSpace /= (float) 2;
      redundantXSpace /= (float) 2;
  
      matrix.postTranslate(redundantXSpace, redundantYSpace);
  
      origWidth = width - 2 * redundantXSpace;
      origHeight = height - 2 * redundantYSpace;
      right = width * saveScale - width - (2 * redundantXSpace * saveScale);
      bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
      setImageMatrix(matrix);
  }
  
  class DoubleTapGestureListener extends SimpleOnGestureListener {
  
      @Override
      public boolean onDown(MotionEvent e) {
          return true;
      }
  
      // event when double tap occurs
      @Override
      public boolean onDoubleTap(MotionEvent e) {
          float x = e.getX();
          float y = e.getY();
          LogUtil.i(TAG, "Tapped at: (" + x + "," + y + ")");
          if (isZoomed()) {
              zoomOut();
          } else {
              zoomIn();
          }
          return true;
      }
  
  }
  
  public boolean isZoomed() {
      return saveScale &gt; minScale; // this seems to work
  }
  
  public void zoomIn() {
      LogUtil.i(TAG, "Zooming in");
      // TODO: no idea how to do this
  
  }
  
  public void zoomOut() {
      LogUtil.i(TAG, "Zooming out");
      // TODO: no idea how to do this
  }
  
  }
  </code></pre>