Here is best code for Multi-Touch Pinch Zoom on ImagView.
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;
@SuppressLint("ClickableViewAccessibility")
public class PinchTouchImageView extends ImageView {
Matrix mMatrix;
// 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 mPointLast = new PointF();
PointF mPointStatr = new PointF();
float minScale = 1f;
float maxScale = 3f;
float[] mat;
int mViewWidth, mViewHeight;
static final int CLICK = 3;
float mSaveScale = 1f;
protected float mOrigWidth, mOrigHeight;
int oldMeasuredWidth, oldMeasuredHeight;
ScaleGestureDetector mScaleDetector;
Context mContext;
public PinchTouchImageView(Context context) {
super(context);
sharedConstructing(context);
}
public PinchTouchImageView(Context context, AttributeSet attrs) {
super(context, attrs);
sharedConstructing(context);
}
private void sharedConstructing(Context context) {
super.setClickable(true);
this.mContext = context;
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
mMatrix = new Matrix();
mat = new float[9];
setImageMatrix(mMatrix);
setScaleType(ScaleType.MATRIX);
setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
PointF mCurrentPoint = new PointF(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mPointLast.set(mCurrentPoint);
mPointStatr.set(mPointLast);
MODE = DRAG;
break;
case MotionEvent.ACTION_MOVE:
if (MODE == DRAG) {
float deltaX = mCurrentPoint.x - mPointLast.x;
float deltaY = mCurrentPoint.y - mPointLast.y;
float fixTransX = getFixDragTrans(deltaX, mViewWidth, mOrigWidth * mSaveScale);
float fixTransY = getFixDragTrans(deltaY, mViewHeight, mOrigHeight * mSaveScale);
mMatrix.postTranslate(fixTransX, fixTransY);
fixTrans();
mPointLast.set(mCurrentPoint.x, mCurrentPoint.y);
}
break;
case MotionEvent.ACTION_UP:
MODE = NONE;
int xDiff = (int) Math.abs(mCurrentPoint.x - mPointStatr.x);
int yDiff = (int) Math.abs(mCurrentPoint.y - mPointStatr.y);
if (xDiff < CLICK && yDiff < CLICK)
performClick();
break;
case MotionEvent.ACTION_POINTER_UP:
MODE = NONE;
break;
}
setImageMatrix(mMatrix);
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 = mSaveScale;
mSaveScale *= mScaleFactor;
if (mSaveScale > maxScale) {
mSaveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (mSaveScale < minScale) {
mSaveScale = minScale;
mScaleFactor = minScale / origScale;
}
if (mOrigWidth * mSaveScale <= mViewWidth || mOrigHeight * mSaveScale <= mViewHeight)
mMatrix.postScale(mScaleFactor, mScaleFactor, mViewWidth / 2, mViewHeight / 2);
else
mMatrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
fixTrans();
return true;
}
}
void fixTrans() {
mMatrix.getValues(mat);
float transX = mat[Matrix.MTRANS_X];
float transY = mat[Matrix.MTRANS_Y];
float fixTransX = getFixTrans(transX, mViewWidth, mOrigWidth * mSaveScale);
float fixTransY = getFixTrans(transY, mViewHeight, mOrigHeight * mSaveScale);
if (fixTransX != 0 || fixTransY != 0)
mMatrix.postTranslate(fixTransX, fixTransY);
}
float getFixTrans(float trans, float viewSize, float contentSize) {
float minTrans, maxTrans;
if (contentSize <= viewSize) {
minTrans = 0;
maxTrans = viewSize - contentSize;
} else {
minTrans = viewSize - contentSize;
maxTrans = 0;
}
if (trans < minTrans)
return -trans + minTrans;
if (trans > maxTrans)
return -trans + maxTrans;
return 0;
}
float getFixDragTrans(float delta, float viewSize, float contentSize) {
if (contentSize <= viewSize) {
return 0;
}
return delta;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mViewWidth = MeasureSpec.getSize(widthMeasureSpec);
mViewHeight = MeasureSpec.getSize(heightMeasureSpec);
if (oldMeasuredHeight == mViewWidth && oldMeasuredHeight == mViewHeight || mViewWidth == 0 || mViewHeight == 0)
return;
oldMeasuredHeight = mViewHeight;
oldMeasuredWidth = mViewWidth;
if (mSaveScale == 1) {
// Fit to screen.
float scale;
Drawable mDrawable = getDrawable();
if (mDrawable == null || mDrawable.getIntrinsicWidth() == 0 || mDrawable.getIntrinsicHeight() == 0)
return;
int bmWidth = mDrawable.getIntrinsicWidth();
int bmHeight = mDrawable.getIntrinsicHeight();
float scaleX = (float) mViewWidth / (float) bmWidth;
float scaleY = (float) mViewHeight / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
mMatrix.setScale(scale, scale);
// Center the image
float redundantYSpace = (float) mViewHeight - (scale * (float) bmHeight);
float redundantXSpace = (float) mViewWidth - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
mMatrix.postTranslate(redundantXSpace, redundantYSpace);
mOrigWidth = mViewWidth - 2 * redundantXSpace;
mOrigHeight = mViewHeight - 2 * redundantYSpace;
setImageMatrix(mMatrix);
}
fixTrans();
}
}