title: Touch Image Zooming in android
body: <p>I got a series of images in a gallery and was trying to add in the zooming functionality for the same. I read a lot on zooming in android. Worked out various examples. Finally i could create a project to zoom images by drawing that particular image from the drawable folder without using any xml file. It is shown below</p>
  
  <p>I created a java class called Zooming.java</p>
  
  <pre><code>public class Zooming extends ImageView {
  
      Matrix matrix;
  
      //3 states for zooming 
      static final int NONE = 0;
      static final int DRAG = 1;
      static final int ZOOM = 2;
      int mode = NONE;
  
      PointF last = new PointF();
      PointF start = new PointF();
      float minScale = 1f;
      float maxScale = 3f;
      float[] m;
  
      int viewWidth, viewHeight;
      static final int CLICK = 3;
      float saveScale = 1f;
      protected float origWidth, origHeight;
      int oldMeasuredWidth, oldMeasuredHeight;
  
      ScaleGestureDetector mScaleDetector;
  
      Context context;
  
      public Zooming(Context context) {
          super(context);
          sharedConstructing(context);
      }
  
      public Zooming(Context context, AttributeSet attrs) {
          super(context, attrs);
          sharedConstructing(context);
      }
  
      private void sharedConstructing(Context context) {
          super.setClickable(true);
          this.context = context;
          mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
          matrix = new Matrix();
          m = new float[9];
          setImageMatrix(matrix);
          setScaleType(ScaleType.MATRIX);
  
          setOnTouchListener(new OnTouchListener() {
  
              @Override
              public boolean onTouch(View v, MotionEvent event) {
                  mScaleDetector.onTouchEvent(event);
                  PointF curr = new PointF(event.getX(), event.getY());
  
                  switch (event.getAction()) {
                  case MotionEvent.ACTION_DOWN:
                      last.set(curr);
                      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 fixTransX = getFixDragTrans(deltaX, viewWidth,
                                  origWidth * saveScale);
                          float fixTransY = getFixDragTrans(deltaY, viewHeight,
                                  origHeight * saveScale);
                          matrix.postTranslate(fixTransX, fixTransY);
                          fixTrans();
                          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
              }
  
          });
      }
  
      public void setMaxZoom(float x) {
          maxScale = x;
      }
  
      private class ScaleListener extends
              ScaleGestureDetector.SimpleOnScaleGestureListener {
          @Override
          public boolean onScaleBegin(ScaleGestureDetector detector) {
              mode = ZOOM;
              return true;
          }
  
          @Override
          public boolean onScale(ScaleGestureDetector detector) {
              float mScaleFactor = detector.getScaleFactor();
              float origScale = saveScale;
              saveScale *= mScaleFactor;
              if (saveScale &gt; maxScale) {
                  saveScale = maxScale;
                  mScaleFactor = maxScale / origScale;
              } else if (saveScale &lt; minScale) {
                  saveScale = minScale;
                  mScaleFactor = minScale / origScale;
              }
  
              if (origWidth * saveScale &lt;= viewWidth
                      || origHeight * saveScale &lt;= viewHeight)
                  matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2,
                          viewHeight / 2);
              else
                  matrix.postScale(mScaleFactor, mScaleFactor,
                          detector.getFocusX(), detector.getFocusY());
  
              fixTrans();
              return true;
          }
      }
  
      void fixTrans() {
          matrix.getValues(m);
          float transX = m[Matrix.MTRANS_X];
          float transY = m[Matrix.MTRANS_Y];
  
          float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
          float fixTransY = getFixTrans(transY, viewHeight, origHeight
                  * saveScale);
  
          if (fixTransX != 0 || fixTransY != 0)
              matrix.postTranslate(fixTransX, fixTransY);
      }
  
      float getFixTrans(float trans, float viewSize, float contentSize) {
          float minTrans, maxTrans;
  
          if (contentSize &lt;= viewSize) {
              minTrans = 0;
              maxTrans = viewSize - contentSize;
          } else {
              minTrans = viewSize - contentSize;
              maxTrans = 0;
          }
  
          if (trans &lt; minTrans)
              return -trans + minTrans;
          if (trans &gt; maxTrans)
              return -trans + maxTrans;
          return 0;
      }
  
      float getFixDragTrans(float delta, float viewSize, float contentSize) {
          if (contentSize &lt;= viewSize) {
              return 0;
          }
          return delta;
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
          viewWidth = MeasureSpec.getSize(widthMeasureSpec);
          viewHeight = MeasureSpec.getSize(heightMeasureSpec);
  
          if (oldMeasuredHeight == viewWidth &amp;&amp; oldMeasuredHeight == viewHeight
                  || viewWidth == 0 || viewHeight == 0)
              return;
          oldMeasuredHeight = viewHeight;
          oldMeasuredWidth = viewWidth;
  
          if (saveScale == 1) {
              float scale;
  
              Drawable drawable = getDrawable();
              if (drawable == null || drawable.getIntrinsicWidth() == 0
                      || drawable.getIntrinsicHeight() == 0)
                  return;
              int bmWidth = drawable.getIntrinsicWidth();
              int bmHeight = drawable.getIntrinsicHeight();
  
              Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
  
              float scaleX = (float) viewWidth / (float) bmWidth;
              float scaleY = (float) viewHeight / (float) bmHeight;
              scale = Math.min(scaleX, scaleY);
              matrix.setScale(scale, scale);
  
              // Center the image
              float redundantYSpace = (float) viewHeight
                      - (scale * (float) bmHeight);
              float redundantXSpace = (float) viewWidth
                      - (scale * (float) bmWidth);
              redundantYSpace /= (float) 2;
              redundantXSpace /= (float) 2;
  
              matrix.postTranslate(redundantXSpace, redundantYSpace);
  
              origWidth = viewWidth - 2 * redundantXSpace;
              origHeight = viewHeight - 2 * redundantYSpace;
              setImageMatrix(matrix);
          }
          fixTrans();
      }
  }
  </code></pre>
  
  <p>And the MainActivity class is as follows:</p>
  
  <pre><code>public class MainActivity extends Activity {
  
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          Zooming img = new Zooming(this);
          img.setImageResource(R.drawable.pic_1);
          img.setMaxZoom(4f);
          setContentView(img);
      }
  </code></pre>
  
  <p>It is working fine. I can zoom the pic with ease.
  My problem is, i want to zoom the pic by using main.xml file. That is by using an imageview or CustomView (com.ZomThePic.Zooming) and giving the source of image there. I tried by adding the imageview as well as by using the Zooming CustomView in my main.xml</p>
  
  <p>main.xml</p>
  
  <pre><code>&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  
      android:orientation="vertical"
  
      android:layout_width="match_parent"
  
      android:layout_height="match_parent"
  
  &lt;com.zoomthepic.zooming
  
      android:layout_width="match_parent"
  
      android:layout_height="match_parent"
  
      android:id="@+id/pic_id"
  
      android:src="@drawable/pic_1"/&gt;
  
  
  &lt;LinearLayout/&gt;
  </code></pre>
  
  <p>And by changing the code in MainActivity as :</p>
  
  <p>public class MainActivity extends Activity {</p>
  
  <pre><code>protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
  
      Zooming img = (Zooming)findViewById(R.id.pic_id);
      img.setMaxZoom(4);
  }
  </code></pre>
  
  <p>But Unfortunately the application get ceased. Is it possible to get the zooming effect for the image given in the layout file by adding android:src="@drawable/pic_1"  rather than by defining the pic in a java class with an R.drawable.pic_1 by using the same Zooming java class ?  Can anyone help me to resolve the problem ?</p>