[转]Android:手势控制ImageView放大缩小图片

2016年02月11日 Android 暂无评论 阅读3648次
[转]Android:手势控制ImageView放大缩小图片


自定义ImageView的一个应用,转载的文章。
功能:在ImageView中识别手势用以控制图片放大或缩小。

public class MatrixImageView extends ImageView {
    private GestureDetector mGestureDetector;
    private Matrix mMatrix = new Matrix();
    private float mImageWidth;
    private float mImageHeight;
    private float mScale;
    private OnMovingListener mMoveListener;
    private OnSingleTapListener mSingleTapListener;
    public MatrixImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public MatrixImageView(Context context) {
        super(context, null);
        init();
    }
    private void init() {
        MatrixTouchListener listener = new MatrixTouchListener();
        setOnTouchListener(listener);
        mGestureDetector = new GestureDetector(getContext(),
                new GestureListener(listener));
        setBackgroundColor(Color.BLACK);
        setScaleType(ScaleType.FIT_CENTER);
    }
    public void setOnMovingListener(OnMovingListener listener) {
        mMoveListener = listener;
    }
    public void setOnSingleTapListener(OnSingleTapListener onSingleTapListener) {
        this.mSingleTapListener = onSingleTapListener;
    }
    @Override
    public void setImageBitmap(Bitmap bm) {
        super.setImageBitmap(bm);
        if (getWidth() == 0) {
            ViewTreeObserver vto = getViewTreeObserver();
            vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                public boolean onPreDraw() {
                    initData();
                    MatrixImageView.this.getViewTreeObserver()
                            .removeOnPreDrawListener(this);
                    return true;
                }
            });
        } else {
            initData();
        }
    }
    private void initData() {
        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];
        mScale = values[Matrix.MSCALE_X];
    }
    public class MatrixTouchListener implements OnTouchListener {
        private static final int MODE_DRAG = 1;
        private static final int MODE_ZOOM = 2;
        private static final int MODE_UNABLE = 3;
        private static final float MAX_SCALE = 6;
        private static final float DOUBLE_CLICK_SACLE = 2;
        private int mMode = 0;
        private float mStartDis;
        private Matrix mCurrentMatrix = new Matrix();
        private boolean mLeftDragable;
        private boolean mRightDragable;
        private boolean mFirstMove = false;
        private PointF mStartPoint = new PointF();
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mMode = MODE_DRAG;
                mStartPoint.set(event.getX(), event.getY());
                isMatrixEnable();
                startDrag();
                checkDragable();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                reSetMatrix();
                stopDrag();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mMode == MODE_ZOOM) {
                    setZoomMatrix(event);
                } else if (mMode == MODE_DRAG) {
                    setDragMatrix(event);
                } else {
                    stopDrag();
                }
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if (mMode == MODE_UNABLE)
                    return true;
                mMode = MODE_ZOOM;
                mStartDis = distance(event);
                break;
            case MotionEvent.ACTION_POINTER_UP:
                break;
            default:
                break;
            }
            return mGestureDetector.onTouchEvent(event);
        }
        private void startDrag() {
            if (mMoveListener != null)
                mMoveListener.startDrag();
        }
        private void stopDrag() {
            if (mMoveListener != null)
                mMoveListener.stopDrag();
        }
        private void checkDragable() {
            mLeftDragable = true;
            mRightDragable = true;
            mFirstMove = true;
            float[] values = new float[9];
            getImageMatrix().getValues(values);
            if (values[Matrix.MTRANS_X] >= 0)
                mRightDragable = false;
            if ((mImageWidth) * values[Matrix.MSCALE_X]
                    + values[Matrix.MTRANS_X] <= getWidth()) {
                mLeftDragable = false;
            }
        }
        public void setDragMatrix(MotionEvent event) {
            if (isZoomChanged()) {
                float dx = event.getX() - mStartPoint.x;
                float dy = event.getY() - mStartPoint.y;
                if (Math.sqrt(dx * dx + dy * dy) > 10f) {
                    mStartPoint.set(event.getX(), event.getY());
                    mCurrentMatrix.set(getImageMatrix());
                    float[] values = new float[9];
                    mCurrentMatrix.getValues(values);
                    dy = checkDyBound(values, dy);
                    dx = checkDxBound(values, dx, dy);
                    mCurrentMatrix.postTranslate(dx, dy);
                    setImageMatrix(mCurrentMatrix);
                }
            } else {
                stopDrag();
            }
        }
        private boolean isZoomChanged() {
            float[] values = new float[9];
            getImageMatrix().getValues(values);
            float scale = values[Matrix.MSCALE_X];
            return scale != mScale;
        }
        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;
        }
        private float checkDxBound(float[] values, float dx, float dy) {
            float width = getWidth();
            if (!mLeftDragable && dx < 0) {
                if (Math.abs(dx) * 0.4f > Math.abs(dy) && mFirstMove) {
                    stopDrag();
                }
                return 0;
            }
            if (!mRightDragable && dx > 0) {
                if (Math.abs(dx) * 0.4f > Math.abs(dy) && mFirstMove) {
                    stopDrag();
                }
                return 0;
            }
            mLeftDragable = true;
            mRightDragable = true;
            if (mFirstMove)
                mFirstMove = false;
            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;
        }
        private void setZoomMatrix(MotionEvent event) {
            if (event.getPointerCount() < 2)
                return;
            float endDis = distance(event);
            if (endDis > 10f) {
                float scale = endDis / mStartDis;
                mStartDis = endDis;
                mCurrentMatrix.set(getImageMatrix());
                float[] values = new float[9];
                mCurrentMatrix.getValues(values);
                scale = checkMaxScale(scale, values);
                PointF centerF = getCenter(scale, values);
                mCurrentMatrix.postScale(scale, scale, centerF.x, centerF.y);
                setImageMatrix(mCurrentMatrix);
            }
        }
        private PointF getCenter(float scale, float[] values) {
            if (scale * values[Matrix.MSCALE_X] < mScale || scale >= 1) {
                return new PointF(getWidth() / 2, getHeight() / 2);
            }
            float cx = getWidth() / 2;
            float cy = getHeight() / 2;
            if ((getWidth() / 2 - values[Matrix.MTRANS_X]) * scale < getWidth() / 2)
                cx = 0;
            if ((mImageWidth * values[Matrix.MSCALE_X] + values[Matrix.MTRANS_X])
                    * scale < getWidth())
                cx = getWidth();
            return new PointF(cx, cy);
        }
        private float checkMaxScale(float scale, float[] values) {
            if (scale * values[Matrix.MSCALE_X] > MAX_SCALE)
                scale = MAX_SCALE / values[Matrix.MSCALE_X];
            return scale;
        }
        private void reSetMatrix() {
            if (checkRest()) {
                mCurrentMatrix.set(mMatrix);
                setImageMatrix(mCurrentMatrix);
            } else {
                float[] values = new float[9];
                getImageMatrix().getValues(values);
                float height = mImageHeight * values[Matrix.MSCALE_Y];
                if (height < getHeight()) {
                    float topMargin = (getHeight() - height) / 2;
                    if (topMargin != values[Matrix.MTRANS_Y]) {
                        mCurrentMatrix.set(getImageMatrix());
                        mCurrentMatrix.postTranslate(0, topMargin
                                - values[Matrix.MTRANS_Y]);
                        setImageMatrix(mCurrentMatrix);
                    }
                }
            }
        }
        private boolean checkRest() {
            float[] values = new float[9];
            getImageMatrix().getValues(values);
            float scale = values[Matrix.MSCALE_X];
            return scale < mScale;
        }
        private void isMatrixEnable() {
            if (getScaleType() != ScaleType.CENTER) {
                setScaleType(ScaleType.MATRIX);
            } else {
                mMode = MODE_UNABLE;
            }
        }
        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 : DOUBLE_CLICK_SACLE;
            mCurrentMatrix.set(mMatrix);
            mCurrentMatrix.postScale(scale, scale, getWidth() / 2,
                    getHeight() / 2);
            setImageMatrix(mCurrentMatrix);
        }
    }
    private class GestureListener extends SimpleOnGestureListener {
        private final MatrixTouchListener mTouchListener;
        public GestureListener(MatrixTouchListener listener) {
            this.mTouchListener = listener;
        }
        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            mTouchListener.onDoubleClick();
            return true;
        }
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return super.onSingleTapUp(e);
        }
        @Override
        public void onLongPress(MotionEvent e) {
            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) {
            return super.onFling(e1, e2, velocityX, velocityY);
        }
        @Override
        public void onShowPress(MotionEvent e) {
            super.onShowPress(e);
        }
        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            return super.onDoubleTapEvent(e);
        }
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (mSingleTapListener != null)
                mSingleTapListener.onSingleTap(e);
            return super.onSingleTapConfirmed(e);
        }
    }
    public interface OnMovingListener {
        public void startDrag();
        public void stopDrag();
    }
    public interface OnSingleTapListener {
        public void onSingleTap(MotionEvent e);
    }
}




我对其中定义OnSingleTapListener接口的方法稍作了一些修改,为onSingleTap回调方法增加了MotionEvent类型的参数,来方便我们根据用户具体的事件内容作出对应的控制。

因多处转载,中间并伴随着不断的修改完善,原作者无从找起,最后感谢各位奉献着。

分享本文至:

WRITTEN BY

avatar
本文标签:ImageViewzoom
看了本文是不是觉得很赞,那就赶紧点击下面按钮分享给身边的朋友吧!

欢迎留言




用户登录

sitemap