package com.example.tpi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.PointF; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; /** * @ClassName: MatrixImageView * @Description: 帶放大、縮小、移動效果的ImageView * @author LinJ * @date 2015-1-7 上午11:15:07 * */ public class MatrixImageView extends ImageView { private final static String TAG = "MatrixImageView"; private GestureDetector mGestureDetector; /** 模板Matrix,用以初始化 */ private Matrix mMatrix = new Matrix(); /** 圖片長度 */ private float mImageWidth; /** 圖片高度 */ private float mImageHeight; public MatrixImageView(Context context, AttributeSet attrs) { super(context, attrs); MatrixTouchListener mListener = new MatrixTouchListener(); setOnTouchListener(mListener); mGestureDetector = new GestureDetector(getContext(), new GestureListener(mListener)); // 背景設置爲balck setBackgroundColor(Color.BLACK); // 將縮放類型設置爲FIT_CENTER,表示把圖片按比例擴大/縮小到View的寬度,居中顯示 setScaleType(ScaleType.FIT_CENTER); } @Override public void setImageBitmap(Bitmap bm) { // TODO Auto-generated method stub super.setImageBitmap(bm); // 設置完圖片後,獲取該圖片的座標變換矩陣 mMatrix.set(getImageMatrix()); float[] values = new float[9]; mMatrix.getValues(values); // 圖片寬度爲屏幕寬度除縮放倍數 mImageWidth = getWidth() / values[Matrix.MSCALE_X]; mImageHeight = (getHeight() - values[Matrix.MTRANS_Y] * 2) / values[Matrix.MSCALE_Y]; } public class MatrixTouchListener implements OnTouchListener { /** 拖拉照片模式 */ private static final int MODE_DRAG = 1; /** 放大縮小照片模式 */ private static final int MODE_ZOOM = 2; /** 不支持Matrix */ private static final int MODE_UNABLE = 3; /** 最大縮放級別 */ float mMaxScale = 6; /** 雙擊時的縮放級別 */ float mDobleClickScale = 2; private int mMode = 0;// /** 縮放開始時的手指間距 */ private float mStartDis; /** 當前Matrix */ private Matrix mCurrentMatrix = new Matrix(); /** 用於記錄開始時候的座標位置 */ private PointF startPoint = new PointF(); @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: // 設置拖動模式 mMode = MODE_DRAG; //startPoint.set(event.getX(), event.getY()); isMatrixEnable(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: reSetMatrix(); break; case MotionEvent.ACTION_MOVE: if (mMode == MODE_ZOOM) { setZoomMatrix(event); } else if (mMode == MODE_DRAG) { setDragMatrix(event); } break; case MotionEvent.ACTION_POINTER_DOWN: if (mMode == MODE_UNABLE) return true; mMode = MODE_ZOOM; mStartDis = distance(event); break; default: break; } return mGestureDetector.onTouchEvent(event); } public void setDragMatrix(MotionEvent event) { if (isZoomChanged()) { float dx = event.getX() - startPoint.x; // 獲得x軸的移動距離 float dy = event.getY() - startPoint.y; // 獲得x軸的移動距離 // 避免和雙擊衝突,大於10f纔算是拖動 if (Math.sqrt(dx * dx + dy * dy) > 10f) { startPoint.set(event.getX(), event.getY()); // 在當前基礎上移動 mCurrentMatrix.set(getImageMatrix()); float[] values = new float[9]; mCurrentMatrix.getValues(values); dx = checkDxBound(values, dx); dy = checkDyBound(values, dy); mCurrentMatrix.postTranslate(dx, dy); setImageMatrix(mCurrentMatrix); } } } /** * 判斷縮放級別是不是改變過 * * @return true表示非初始值,false表示初始值 */ private boolean isZoomChanged() { float[] values = new float[9]; getImageMatrix().getValues(values); // 獲取當前X軸縮放級別 float scale = values[Matrix.MSCALE_X]; // 獲取模板的X軸縮放級別,二者作比較 mMatrix.getValues(values); return scale != values[Matrix.MSCALE_X]; } /** * 和當前矩陣對比,檢驗dy,使圖像移動後不會超出ImageView邊界 * * @param values * @param dy * @return */ private float checkDyBound(float[] values, float dy) { float height = getHeight(); if (mImageHeight * values[Matrix.MSCALE_Y] < height) return 0; if (values[Matrix.MTRANS_Y] + dy > 0) dy = -values[Matrix.MTRANS_Y]; else if (values[Matrix.MTRANS_Y] + dy < -(mImageHeight * values[Matrix.MSCALE_Y] - height)) dy = -(mImageHeight * values[Matrix.MSCALE_Y] - height) - values[Matrix.MTRANS_Y]; return dy; } /** * 和當前矩陣對比,檢驗dx,使圖像移動後不會超出ImageView邊界 * * @param values * @param dx * @return */ private float checkDxBound(float[] values, float dx) { float width = getWidth(); if (mImageWidth * values[Matrix.MSCALE_X] < width) return 0; if (values[Matrix.MTRANS_X] + dx > 0) dx = -values[Matrix.MTRANS_X]; else if (values[Matrix.MTRANS_X] + dx < -(mImageWidth * values[Matrix.MSCALE_X] - width)) dx = -(mImageWidth * values[Matrix.MSCALE_X] - width) - values[Matrix.MTRANS_X]; return dx; } /** * 設置縮放Matrix * * @param event */ private void setZoomMatrix(MotionEvent event) { // 只有同時觸屏兩個點的時候才執行 if (event.getPointerCount() < 2) return; float endDis = distance(event);// 結束距離 if (endDis > 10f) { // 兩個手指併攏在一塊兒的時候像素大於10 float scale = endDis / mStartDis;// 獲得縮放倍數 mStartDis = endDis;// 重置距離 mCurrentMatrix.set(getImageMatrix());// 初始化Matrix float[] values = new float[9]; mCurrentMatrix.getValues(values); scale = checkMaxScale(scale, values); setImageMatrix(mCurrentMatrix); } } /** * 檢驗scale,使圖像縮放後不會超出最大倍數 * * @param scale * @param values * @return */ private float checkMaxScale(float scale, float[] values) { if (scale * values[Matrix.MSCALE_X] > mMaxScale) scale = mMaxScale / values[Matrix.MSCALE_X]; mCurrentMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2); return scale; } /** * 重置Matrix */ private void reSetMatrix() { if (checkRest()) { mCurrentMatrix.set(mMatrix); setImageMatrix(mCurrentMatrix); } } /** * 判斷是否須要重置 * * @return 當前縮放級別小於模板縮放級別時,重置 */ private boolean checkRest() { // TODO Auto-generated method stub float[] values = new float[9]; getImageMatrix().getValues(values); // 獲取當前X軸縮放級別 float scale = values[Matrix.MSCALE_X]; // 獲取模板的X軸縮放級別,二者作比較 mMatrix.getValues(values); return scale < values[Matrix.MSCALE_X]; } /** * 判斷是否支持Matrix */ private void isMatrixEnable() { // 當加載出錯時,不可縮放 if (getScaleType() != ScaleType.CENTER) { setScaleType(ScaleType.MATRIX); } else { mMode = MODE_UNABLE;// 設置爲不支持手勢 } } /** * 計算兩個手指間的距離 * * @param event * @return */ private float distance(MotionEvent event) { float dx = event.getX(1) - event.getX(0); float dy = event.getY(1) - event.getY(0); /** 使用勾股定理返回兩點之間的距離 */ return (float) Math.sqrt(dx * dx + dy * dy); } /** * 雙擊時觸發 */ public void onDoubleClick() { float scale = isZoomChanged() ? 1 : mDobleClickScale; mCurrentMatrix.set(mMatrix);// 初始化Matrix mCurrentMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2); setImageMatrix(mCurrentMatrix); } } private class GestureListener extends SimpleOnGestureListener { private final MatrixTouchListener listener; public GestureListener(MatrixTouchListener listener) { this.listener = listener; } @Override public boolean onDown(MotionEvent e) { // 捕獲Down事件 return true; } @Override public boolean onDoubleTap(MotionEvent e) { // 觸發雙擊事件 listener.onDoubleClick(); return true; } @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub return super.onSingleTapUp(e); } @Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub super.onLongPress(e); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO Auto-generated method stub return super.onFling(e1, e2, velocityX, velocityY); } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub super.onShowPress(e); } @Override public boolean onDoubleTapEvent(MotionEvent e) { // TODO Auto-generated method stub return super.onDoubleTapEvent(e); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { // TODO Auto-generated method stub return super.onSingleTapConfirmed(e); } } }