package wb.android.ui; 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.widget.ImageView; //Adapted from: https://github.com/MikeOrtiz/TouchImageView/blob/master/src/com/example/touch/TouchImageView.java public class PinchToZoomImageView extends ImageView { private static final float DEFAULT_MIN_SCALE = 1f; private static final float DEFAULT_MAX_SCALE = 2.5f; private static final int CLICK = 3; private final ScaleGestureDetector mScaleDetector; private final Matrix mMatrix; private final PointF mLast, mStart; private final float[] mValues; private float mMinScale, mMaxScale, mOrigWidth, mOrigHeight, mSaveScale; private int mViewWidth, mViewHeight, mOldMeasuredHeight; private Mode mMode; private enum Mode { NONE, DRAG, ZOOM, } public PinchToZoomImageView(Context context) { super(context); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); mMatrix = new Matrix(); mStart = new PointF(); mLast = new PointF(); mValues = new float[9]; mMinScale = DEFAULT_MIN_SCALE; mMaxScale = DEFAULT_MAX_SCALE; mSaveScale = 1f; setImageMatrix(mMatrix); setScaleType(ImageView.ScaleType.MATRIX); } public PinchToZoomImageView(Context context, AttributeSet attrs) { super(context, attrs); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); mMatrix = new Matrix(); mStart = new PointF(); mLast = new PointF(); mValues = new float[9]; mMinScale = DEFAULT_MIN_SCALE; mMaxScale = DEFAULT_MAX_SCALE; mSaveScale = 1f; setImageMatrix(mMatrix); setScaleType(ImageView.ScaleType.MATRIX); } public PinchToZoomImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); mMatrix = new Matrix(); mStart = new PointF(); mLast = new PointF(); mValues = new float[9]; mMinScale = DEFAULT_MIN_SCALE; mMaxScale = DEFAULT_MAX_SCALE; mSaveScale = 1f; setImageMatrix(mMatrix); setScaleType(ImageView.ScaleType.MATRIX); } @Override public boolean onTouchEvent(MotionEvent event) { setScaleType(ImageView.ScaleType.MATRIX); mScaleDetector.onTouchEvent(event); PointF curr = new PointF(event.getX(), event.getY()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLast.set(curr); mStart.set(mLast); mMode = Mode.DRAG; break; case MotionEvent.ACTION_MOVE: if (mMode == Mode.DRAG) { float deltaX = curr.x - mLast.x; float deltaY = curr.y - mLast.y; float fixTransX = getFixDragTrans(deltaX, mViewWidth, mOrigWidth * mSaveScale); float fixTransY = getFixDragTrans(deltaY, mViewHeight, mOrigHeight * mSaveScale); mMatrix.postTranslate(fixTransX, fixTransY); fixTrans(); mLast.set(curr.x, curr.y); } break; case MotionEvent.ACTION_UP: mMode = Mode.NONE; int xDiff = (int) Math.abs(curr.x - mStart.x); int yDiff = (int) Math.abs(curr.y - mStart.y); if (xDiff < CLICK && yDiff < CLICK) performClick(); break; case MotionEvent.ACTION_POINTER_UP: mMode = Mode.NONE; break; default: return false; } setImageMatrix(mMatrix); return true; } public void setMaximumZoomScale(float scale) { mMaxScale = scale; } public void setMinimumZoomScale(float scale) { mMinScale = scale; } private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { @Override public boolean onScaleBegin(ScaleGestureDetector detector) { mMode = Mode.ZOOM; return true; } @Override public boolean onScale(ScaleGestureDetector detector) { float mScaleFactor = detector.getScaleFactor(); float origScale = mSaveScale; mSaveScale *= mScaleFactor; if (mSaveScale > mMaxScale) { mSaveScale = mMaxScale; mScaleFactor = mMaxScale / origScale; } else if (mSaveScale < mMinScale) { mSaveScale = mMinScale; mScaleFactor = mMinScale / 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; } } private void fixTrans() { mMatrix.getValues(mValues); float transX = mValues[Matrix.MTRANS_X]; float transY = mValues[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); } private 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; } private 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 (mOldMeasuredHeight == mViewWidth && mOldMeasuredHeight == mViewHeight || mViewWidth == 0 || mViewHeight == 0) return; mOldMeasuredHeight = mViewHeight; if (mSaveScale == 1) { //Fit to screen. float scale; Drawable drawable = getDrawable(); if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) return; int bmWidth = drawable.getIntrinsicWidth(); int bmHeight = drawable.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(); } }