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); } } }