Search This Blog

Support my cause by downloading

Saturday, 7 April 2012

Pinching Zoom in android Image View or Bitmap

See Updated Tutorial


This is little bit complex article. In android , we can achieve pinch zoom with two or more than two finger. But it s little bit complex. I have developed it with the help of one GuitHub project.

Here we use Bitmap, Gesture, Matrix and other Bitmap Basic function. I have develop simply three classes.
main class is TouchImageView.java that is a Image-View.you can set this class Object anywhere

Make one project and use my classes

main class TouchImageView.java 

 package com.ahmad;  
 import android.content.Context;  
 import android.graphics.Bitmap;  
 import android.graphics.Matrix;  
 import android.graphics.PointF;  
 import android.util.FloatMath;  
 import android.util.Log;  
 import android.view.MotionEvent;  
 import android.view.View;  
 import android.widget.ImageView;  
 public class TouchImageView extends ImageView  
 {  
   private static final String TAG = "Touch";  
   Matrix matrix = new Matrix();  
   Matrix savedMatrix = 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 start = new PointF();  
   PointF mid = new PointF();  
   float oldDist = 1f;  
   Context context;  
   public TouchImageView(Context context)  
   {  
     super(context);  
     super.setClickable(true);  
     this.context = context;  
     matrix.setTranslate(1f, 1f);  
     setImageMatrix(matrix);  
     setScaleType(ScaleType.MATRIX);  
     setOnTouchListener(new OnTouchListener()  
     {  
       @Override  
       public boolean onTouch(View v, MotionEvent rawEvent)  
       {  
         WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent);  
         // Dump touch event to log  
         // if (Viewer.isDebug == true)  
         {  
           // dumpEvent(event);  
           //  
         }  
         // Handle touch events here...  
         switch (event.getAction() & MotionEvent.ACTION_MASK)  
         {  
           case MotionEvent.ACTION_DOWN:  
           savedMatrix.set(matrix);  
           start.set(event.getX(), event.getY());  
           Log.d(TAG, "mode=DRAG");  
           mode = DRAG;  
           break;  
           case MotionEvent.ACTION_POINTER_DOWN:  
           oldDist = spacing(event);  
           Log.d(TAG, "oldDist=" + oldDist);  
           if (oldDist > 10f)  
           {  
             savedMatrix.set(matrix);  
             midPoint(mid, event);  
             mode = ZOOM;  
             Log.d(TAG, "mode=ZOOM");  
           }  
           break;  
           case MotionEvent.ACTION_UP:  
           int xDiff = (int) Math.abs(event.getX() - start.x);  
           int yDiff = (int) Math.abs(event.getY() - start.y);  
           if (xDiff < 8 && yDiff < 8)  
           {  
             performClick();  
           }  
           case MotionEvent.ACTION_POINTER_UP:  
           mode = NONE;  
           Log.d(TAG, "mode=NONE");  
           break;  
           case MotionEvent.ACTION_MOVE:  
           if (mode == DRAG)  
           {  
             // ...  
             matrix.set(savedMatrix);  
             matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);  
           }  
           else if (mode == ZOOM)  
           {  
             float newDist = spacing(event);  
             Log.d(TAG, "newDist=" + newDist);  
             if (newDist > 10f)  
             {  
               matrix.set(savedMatrix);  
               float scale = newDist / oldDist;  
               matrix.postScale(scale, scale, mid.x, mid.y);  
             }  
           }  
           break;  
         }  
         setImageMatrix(matrix);  
         return true; // indicate event was handled  
       }  
     }  
     );  
   }  
   public void setImage(Bitmap bm, int displayWidth, int displayHeight)  
   {  
     super.setImageBitmap(bm);  
     //Fit to screen.  
     float scale;  
     if ((displayHeight / bm.getHeight()) >= (displayWidth / bm.getWidth()))  
     {  
       scale = (float)displayWidth / (float)bm.getWidth();  
     }  
     else  
     {  
       scale = (float)displayHeight / (float)bm.getHeight();  
     }  
     savedMatrix.set(matrix);  
     matrix.set(savedMatrix);  
     matrix.postScale(scale, scale, mid.x, mid.y);  
     setImageMatrix(matrix);  
     // Center the image  
     float redundantYSpace = (float)displayHeight - (scale * (float)bm.getHeight()) ;  
     float redundantXSpace = (float)displayWidth - (scale * (float)bm.getWidth());  
     redundantYSpace /= (float)2;  
     redundantXSpace /= (float)2;  
     savedMatrix.set(matrix);  
     matrix.set(savedMatrix);  
     matrix.postTranslate(redundantXSpace, redundantYSpace);  
     setImageMatrix(matrix);  
   }  
   /** Show an event in the LogCat view, for debugging */  
   @SuppressWarnings("unused")  
   private void dumpEvent(WrapMotionEvent event)  
   {  
     String names[] =  
     {  
       "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",  
       "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?"  
     }  
     ;  
     StringBuilder sb = new StringBuilder();  
     int action = event.getAction();  
     int actionCode = action & MotionEvent.ACTION_MASK;  
     sb.append("event ACTION_").append(names[actionCode]);  
     if (actionCode == MotionEvent.ACTION_POINTER_DOWN  
     || actionCode == MotionEvent.ACTION_POINTER_UP)  
     {  
       sb.append("(pid ").append(  
       action >> MotionEvent.ACTION_POINTER_ID_SHIFT);  
       sb.append(")");  
     }  
     sb.append("[");  
     for (int i = 0; i < event.getPointerCount(); i++)  
     {  
       sb.append("#").append(i);  
       sb.append("(pid ").append(event.getPointerId(i));  
       sb.append(")=").append((int) event.getX(i));  
       sb.append(",").append((int) event.getY(i));  
       if (i + 1 < event.getPointerCount())  
       sb.append(";");  
     }  
     sb.append("]");  
     Log.d(TAG, sb.toString());  
   }  
   /** Determine the space between the first two fingers */  
   private float spacing(WrapMotionEvent event)  
   {  
     float x = event.getX(0) - event.getX(1);  
     float y = event.getY(0) - event.getY(1);  
     return FloatMath.sqrt(x * x + y * y);  
   }  
   /** Calculate the mid point of the first two fingers */  
   private void midPoint(PointF point, WrapMotionEvent event)  
   {  
     float x = event.getX(0) + event.getX(1);  
     float y = event.getY(0) + event.getY(1);  
     point.set(x / 2, y / 2);  
   }  
 }  

