Monday, March 21, 2011

Pinch Zoom to view completely

Hope you have gone through my previous post Zooming a view completely 
Now to apply pinch zoom we need to override the OnTouchListener.
There are many blogs and code to do pinch zoom in image view me used that logic here
So my main activity class will be like this

public class PinchZoomLayout extends Activity {
 View mainView = null;

 // Remember some things for zooming
 PointF start = new PointF();
 PointF mid = new PointF();

 float oldDist = 1f;
 PointF oldDistPoint = new PointF();

 public static String TAG = "ZOOM";

 static final int NONE = 0;
 static final int DRAG = 1;
 static final int ZOOM = 2;
 int mode = NONE;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  mainView = (LinearLayout) findViewById(R.id.linearLayout);

  Button buttonZoomOut = (Button) findViewById(R.id.buttonZoomOut);
  Button buttonNormal = (Button) findViewById(R.id.buttonNormal);
  Button buttonZoomIn = (Button) findViewById(R.id.buttonZoomIn);

  buttonZoomOut.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    zoom(0.5f, 0.5f, new PointF(0, 0));
   }
  });
  buttonNormal.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    zoom(1f, 1f, new PointF(0, 0));
   }
  });
  buttonZoomIn.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    zoom(2f, 2f, new PointF(0, 0));
   }
  });

  mainView.setOnTouchListener(new OnTouchListener() {
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    Log.d(TAG, "mode=DRAG");
    switch (event.getAction() & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN:
     start.set(event.getX(), event.getY());
     Log.d(TAG, "mode=DRAG");
     mode = DRAG;

     break;
    case MotionEvent.ACTION_POINTER_DOWN:
     oldDist = spacing(event);
     oldDistPoint = spacingPoint(event);
     Log.d(TAG, "oldDist=" + oldDist);
     if (oldDist > 10f) {
      midPoint(mid, event);
      mode = ZOOM;
      Log.d(TAG, "mode=ZOOM");
     }
     System.out.println("current time :" + System.currentTimeMillis());
     break;// return !gestureDetector.onTouchEvent(event);
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_POINTER_UP:
     Log.d(TAG, "mode=NONE");
     mode = NONE;
     break;
    case MotionEvent.ACTION_MOVE:
     if (mode == DRAG) {

     } else if (mode == ZOOM) {
      PointF newDist = spacingPoint(event);
      float newD = spacing(event);
      Log.e(TAG, "newDist=" + newDist);
      float[] old = new float[9];
      float[] newm = new float[9];
      Log.e(TAG, "x=" + old[0] + ":&:" + old[2]);
      Log.e(TAG, "y=" + old[4] + ":&:" + old[5]);
      float scale = newD / oldDist;
      float scalex = newDist.x / oldDistPoint.x;
      float scaley = newDist.y / oldDistPoint.y;
      zoom(scale, scale, start);
     }
     break;
    }
    return true;
   }
  });
 }

 /** 
  * zooming is done from here 
  */
 public void zoom(Float scaleX, Float scaleY, PointF pivot) {
  mainView.setPivotX(pivot.x);
  mainView.setPivotY(pivot.y);
  mainView.setScaleX(scaleX);
  mainView.setScaleY(scaleY);
 }

 /**
  * space between the first two fingers
  */
 private float spacing(MotionEvent event) {
  // ...
  float x = event.getX(0) - event.getX(1);
  float y = event.getY(0) - event.getY(1);
  return FloatMath.sqrt(x * x + y * y);
 }

 private PointF spacingPoint(MotionEvent event) {
  PointF f = new PointF();
  f.x = event.getX(0) - event.getX(1);
  f.y = event.getY(0) - event.getY(1);
  return f;
 }

 /**
  * the mid point of the first two fingers
  */
 private void midPoint(PointF point, MotionEvent event) {
  // ...
  float x = event.getX(0) + event.getX(1);
  float y = event.getY(0) + event.getY(1);
  point.set(x / 2, y / 2);
 }
}
 
But this zoom is not much smooth :(.

17 comments:

  1. mainView.setPivotX(pivot.x);
    mainView.setPivotY(pivot.y);
    mainView.setScaleX(scaleX);
    mainView.setScaleY(scaleY);

    The method setPivotX(float) is undefined for the type View

    this problem i get may i know what is the solution for this

    ReplyDelete
  2. In which API level are your working?
    And this all are available only from API Level 11

    ReplyDelete
  3. is there any alternative code to work in API lower versions

    ReplyDelete
  4. To my very best knowledge, no direct method for lower API and I don't know any code to do so :(

    ReplyDelete
  5. I an new to android. So I have One Question related to above code i.e.Why you use TouchListener() method in this code because code works fine without using touchEvent() .

    ReplyDelete
  6. Hi can this be used to pinch zoom in some view which has text boxes and buttons?

    ReplyDelete
  7. Too good document, thanks a lot buddy.

    ReplyDelete
  8. not working below API level 11. So is there anyway to do on API level 8,9,10

    ReplyDelete
  9. While Zooming the views get flicker. zoom is not so smooth as you have mentioned. Tell us what we can do to acheive the smootheness and avoid flicking

    ReplyDelete
  10. can u share completely zip source? i need it bro.. Thanks!

    ReplyDelete
  11. Flickering while zooming. If you have the solution please post it as well. Thanks

    ReplyDelete
  12. Hi Habeeb,

    The above tutorial was very helpful. Buy i have an urgent requirement to drag the zoomed view.. So what should we write in if(mode == DRAG) {} .. please help me out with the code..

    ThankYou.

    ReplyDelete
  13. can i get the source code for the whole Project please ! :)

    ReplyDelete
  14. It's not a feasible solution because image display is flickering while zooming.

    Rather I would suggest to define a custom layout and listen to the zoom/touch through ScaleGestureDetector.

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. The zoom is not smooth because the view you are scaling is the same view the ontouchlistener is set on (so when the scale increases, the touch is triggered again, causing a jitter), set the touch listener 1 parent higher and it will be smooth. In case of this code you can replace this part:

    mainView.setOnTouchListener(new OnTouchListener() {

    with:

    View rootView = findViewById(android.R.id.content);
    rootView.setOnTouchListener(new OnTouchListener() {

    which will have the content view root handle as the listener, scaling it's mainView

    Enjoy

    ReplyDelete
  17. Thanks for the example. It works fine in all layouts but when using in scroll view. We can't scroll

    ReplyDelete