慕课网实现自定义多指触控双击缩放的IamgeView以及解决和ViewPager的滑动冲突代码摘要

一下代码实现了:
第一:多指触控相对手指中心点缩放;
第二:双击相对手指中心点控制速度缩放;
第三:实现放大后自由移动;
第四:解决和ViewPager一起使用时的滑动冲突;

首先就是自定义ZoomImageView

/**
 * Created by 90720 on 2016/9/10.
 */
public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener, ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener {


    //--------------自由缩放,边界控制
    boolean mOnce = false;

    /**
     * 初始化时图片缩放的值,也是缩小的极限
     */
    private float mInitScale;
    /**
     * 双击放大时图片放大的值
     */
    private float mMidScale;
    /**
     * 放大的极限
     */
    private float mMaxScale;

    /**
     * 通过矩阵实现缩放
     */
    private Matrix mScaleMatrix;

    /**
     * 通过系统处理多点触控的类实现多点触控
     */
    private ScaleGestureDetector mScaleGestureDetector;

    //------------自由移动,边界控制

    /**
     * 记录上一次多点触控的数量
     */
    private int mLastPonterCoutn;

    private float mLastX;
    private float mLastY;

    private int mTouchSlop;

    private boolean isCanDrag;
    /**
     * 判断移动的时候是否进行边界检查
     */
    private boolean isCheckLeftAndrRight;
    private boolean isCheckTopAndButtom;

    //---------------双击放大或缩小,边界控制
    private GestureDetector mGrstureDector;

    //判断若正在缩放,忽略再次双击
    private boolean isAutoScale;

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

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

    public ZoomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mScaleMatrix = new Matrix();
        setScaleType(ScaleType.MATRIX);
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
        /**
         * 不要忘记这个方法
         */
        setOnTouchListener(this);
        //对角线的标准值
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        //双击缩放:使用线程让图片有节奏缩放
        mGrstureDector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            //实现即时缩放
//            @Override
//            public boolean onDoubleTap(MotionEvent e) {
//                //得到当前点击的xy坐标
//                float x=e.getX();
//                float y=e.getY();
//                if (getScale()<mMidScale){
//                    mScaleMatrix.postScale(mMidScale/getScale(),mMidScale/getScale(),x,y);
//                    setImageMatrix(mScaleMatrix);
//                }else {
//                    mScaleMatrix.postScale(mInitScale/getScale(),mInitScale/getScale(),x,y);
//                    setImageMatrix(mScaleMatrix);
//                }
            //实现有节奏缩放
            @Override
            public boolean onDoubleTap(MotionEvent e) {

                if (isAutoScale) {
                    return true;
                }
                //得到当前点击的xy坐标
                float x = e.getX();
                float y = e.getY();
                if (getScale() < mMidScale) {
                    postDelayed(new AutoScaleRunnable(mMidScale, x, y), 16);
                    isAutoScale = true;
                } else {
                    postDelayed(new AutoScaleRunnable(mInitScale, x, y), 16);
                    isAutoScale = true;
                }
                return true;
            }
        });
    }

    /**
     * 注册
     */
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    /**
     * 解除注册
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @SuppressWarnings("deprecation")//压打一下:兼容低版本的api
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }

    /**
     * 检测布局完成后执行
     */
    @Override
    public void onGlobalLayout() {

        //获取ImageView加载完成后的图片

        if (!mOnce) {
            //得到控件的宽和高
            int width = getWidth();
            int height = getHeight();
            //得到图片以及宽和高
            Drawable d = getDrawable();
            if (d == null) {
                return;
            }
            float scale = 1.0f;//缩放比例
            int dw = d.getIntrinsicWidth();
            int dh = d.getIntrinsicHeight();

            /**
             * 如果图片的宽大于控件的宽,但是高度小于控件的高度,将其缩小
             */
            if (dw > width && dh < height) {
                scale = width * 1.0f / dw;
            }
            /**
             * 如果图片的宽小于控件的宽,但是高度大于控件的高度,将其缩小
             */
            if (dh > height && dw < width) {
                scale = height * 1.0f / dh;
            }
            /**
             * 如果图片的宽和高都大于或小于控件的宽和高将其缩小或放大
             */
            if ((dw > width && dh > height) || (dw < width && dh < height)) {
                scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
            }

            mInitScale = scale;
            mMaxScale = mInitScale * 4;
            mMidScale = mInitScale * 2;

            //图片缩放完成后将图片移动至控件的中心:控件二分之一的宽度或高度减去图片二分之一的宽度高度
            int dx = getWidth() / 2 - dw / 2;
            int dy = getHeight() / 2 - dh / 2;

            /**
             * 通过Matrix实现缩放逻辑
             */
            mScaleMatrix.postTranslate(dx, dy);
            mScaleMatrix.postScale(mInitScale, mInitScale, width / 2, height / 2);
            setImageMatrix(mScaleMatrix);

            mOnce = true;
        }

    }

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

    /**
     * 缩放区间:mInitScale-mMaxScale
     *
     * @param scaleGestureDetector
     * @return
     */
    @Override
    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

        float scale = getScale();
        float scaleFactor = scaleGestureDetector.getScaleFactor();
        if (getDrawable() == null) {
            return true;
        }
        //scaleFactor>1.0f 想要放大,反之想要缩小
        if ((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)) {

            //如果缩放的结果比我们最小的要小,要重置为mInitScale
            if (scale * scaleFactor < mInitScale) {
                scaleFactor = mInitScale / scale;

            }
            if (scale * scaleFactor > mMaxScale) {
                scale = mMaxScale / scale;
            }
            //实现缩放,双指中心的缩放
            mScaleMatrix.postScale(scaleFactor, scaleFactor, scaleGestureDetector.getFocusX(), scaleGestureDetector.getFocusY());
            //检测图片边缘
            checkBorderAndCenterWhenScale();

            setImageMatrix(mScaleMatrix);
        }

        return true;
    }

    /**
     * 在缩放的同时进行边界控制和位置控制
     */
    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.top;
            }
        }
        //如果宽度或者高度小于控件的宽或搞,让其居中
        if (rect.width() < width) {
            deltaX = width / 2f - rect.right + rect.width() / 2f;
        }
        if (rect.height() < height) {
            deltaY = height / 2f - rect.bottom + rect.height() / 2f;
        }

        mScaleMatrix.postTranslate(deltaX, deltaY);

    }

    /**
     * 获得图片缩放后的宽和高,以及左右宽高
     *
     * @return
     */
    private RectF getMatrixRectF() {
        Matrix matrix = mScaleMatrix;
        RectF rectf = new RectF();//矩形2x2
        Drawable d = getDrawable();
        if (d != null) {
            rectf.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            matrix.mapRect(rectf);
        }
        return rectf;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        //双击的时候不允许放大或者缩小
        if (mGrstureDector.onTouchEvent(motionEvent)) {
            return true;
        }

        //将手指触摸屏幕的事件传递给系统处理多点触控的类ScaleGestureDetector
        mScaleGestureDetector.onTouchEvent(motionEvent);
        float x = 0;
        float y = 0;
        //拿到多点触控的数量
        int pointerCount = motionEvent.getPointerCount();
        for (int i = 0; i < pointerCount; i++) {
            x += motionEvent.getX();
            y += motionEvent.getY();
        }
        //中心的点的位置是
        x /= pointerCount;
        y /= pointerCount;
        if (mLastPonterCoutn != pointerCount) {
            mLastX = x;
            mLastY = y;
        }
        mLastPonterCoutn = pointerCount;
        RectF rectF = getMatrixRectF();
        switch (motionEvent.getAction()) {

            case MotionEvent.ACTION_DOWN:
                if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f) {
                    //当图片被放大后请求不被父布局拦截触摸事件
                    if (getParent() instanceof ViewPager) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (rectF.width() > getWidth() + 0.01f || rectF.height() > getHeight() + 0.01f) {
                    //当图片被放大后请求不被父布局拦截触摸事件
                    if (getParent() instanceof ViewPager) {
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }
                isCanDrag = false;
                float dx = x - mLastX;
                float dy = y - mLastY;
                if (!isCanDrag) {
                    isCanDrag = isMoveAction(dx, dy);
                }

                if (isCanDrag) {
                    if (getDrawable() != null) {
                        //默认可以检测
                        isCheckLeftAndrRight = isCheckTopAndButtom = true;
                        //如果宽度小于控件宽度不允许横向移动
                        if (rectF.width() < getWidth()) {
                            isCheckLeftAndrRight = false;
                            dx = 0;
                        }
                        //如果高度小于控件高度不允许纵向移动
                        if (rectF.height() < getHeight()) {
                            isCheckTopAndButtom = false;
                            dy = 0;
                        }
                        mScaleMatrix.postTranslate(dx, dy);
                        checkBorderWhenTranslate();
                        setImageMatrix(mScaleMatrix);
                    }

                }
                mLastX = x;
                mLastY = y;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mLastPonterCoutn = 0;
                break;

        }
        return true;
    }

    /**
     * 当图片移动的时候进行边界检查
     */
    private void checkBorderWhenTranslate() {
        RectF rectF = getMatrixRectF();
        float deltaX = 0;
        float delatY = 0;
        int width = getWidth();
        int height = getHeight();
        if (rectF.top > 0 && isCheckTopAndButtom) {
            delatY = -rectF.top;

        }
        if (rectF.bottom < height && isCheckTopAndButtom) {
            delatY = height - rectF.bottom;
        }
        if (rectF.left > 0 && isCheckLeftAndrRight) {
            deltaX = -rectF.left;
        }
        if (rectF.right < width && isCheckLeftAndrRight) {
            deltaX = width - rectF.right;
        }
        mScaleMatrix.postTranslate(deltaX, delatY);
    }

    private boolean isMoveAction(float dx, float dy) {

        return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;
    }

    /**
     * 开启线程实现有节奏缩放
     */
    private class AutoScaleRunnable implements Runnable {

        //单次缩放比例
        private final float BIGGER = 1.07f;
        private final float SMALLER = 0.93f;
        /**
         * 缩放的目标值
         */
        private float mTargetScale;
        //缩放的中心点
        private float x;
        private float y;
        private float tmpScale;

        public AutoScaleRunnable(float mTargetScale, float x, float y) {
            this.mTargetScale = mTargetScale;
            this.x = x;
            this.y = y;

            if (getScale() < mTargetScale) {
                tmpScale = BIGGER;//想放大
            }
            //不用else预防两者=
            if (getScale() > mTargetScale) {
                tmpScale = SMALLER;//想缩小
            }

        }

        @Override
        public void run() {
            //实现单次缩放
            mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);
            float currentScale = getScale();
            if ((tmpScale > 1.0f && currentScale < mTargetScale) || (tmpScale < 1.0f && currentScale > mTargetScale)) {
                postDelayed(this, 16);
            } else {
                //设置为我们的目标值
                float scale = mTargetScale / currentScale;
                mScaleMatrix.postScale(scale, scale, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mScaleMatrix);
                isAutoScale = false;

            }
        }
    }
}

其次是ViewPager的适配器

/**
 * Created by 90720 on 2016/9/12.
 */
public class MyPagerAdapter extends PagerAdapter{

    private ImageView[] mImageViews;
    private Context context;
    private int[] mImg;

    public MyPagerAdapter(Context context, ImageView[] mImageViews, int[] mImg) {
        this.context = context;
        this.mImageViews = mImageViews;
        this.mImg = mImg;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView(mImageViews[position]);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ZoomImageView imageView=new ZoomImageView(context);
        imageView.setImageResource(mImg[position]);
        container.addView(imageView);
        mImageViews[position]=imageView;
        return imageView;
    }

    @Override
    public int getCount() {
        return mImageViews.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view==object;
    }
}

最后是MainActivity代码

public class MainActivity extends AppCompatActivity {

    private ViewPager mViewPager;
    private int[] mImgs=new int[]{R.drawable.xinru,R.drawable.xinru,R.drawable.xinru};
    private ImageView[]mImageViews=new ImageView[mImgs.length];
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mViewPager= (ViewPager) findViewById(R.id.view_pager);
        mViewPager.setAdapter(new MyPagerAdapter(this,mImageViews,mImgs));

    }
}

就这样吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值