Motion class to help in touch event


 package com.ahmad;  
 import android.view.MotionEvent;  
 public class EclairMotionEvent extends WrapMotionEvent {  
   protected EclairMotionEvent(MotionEvent event) {  
       super(event);  
   }  
   public float getX(int pointerIndex) {  
       return event.getX(pointerIndex);  
   }  
   public float getY(int pointerIndex) {  
       return event.getY(pointerIndex);  
   }  
   public int getPointerCount() {  
       return event.getPointerCount();  
   }  
   public int getPointerId(int pointerIndex) {  
       return event.getPointerId(pointerIndex);  
   }  
 }  

Class to keep information about Pointer ID

 package com.ahmad;  
 import android.view.MotionEvent;  
 public class WrapMotionEvent {  
  protected MotionEvent event;  
  protected WrapMotionEvent(MotionEvent event) {  
  this.event = event;  
  }  
    static public WrapMotionEvent wrap(MotionEvent event) {  
  try {  
   return new EclairMotionEvent(event);  
  } catch (VerifyError e) {  
   return new WrapMotionEvent(event);  
  }  
  }  
  public int getAction() {  
  return event.getAction();  
  }  
  public float getX() {  
  return event.getX();  
  }  
  public float getX(int pointerIndex) {  
  verifyPointerIndex(pointerIndex);  
  return getX();  
  }  
  public float getY() {  
  return event.getY();  
  }  
  public float getY(int pointerIndex) {  
  verifyPointerIndex(pointerIndex);  
  return getY();  
  }  
  public int getPointerCount() {  
  return 1;  
  }  
  public int getPointerId(int pointerIndex) {  
  verifyPointerIndex(pointerIndex);  
  return 0;  
  }  
  private void verifyPointerIndex(int pointerIndex) {  
  if (pointerIndex > 0) {  
   throw new IllegalArgumentException(  
    "Invalid pointer index for Donut/Cupcake");  
  }  
  }  
 }  

Notable thing is that this pinch zoom will only work above Android 2.0. Lower version does not support multiple finger

Android News and source code