美文网首页
支持放大缩小和拖动的ImageView

支持放大缩小和拖动的ImageView

作者: 旅行者归来 | 来源:发表于2019-11-30 11:48 被阅读0次
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewTreeObserver;

import androidx.appcompat.widget.AppCompatImageView;

public class ZoomMoveImageView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener, View.OnTouchListener {

    //最大缩放倍数-可以根据需求对外提供set方法
    private static int MAX_SCALE = 10;
    //默认缩放倍数,初始化后会根据图片大小改变这个值
    private static float mScale = 1f;
    private Context mContext;
    //手势缩放监听器
    private ScaleGestureDetector gestureDetector;
    //缩放工具
    private Matrix mMatrix;
    //首次加载,避免onGlobalLayout多次执行
    private boolean isFristLoad = true;

    public ZoomMoveImageView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public ZoomMoveImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    public void init() {
        mMatrix = new Matrix();
        //设置类型,使图片能支持Matrix
        setScaleType(ScaleType.MATRIX);
        this.setOnTouchListener(this);
        gestureDetector = new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {

            /**
             * 缩放进行中,返回值表示是否下次缩放需要重置,如果返回ture,那么scaleGestureDetector就会重置缩放事件,如果返回false,scaleGestureDetector会在之前的缩放上继续进行计算
             */
            @Override
            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
                if (null == getDrawable() || mMatrix == null) {
                    return true;
                }
                //缩放因子,这个是根据两个手指来计算缩放的倍数
                float factor = scaleGestureDetector.getScaleFactor();
                float scale = getScale();
                if ((scale < mScale * MAX_SCALE && factor > 1.0f) || (scale > mScale && factor < 1.0f)) {
                    if (scale * factor < mScale) {
                        factor = mScale / scale;
                    }
                    if (scale * factor > mScale * MAX_SCALE) {
                        factor = mScale * MAX_SCALE / scale;
                    }
                    //以屏幕中央位置进行缩放
                    mMatrix.postScale(factor, factor, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY());
                    borderAndCenterCheck();
                    setImageMatrix(mMatrix);
                }
                return true;
            }

            /**
             * 缩放开始,返回值表示是否受理后续的缩放事件
             */
            @Override
            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
                //缩放开始,这里返回true表示要接收这个事件,必须为true,onScale才能执行
                return true;
            }

