Android可自由缩放的ImageView 发表于 2016-08-03 | 分类于 Android 支持双击缩放和双指缩放 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.ScaleGestureDetector.OnScaleGestureListener;import android.view.View;import android.view.View.OnTouchListener;import android.view.ViewConfiguration;import android.view.ViewTreeObserver.OnGlobalLayoutListener;import android.widget.ImageView;public class ZoomImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener { private boolean mOnce; /** * 初始化时缩放的值 */ private float mInitScale; /** * 双击放大时的到达的值 */ private float mMidScale; /** * 放大的最大值 */ private float mMaxScale; private Matrix mScaleMatrix; /** * 捕获用户多点触控时缩放的比例 */ private ScaleGestureDetector mScaleGestureDetector; // --------自由移动 /** * 记录上一次多点触控的数量 */ private int mLastPointerCount; private float mLastX; private float mLastY; private int mTouchSlop; private boolean isCanDrag; private boolean isCheckLeftAndRight; private boolean isCheckTopAndBottom; private GestureDetector mGestureDetector; private boolean isAutoScale; public ZoomImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mScaleMatrix = new Matrix(); setScaleType(ScaleType.MATRIX); mScaleGestureDetector = new ScaleGestureDetector(context, this); setOnTouchListener(this); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { if (isAutoScale) { return true; } float x = e.getX(); float y = e.getY(); if (getScale() < mMidScale) { // mScaleMatrix.postScale(mMidScale / getScale(), // mMidScale / getScale(), x, y); // setImageMatrix(mScaleMatrix); postDelayed(new AutoScaleRunnable(mMidScale, x, y), 16); isAutoScale = true; } else { // mScaleMatrix.postScale(mInitScale / getScale(), // mInitScale / getScale(), x, y); // setImageMatrix(mScaleMatrix); postDelayed( new AutoScaleRunnable(mInitScale, x, y), 16); isAutoScale = true; } return super.onDoubleTap(e); } }); } private class AutoScaleRunnable implements Runnable { /** * 缩放目标值 */ private float mTargetScale; /** * 缩放的中心点 */ private float x; private float y; private float tmpScale; private final float BIGGER = 1.07f; private final float SMALLER = 0.93f; public AutoScaleRunnable(float mTargetScale, float x, float y) { super(); this.mTargetScale = mTargetScale; this.x = x; this.y = y; if (getScale() < mTargetScale) { tmpScale = BIGGER; } if (getScale() > mTargetScale) { tmpScale = SMALLER; } } @Override public void run() { mScaleMatrix.postScale(tmpScale, tmpScale, x, y); CheckBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); float currentScale = getScale(); if ((tmpScale > 1.0f && currentScale < mTargetScale) || (tmpScale < 1.0f && currentScale > mTargetScale)) { postDelayed(this, 16); } else { // 设置为我们的目标值 float scale = mTargetScale / currentScale; mScaleMatrix.postScale(scale, scale, x, y); CheckBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); isAutoScale = false; } } } public ZoomImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ZoomImageView(Context context) { this(context, null); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this); } @SuppressWarnings("deprecation") @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeGlobalOnLayoutListener(this); } /** * 获取ImageView获取加载完成的图片 */ @Override public void onGlobalLayout() { if (!mOnce) { // 得到控件的宽和高 int width = getWidth(); int height = getHeight(); // 得到我们的图片、以及宽和高 Drawable d = getDrawable(); if (d == null) { return; } int dw = d.getIntrinsicWidth(); int dh = d.getIntrinsicHeight(); float scale = 1.0f; /** * 如果图片的宽度大于控件的宽度,高度小于控件的高度,将其缩小 */ if (dw > width && dh < height) { scale = width * 1.0f / dw; } /** * 如果图片的宽度小于控件的宽度,高度大于控件的高度,将其缩小 */ if (dh > height && dw < width) { scale = height * 1.0f / dh; } if ((dw > width && dh > height) || (dw < width && dh < height)) { scale = Math.min(width * 1.0f / dw, height * 1.0f / dh); } /** * 得到初始化的缩放比例 */ mInitScale = scale; mMaxScale = mInitScale * 4; mMidScale = mInitScale * 2; // 将图片移动至控件的中心 int dx = getWidth() / 2 - dw / 2; int dy = getHeight() / 2 - dh / 2; mScaleMatrix.postTranslate(dx, dy); mScaleMatrix.postScale(mInitScale, mInitScale, width / 2, height / 2); setImageMatrix(mScaleMatrix); mOnce = true; } } // 缩放区间:initScale maxScale @Override public boolean onScale(ScaleGestureDetector detector) { float scale = getScale(); float scaleFactor = detector.getScaleFactor(); if (getDrawable() == null) return true; // 缩放范围的控制 if ((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)) { if (scale * scaleFactor < mInitScale) { scaleFactor = mInitScale / scale; } if (scale * scaleFactor > mMaxScale) { scale = mMaxScale / scale; } // 缩放 mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); CheckBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); } return true; } /** * 获得图片放大缩小以后的宽和高,以及l,r,t,b * * @return */ private RectF getMatrixRectF() { Matrix matrix = mScaleMatrix; RectF rectF = new RectF(); Drawable d = getDrawable(); if (d != null) { rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rectF); } return rectF; } /** * 在缩放的时候进行边界控制以及位置的控制 */ private void CheckBorderAndCenterWhenScale() { RectF rect = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); // 缩放时进行边界检测,防止出现白边 if (rect.width() >= width) { if (rect.left > 0) { deltaX = -rect.left; } if (rect.right < width) { deltaX = width - rect.right; } } if (rect.height() >= height) { if (rect.top > 0) { deltaY = -rect.top; } if (rect.bottom < height) { deltaY = height - rect.bottom; } } // 如果图片宽度或者高度小于控件的宽或者高,则让他居中 if (rect.width() < width) { deltaX = width / 2 - rect.right + rect.width() / 2; } if (rect.height() < height) { deltaY = height / 2 - rect.bottom + rect.height() / 2; } mScaleMatrix.postTranslate(deltaX, deltaY); } /** * 获取当前图片的缩放的值 * * @return */ public float getScale() { float[] values = new float[9]; mScaleMatrix.getValues(values); return values[Matrix.MSCALE_X]; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } @Override public boolean onTouch(View v, MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) { return true; } mScaleGestureDetector.onTouchEvent(event); float x = 0; float y = 0; int pointerCount = event.getPointerCount(); for (int i = 0; i < pointerCount; i++) { x += event.getX(i); y += event.getY(i); } x /= pointerCount; y /= pointerCount; if (mLastPointerCount != pointerCount) { isCanDrag = false; mLastX = x; mLastY = y; } mLastPointerCount = pointerCount; RectF rectF = getMatrixRectF(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (rectF.width() > getWidth() + 0.01 || rectF.height() > getHeight() + 0.01) { if (getParent() instanceof ViewPager) { //事件冲突处理 getParent().requestDisallowInterceptTouchEvent(true); } } break; case MotionEvent.ACTION_MOVE: if (rectF.width() > getWidth() + 0.01 || rectF.height() > getHeight() + 0.01) { if (getParent() instanceof ViewPager) { //事件冲突处理 getParent().requestDisallowInterceptTouchEvent(true); } } float dx = x - mLastX; float dy = y - mLastY; if (!isCanDrag) { isCanDrag = isMoveAction(dx, dy); } if (isCanDrag) { rectF = getMatrixRectF(); if (getDrawable() != null) { isCheckLeftAndRight = isCheckTopAndBottom = true; // 如果宽度小于控件宽度,不允许横向移动 if (rectF.width() < getWidth()) { isCheckLeftAndRight = false; dx = 0; } // 如果高度小于控件高度,不允许纵向移动 if (rectF.height() < getHeight()) { isCheckTopAndBottom = false; dy = 0; } mScaleMatrix.postTranslate(dx, dy); CheckBorderAndCenterWhenTrans(); setImageMatrix(mScaleMatrix); } } mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mLastPointerCount = 0; break; default: break; } return true; } /** * 当移动时进行边界检查 */ private void CheckBorderAndCenterWhenTrans() { RectF rectF = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); if (rectF.top > 0 && isCheckTopAndBottom) { deltaY = -rectF.top; } if (rectF.bottom < height && isCheckTopAndBottom) { deltaY = height - rectF.bottom; } if (rectF.left > 0 && isCheckLeftAndRight) { deltaX = -rectF.left; } if (rectF.right < width && isCheckLeftAndRight) { deltaX = width - rectF.right; } mScaleMatrix.postTranslate(deltaX, deltaY); } private boolean isMoveAction(float dx, float dy) { return Math.sqrt(dx * dx + dy * dy) > mTouchSlop; }}