支持多點觸控,放大自由移動,雙擊能夠放大縮小.直接上代碼:html
package com.cbt.view; import android.content.Context; import android.graphics.Matrix; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; 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; /** * Created by caobotao on 15/12/10. */ public class ZoomImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener { private boolean mOnce; //初始化時所發的比例 private float mInitScale; //雙擊後放大的比例 private float mMidScale; //能夠放大的最大比例 private float mMaxScale; private Matrix mMatrix; //經過ScaleGestureDetector能夠獲取到多點觸控的縮放比例 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) { this(context,null); } public ZoomImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); Log.i("ZoomImageView構造方法","ZoomImageView構造方法"); mMatrix = new Matrix(); super.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) { // mMatrix.postScale(mMidScale / getScale(),mMidScale / getScale(),x,y); // setImageMatrix(mMatrix); postDelayed(new AutoScaleRunnable(mMidScale,x,y),16); } else { // mMatrix.postScale(mInitScale / getScale(),mInitScale / getScale(),x,y); // setImageMatrix(mMatrix); postDelayed(new AutoScaleRunnable(mInitScale,getWidth()/2,getHeight()/2),16); } isAutoScale = true; return true; } }); } //當此view附加到窗體上時調用該方法 @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); //添加全局佈局監聽 getViewTreeObserver().addOnGlobalLayoutListener(this); } //當此view從窗體上消除時調用該方法 @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); //移除全局佈局監聽 getViewTreeObserver().removeOnGlobalLayoutListener(this); } /** * 獲取ImageView加載完成的圖片 */ @Override public void onGlobalLayout() { //因爲onGlobalLayout可能會被調用屢次,咱們使用一個標誌mOnce來判斷是否已經調用 if (!mOnce) { //得到此View的寬和高 float width = getWidth(); float height = getHeight(); Log.i("onGlobalLayout Width",width+""); Log.i("onGlobalLayout height",height+""); //獲得圖片及其寬高 Drawable d = getDrawable(); if (d == null) { return; } float dw = d.getIntrinsicWidth(); float dh = d.getIntrinsicHeight(); Log.i("onGlobalLayout dw",dw+""); Log.i("onGlobalLayout dh",dh+""); /** * 比較圖片的尺寸與此View的尺寸,若是圖片的尺寸比此View的尺寸大, * 則縮放,反之,則放大,以達到與此View尺寸一致 */ //縮放比例 float scale = 1.0f; //若是圖片寬度比此View寬度大,且高度比此View小,則以寬度的比例縮小 if (dw > width && dh < height) { scale = width / dw; } //若是圖片寬度比此View寬度小,且高度比此View大,則以高度的比例縮小 if (dw < width && dh > height) { scale = height / dh; } //若是圖片寬度比此View寬度大,且高度比此View大,則以高度的比例與寬度的比例中大的一者縮小 if ( (dw > width && dh > height) || (dw < width && dh < height) ) { scale = Math.max(width / dw,height/ dh); } //若是圖片寬度比此View寬度小,且高度比此View小,則以高度的比例與寬度的比例中小的一者放大 if (dw < width && dh < height) { scale = Math.min(width / dw,height / dh); } //分別設置初始化時的比例,雙擊後的比例,能夠放大的最大比例 mInitScale = scale; mMidScale = scale * 2; mMaxScale = scale * 4; //將圖片移動到此View的中心 float dx = width / 2 - dw / 2;//須要移動的x方向的距離 float dy = height / 2 - dh / 2;//須要移動的y方向的距離 //設置平移 mMatrix.postTranslate(dx,dy); //設置縮放 mMatrix.postScale(mInitScale,mInitScale,width / 2,height / 2 ); setImageMatrix(mMatrix); mOnce = true; } } //獲取當前圖片的縮放比例 public float getScale(){ float[] values = new float[9]; mMatrix.getValues(values); return values[Matrix.MSCALE_X]; } /** * 縮放區間:[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) { scaleFactor = mMaxScale / scale; } //縮放 mMatrix.postScale(scaleFactor,scaleFactor,detector.getFocusX(),detector.getFocusY()); //在縮放的時候進行邊界以及位置的控制 checkBorderAndCenterWhenScale(); setImageMatrix(mMatrix); } return true; } //在縮放的時候進行邊界以及位置的控制 private void checkBorderAndCenterWhenScale() { RectF rectf = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); //縮放時進行邊界檢測,防止出現留白 if (rectf.width() >= width) { if (rectf.left > 0) { deltaX = -rectf.left; } if (rectf.right < width) { deltaX = width - rectf.right; } } if (rectf.height() >= height) { if (rectf.top > 0) { deltaY = -rectf.top; } if (rectf.bottom < height) { deltaY = height - rectf.bottom; } } //若是寬度或者高度小於控件的寬度或高度,則讓其居中 if (rectf.width() < width) { deltaX = width / 2f - rectf.right + rectf.width() / 2f; } if (rectf.height() < height) { deltaY = height / 2f - rectf.bottom + rectf.height() / 2f; } mMatrix.postTranslate(deltaX,deltaY); } //得到圖片縮放後的寬高,以及top,bottom,left,right private RectF getMatrixRectF(){ Matrix matrix = mMatrix; RectF rectf = new RectF(); Drawable d = getDrawable(); if (d != null) { rectf.set(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight()); matrix.mapRect(rectf); } return rectf; } @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(); //累加x和y方向的距離 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: /** * 此View在ViewPager中使用時,圖片放大後自由移動的事件會與 * ViewPager的左右切換的事件發生衝突,致使圖片放大後若是左右 * 移動時不能自由移動圖片,而是使ViewPager切換圖片.這是因爲事 * 件分發時外層的優先級比內層的高,使用下列判斷能夠解決 */ if (rectF.width() > getWidth() || rectF.height() > getHeight()) { getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_MOVE: if (rectF.width() > getWidth() || rectF.height() > getHeight()) { getParent().requestDisallowInterceptTouchEvent(true); } //偏移量 float dx = x - mLastX; float dy = y - mLastY; if (!isCanDrag){ isCanDrag = isMoveAction(dx,dy); } if (isCanDrag) { if (getDrawable() != null) { isCheckLeftAndRight = true; isCheckTopAndBottom = true; //若是寬度小於控件的寬度,不容許橫向移動 if (rectF.width() < getWidth()) { isCheckLeftAndRight = false; dx = 0; } //若是高度小於控件的高度,不容許縱向移動 if (rectF.height() < getHeight()) { isCheckTopAndBottom = false; dy = 0; } mMatrix.postTranslate(dx,dy); //當自由移動時進行邊界檢查,防止留白 checkBorderWhenTranslate(); setImageMatrix(mMatrix); } } mLastX = x; mLastY = y; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mLastPointerCount = 0; break; } return true; } //當自由移動時進行邊界檢查,防止留白 private void checkBorderWhenTranslate() { 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; } mMatrix.postTranslate(deltaX,deltaY); } //判斷是否足以觸發MOVE事件 private boolean isMoveAction(float dx, float dy) { return Math.sqrt(dx * dx + dy * dy) > MTouchSlop; } //實現緩慢縮放 private class AutoScaleRunnable implements Runnable{ //縮放的目標比例 private float mTargetScale; //縮放的中心點 private float x; private float y; private final float BIGGER = 1.07f; private final float SMALLER = 0.93f; //臨時縮放比例 private float tempScale; public AutoScaleRunnable(float mTargetScale,float x,float y) { this.mTargetScale = mTargetScale; this.x = x; this.y = y; if (getScale() < mTargetScale) { tempScale = BIGGER; } if (getScale() > mTargetScale) { tempScale = SMALLER; } } @Override public void run() { //進行縮放 mMatrix.postScale(tempScale,tempScale,x,y); checkBorderAndCenterWhenScale(); setImageMatrix(mMatrix); float currentScale = getScale(); //若是能夠放大或者縮小 if ((tempScale > 1.0f && currentScale < mTargetScale) || (tempScale < 1.0f && currentScale > mTargetScale) ){ postDelayed(this,16); } //設置爲目標縮放比例 else { float scale = mTargetScale / currentScale; mMatrix.postScale(scale,scale,x,y); checkBorderAndCenterWhenScale(); setImageMatrix(mMatrix); isAutoScale = false; } } } }
做者:caobotaoandroid
出處:http://www.cnblogs.com/caobotao/p/5041737.htmlide
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接,不然保留追究法律責任的權利佈局