            /**
             * 缩放结束
             */
            @Override
            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
                float scale = getScale();
                if (scale < mScale) {
                    float s = mScale / scale;
                    mMatrix.postScale(s, s, getWidth() / 2, getHeight() / 2);
                    setImageMatrix(mMatrix);
                }
            }
        });
    }


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

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

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

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

    @Override
    public void onGlobalLayout() {
        if (isFristLoad) {
            isFristLoad = false;
            //获取控件的宽度和高度
            int width = getWidth();
            int height = getHeight();
            //获取到ImageView对应图片的宽度和高度
            Drawable drawable = getDrawable();
            if (drawable == null) {
                return;
            }
            // 图片固有宽度
            int imgWidth = drawable.getIntrinsicWidth();
            // 图片固有高度
            int imgHeight = drawable.getIntrinsicHeight();
            //接下来对图片做初始的缩放处理,保证图片能看全
            if (imgWidth >= width && imgHeight >= height) {
                // 图片宽度和高度都大于控件(缩小)
                mScale = Math.min(width * 1.0f / imgWidth, height * 1.0f / imgHeight);
            } else if (imgWidth > width && imgHeight < height) {
                // 图片宽度大于控件,高度小于控件(缩小)
                mScale = width * 1.0f / imgWidth;
            } else if (imgWidth < width && imgHeight > height) {
                // 图片宽度小于控件,高度大于控件(缩小)
                mScale = height * 1.0f / imgHeight;
            } else {
                // 图片宽度小于控件,高度小于控件(放大)
                mScale = Math.min(width * 1.0f / imgWidth, height * 1.0f / imgHeight);
            }
            // 将图片移动到手机屏幕的中间位置
            float distanceX = width / 2 - imgWidth / 2;
            float distanceY = height / 2 - imgHeight / 2;
            mMatrix.postTranslate(distanceX, distanceY);
            mMatrix.postScale(mScale, mScale, width / 2, height / 2);
            setImageMatrix(mMatrix);
        }
    }


    /**
     * 获得图片放大缩小以后的宽和高
     */
    private RectF getMatrixRectF() {
        RectF rectF = new RectF();
        Drawable drawable = getDrawable();
        if (drawable != null) {
            rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
            mMatrix.mapRect(rectF);
        }
        return rectF;
    }

    /**
     * 图片在缩放时进行边界控制
     */
    private void borderAndCenterCheck() {
        RectF rect = getMatrixRectF();
        float deltaX = 0;
        float deltaY = 0;
        int viewWidth = getWidth();
        int viewHeight = getHeight();
        // 缩放时进行边界检测,防止出现白边
        if (rect.width() >= viewWidth) {
            if (rect.left > 0) {
                deltaX = -rect.left;
            }
            if (rect.right < viewWidth) {
                deltaX = viewWidth - rect.right;
            }
        }
        if (rect.height() >= viewHeight) {
            if (rect.top > 0) {
                deltaY = -rect.top;
            }
            if (rect.bottom < viewHeight) {
                deltaY = viewHeight - rect.bottom;
            }
        }
        // 如果宽度或者高度小于控件的宽或者高;则让其居中
        if (rect.width() < viewWidth) {
            deltaX = viewWidth / 2f - rect.right + rect.width() / 2f;
        }
        if (rect.height() < viewHeight) {
            deltaY = viewHeight / 2f - rect.bottom + rect.height() / 2f;
        }
        mMatrix.postTranslate(deltaX, deltaY);
    }


    private float preX, preY, currentX, currentY;
    private int prePointerCount;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        currentX = 0;
        currentY = 0;
        int pointerCount = event.getPointerCount();
        for (int i = 0; i < pointerCount; i++) {
            currentX += event.getX();
            currentY += event.getY();
        }
        currentX /= pointerCount;
        currentY /= pointerCount;
        if (pointerCount != prePointerCount) {
            preX = currentX;
            preY = currentY;
            prePointerCount = pointerCount;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                float dx = currentX - preX;
                float dy = currentY - preY;
                setMovePosition(getDrawable(), mMatrix, dx, dy, getWidth(), getHeight());
                setImageMatrix(mMatrix);
                preX = currentX;
                preY = currentY;
                break;
            case MotionEvent.ACTION_UP://有多根手指触摸屏幕时,只有当所有的手指抬起时这里才执行
                prePointerCount = 0;
                break;
        }
        return gestureDetector.onTouchEvent(event);
    }

    public static void setMovePosition(Drawable drawable, Matrix matrix, float dx, float dy, int w, int h) {
        RectF rectF = new RectF(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        matrix.mapRect(rectF);
        float rw = rectF.width();
        float rh = rectF.height();
        if (rw > w && rectF.left + dx <= 0 && rectF.right + dx >= w) {
            matrix.postTranslate(dx, 0);
        }
        if (rh > h && rectF.top + dy <= 0 && rectF.bottom + dy >= h) {
            matrix.postTranslate(0, dy);
        }
    }

    public void setShowPosition(Drawable drawable, Matrix matrix, int w, int h) {
        RectF rectF = new RectF(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        matrix.mapRect(rectF);
        float rw = rectF.width();
        float rh = rectF.height();
        float moveX = 0, moveY = 0;
        if (rw < w) {
            moveX = w / 2 - rw / 2 - rectF.left;
        }
        if (rh < h) {
            moveY = h / 2 - rh / 2 - rectF.top;
        }
        if (rw > w && rectF.left > 0) {
            moveX = -rectF.left;
        }
        if (rw > w && rectF.right < w) {
            moveX = w - rectF.right;
        }
        if (rh > h && rectF.top > 0) {
            moveY = -rectF.top;
        }
        if (rh > h && rectF.bottom < h) {
            moveY = h - rectF.bottom;
        }
        matrix.postTranslate(moveX, moveY);
    }
}

相关文章

网友评论

      本文标题:支持放大缩小和拖动的ImageView

      本文链接:https://www.haomeiwen.com/subject/cvhowctx.html