android自定义view实现可自由放大缩小和移动的imageView

本自定义的ZoomImageView实现的功能有:

  1. 多指触摸以中心点自由放大缩小,
  2. 图片整体默认居中于控件中,
  3. 双击放大缩小并防止出现白边

效果图就先不上了,

代码如下:

package com.hai.widget;

import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;
import android.widget.ImageView;

/**
 * 可自由放大缩小Imageview
 * Created by 黄海 on 2016/2/18.
 */
public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener, ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener {
    private boolean mOnce;//init一次
    /**
     * 初始缩放比例
     */
    private float mInitScale;
    /**
     * 双击放大比例
     */
    private float mMidScale;
    /**
     * 最大放大比例
     */
    private float mMaxScale;
    private Matrix mMatrix;
    /**
     * 多点触摸检测
     */
    private ScaleGestureDetector mScaleGestureDetector;
    //---------多点触控--------
    private int mLastPointCount;
    private float mLastX;
    private float mLastY;
    private boolean isCanDrag;
    private int mTouchSlop;
    private boolean isCheckTopAndBottom;
    private boolean isCheckLeftAndRight;

    //-------------双击放大缩小
    private GestureDetector mGestureDetector;
    private boolean isAutoScale;

    public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mMatrix = new Matrix();
        setScaleType(ScaleType.MATRIX);
        setOnTouchListener(this);
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        isCheckTopAndBottom = true;
        isCheckLeftAndRight = true;
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                if (isAutoScale) {
                    return true;
                }
                if (getScale() < mMidScale) {
                    //放大
//                    mMatrix.postScale(mMidScale / getScale(), mMidScale / getScale(), e.getX(), e.getY());
                    postDelayed(new AutoScaleRunnable(mMidScale, e.getX(), e.getY()), 16);
                    isAutoScale = true;
                } else {
                    //缩小
//                    mMatrix.postScale(mInitScale / getScale(), mInitScale / getScale(), e.getX(), e.getY());
                    postDelayed(new AutoScaleRunnable(mInitScale, e.getX(), e.getY()), 16);
                    isAutoScale = true;
                }
                return true;
            }
        });
    }

    class AutoScaleRunnable implements Runnable {
        private float x, y;
        private float targetScale;
        private float tempScale;
        private final float BIGGER = 1.07f;
        private final float SMALL = 0.93f;

        public AutoScaleRunnable(float targetScale, float x, float y) {
            this.targetScale = targetScale;
            this.x = x;
            this.y = y;
            if (getScale() < targetScale) {
                tempScale = BIGGER;
            }
            if (getScale() > targetScale) {
                tempScale = SMALL;
            }
        }

        @Override
        public void run() {
            mMatrix.postScale(tempScale, tempScale, x, y);
            checkBorderAndCenterWhenScale();
            setImageMatrix(mMatrix);
            float currentScale = getScale();
            if ((currentScale < targetScale && tempScale > 1.0f) || (currentScale > targetScale && tempScale < 1.0f)) {
                postDelayed(this, 15);
            } else {
                mMatrix.postScale(targetScale / currentScale, targetScale / currentScale, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mMatrix);
                isAutoScale = false;
            }
        }
    }

    public ZoomImageView(Context context) {
        this(context, null);

    }

    public ZoomImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    @Override
    public void onGlobalLayout() {
        if (!mOnce) {
            int width = getWidth();
            int height = getHeight();
            Drawable drawable = getDrawable();
            int dw = drawable.getIntrinsicWidth();
            int dh = drawable.getIntrinsicHeight();
            float scale = 1.0f;
            /**
             * 图片宽度大于屏幕宽度,高小于屏幕高
             */
            if (dw > width && dh < height) {
                scale = 1.0f * width / dw;
            }
            if (dw < width && dh > height) {
                scale = 1.0f * height / dh;
            }
            if ((dw > width && dh > height) || (dw < width && dh < height)) {
                scale = Math.min(1.0f * width / dw, 1.0f * height / dh);
            }
            //得到缩放比例
            mInitScale = scale;
            mMidScale = scale * 3;
            mMaxScale = scale * 5;
            //图片平移到中心
            int dx = width / 2 - dw / 2;
            int dy = height / 2 - dh / 2;
            mMatrix.postTranslate(dx, dy);
            mMatrix.postScale(scale, scale, width / 2, height / 2);
            setImageMatrix(mMatrix);
            mOnce = true;
        }

    }

    /**
     * 获取当前图片的缩放值
     *
     * @return
     */
    float getScale() {
        float values[] = new float[9];
        mMatrix.getValues(values);
        return values[Matrix.MSCALE_X];
    }

    @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 > mMaxScale) {
                scaleFactor = mMaxScale / scale;
            }
            if (scale * scaleFactor < mInitScale) {
                scaleFactor = mInitScale / scale;
            }
            mMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());
            checkBorderAndCenterWhenScale();
            setImageMatrix(mMatrix);
        }
        return false;
    }

    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 / 2f - rect.right + rect.width() / 2;
        }
        if (rect.height() < height) {
            deltaY = height / 2f - rect.bottom + rect.height() / 2;
        }
        mMatrix.postTranslate(deltaX, deltaY);
    }

    /**
     * 左右上下移动白边检测
     */
    private void checkBorderWhenTranslate() {
        RectF rect = getMatrixRectf();
        float deltaX = 0;
        float deltaY = 0;
        int width = getWidth();
        int height = getHeight();
        if (rect.left > 0 && isCheckLeftAndRight) {
            deltaX = -rect.left;
        }
        if (rect.right < width && isCheckLeftAndRight) {
            deltaX = width - rect.right;
        }
        if (rect.top > 0 && isCheckTopAndBottom) {
            deltaY = -rect.top;
        }
        if (rect.bottom < height && isCheckTopAndBottom) {
            deltaY = height - rect.bottom;
        }
        mMatrix.postTranslate(deltaX, deltaY);
    }

    /**
     * 获取图片放大以后的l,t,r,b;
     *
     * @return
     */
    private RectF getMatrixRectf() {
        Matrix matrix = mMatrix;
        Drawable d = getDrawable();
        RectF rectF = new RectF();
        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();
        for (int i = 0; i < pointerCount; i++) {
            x += event.getX(i);
            y += event.getY(i);
        }
        x /= pointerCount;
        y /= pointerCount;
        if (mLastPointCount != pointerCount) {
            isCanDrag = false;
            mLastX = x;
            mLastY = y;
        }
        mLastPointCount = pointerCount;
        RectF rectF = getMatrixRectf();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                    //请求父view不要拦截事件,也就是与viewpage结合使用的时候防止事件冲突
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                /**
                 * 处理图片自由移动
                 */
//                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
//                    //请求父view不要拦截事件
//                    getParent().requestDisallowInterceptTouchEvent(true);
//                }
                float dx = x - mLastX;
                float dy = y - mLastY;
                if (!isCanDrag) {
                    isCanDrag = isMoveAction(dx, dy);
                }
                if (getDrawable() != null) {
                    if (isCanDrag) {
                        //如果宽度小于控件宽度,不能横向移动
                        if (rectF.width() < getWidth()) {
                            isCheckLeftAndRight = false;
                            dx = 0;
                        } else {
                            isCheckLeftAndRight = true;
                        }
                        if (rectF.height() < getHeight()) {
                            isCheckTopAndBottom = false;
                            dy = 0;
                        } else {
                            isCheckTopAndBottom = true;
                        }
                        mMatrix.postTranslate(dx, dy);
                        checkBorderWhenTranslate();
                        setImageMatrix(mMatrix);
                    }
                }
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mLastPointCount = 0;
                getParent().requestDisallowInterceptTouchEvent(false);
                break;
            default:
                break;
        }
        return true;
    }


    /**
     * 判断是否是move
     *
     * @param dx
     * @param dy
     * @return
     */
    private boolean isMoveAction(float dx, float dy) {
        return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;
    }
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值