图片的预览处理----多点触控放缩/双击放大缩小/viewPager冲突解决

在android中,多点触控和双击图片很常用,比如朋友圈点击图片放大兼具手势点击滑动等;当然也有不少第三方来支持这些 比如:phtotView等;

下面来实现一个自己实现的多点操作的图片预览,老规矩 先上图来展示效果:

这里写图片描述

由于是截取模拟器的gif 所以没有显示多点触控 的手势操作 但是手机上完全支持这些操作:

    1.手势多点放缩,且会有最大和最小的限制;
    2.双击放大缩小
    3.在放大时候可以滑动来全览图片
    4.支持viewPager的滑动,切不会和图片放缩冲突

下面从代码角度来剖析demo:

1.初始化操作,最优展示图片:

MainActiivty和Viewpager的Adapter代码 不做展示;最后会全部展示代码;直接来看自定义的图片类:
初始化ZoomImageView:

public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener {

    private boolean mOnce;
    private float minScale;
    private float maxScale;
    private float mTouchScale;
    private Matrix mMatrix;


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

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

    public ZoomImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {


        mMatrix = new Matrix();
        setScaleType(ScaleType.MATRIX);
    }

 @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        //注册监听 绘制完成设置图片
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);

    }

        @Override
    public void onGlobalLayout() {

        if (!mOnce){
            float scale = 1;
            //获取控件的宽和高
            int width = getWidth();
            int height = getHeight();
            //获取图片 及其宽和高
            Drawable drawable = getDrawable();
            if (drawable == null)
                return;
            int intrinsicWidth = drawable.getIntrinsicWidth();
            int intrinsicHeight = drawable.getIntrinsicHeight();

            //控件的宽高和img比较确定缩放的比例
            if (intrinsicWidth > width && intrinsicHeight < height){
                scale = width * 1.0f / intrinsicWidth;
            }
            if (intrinsicHeight > height && intrinsicWidth < width){
                scale = height * 1.0f / intrinsicHeight;
            }
            if ((intrinsicHeight > height && intrinsicWidth > width) || (intrinsicHeight < height && intrinsicWidth < width)){
                scale =  Math.min(height * 1.0f / intrinsicHeight ,width * 1.0f / intrinsicWidth);
            }

            minScale = scale;
            maxScale = 4 * minScale;
            mTouchScale = 2 * minScale;
            float dx = width/2 - intrinsicWidth/2;
            float dy = height/2 - intrinsicHeight/2;
            mMatrix.postTranslate(dx,dy);
            mMatrix.postScale(minScale,minScale,width/2,height/2);

            setImageMatrix(mMatrix);




            mOnce =true;
        }
    }

在 初始化中初始化了minScale,maxScale以及mMatrix等,需要注意的是 ,由于图片width和height与screen不一定适配 所以需要代码进行优化,由于自定义view绘制有流程所以这里用到:

   @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        //注册监听 绘制完成设置图片
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);

    }

在view绑定window时候注册监听,绘制完成在onGlobalLayout中设置图片;核心代码就是此处的监听

getViewTreeObserver().addOnGlobalLayoutListener(this);
 @Override
    public void onGlobalLayout() {

        if (!mOnce){
            float scale = 1;
            //获取控件的宽和高
            int width = getWidth();
            int height = getHeight();
            //获取图片 及其宽和高
            Drawable drawable = getDrawable();
            if (drawable == null)
                return;
            int intrinsicWidth = drawable.getIntrinsicWidth();
            int intrinsicHeight = drawable.getIntrinsicHeight();

            //控件的宽高和img比较确定缩放的比例
            if (intrinsicWidth > width && intrinsicHeight < height){
                scale = width * 1.0f / intrinsicWidth;
            }
            if (intrinsicHeight > height && intrinsicWidth < width){
                scale = height * 1.0f / intrinsicHeight;
            }
            if ((intrinsicHeight > height && intrinsicWidth > width) || (intrinsicHeight < height && intrinsicWidth < width)){
                scale =  Math.min(height * 1.0f / intrinsicHeight ,width * 1.0f / intrinsicWidth);
            }

            minScale = scale;
            maxScale = 4 * minScale;
            mTouchScale = 2 * minScale;
            float dx = width/2 - intrinsicWidth/2;
            float dy = height/2 - intrinsicHeight/2;
            mMatrix.postTranslate(dx,dy);
            mMatrix.postScale(minScale,minScale,width/2,height/2);

            setImageMatrix(mMatrix);




            mOnce =true;
        }
    }

在上面代码中回获取图片的宽和高,以及空间本身(就是自定义imageView在xml)中的宽和高进行对比,然后确定合适的比例来确定图片的最优化缩放
到这里,图片无论小还是大都可以以最优的方案 展示;

2.多点触控放缩图片

接下来开始对图片的多点触控放大缩小进行处理:
这里用到一个多点触控的类ScaleGestureDetector;然后再init()中声明这个类;

mScaleGestureDetector = new ScaleGestureDetector(getContext(), this);

然后自定义件的主类继承接口ScaleGestureDetector.OnScaleGestureListener;
然后实现其中3个方法;

    //放缩过程中
    @Override
    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

        return true;}

    //放缩开始时候
    @Override
    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
        return true;
    }


    //放缩结束时候
    @Override
    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

    }

在onScale中进行多点触控的逻辑处理:

    //放缩过程中
    @Override
    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

        float scale = getScale();
        float scaleFactor = scaleGestureDetector.getScaleFactor();

        //判断drawable是否为空
        if (getDrawable() == null)
            return true;
        //判断范围
        if ((scale < maxScale && scale >= 1.0f) || (scale > minScale && scale <= 1.0f)){

            if (scale * scaleFactor > maxScale){
                    scaleFactor = maxScale/scale;
                }
            if (scale * scaleFactor < minScale){
                    scaleFactor = minScale/scale;
                }
        }
        /**
         * 限定图片放缩的最大值和最小值
         * */
        else if (scale >= maxScale && scaleFactor > 1.0f){
            scaleFactor = maxScale/scale;
        }else if (scale <= minScale/2 && scaleFactor < 1.0f){

            scaleFactor = minScale/(2*scale);
        }


        //注意这里要以手势中心为缩放中心
        mMatrix.postScale(scaleFactor,scaleFactor,
                scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY());

        transBoderAndCenter();

        setImageMatrix(mMatrix);

        return true;
    }

首先获取当前的scale:

private float getScale() {
        float[] values = new float[9];
        mMatrix.getValues(values);
        float value = values[Matrix.MSCALE_X];
        return value;
    }

通过scale与1.0f的比较进行放大和缩小的判断;当scale>1.0f 时候为放大 ,反之为缩小;然后对最大和最小进行限制:

/**
         * 限定图片放缩的最大值和最小值
         * */
        else if (scale >= maxScale && scaleFactor > 1.0f){
            scaleFactor = maxScale/scale;
        }else if (scale <= minScale/2 && scaleFactor < 1.0f){
            //Log.i("==scale",String.valueOf(scaleFactor));
            scaleFactor = minScale/(2*scale);
        }

最后利用mMatrix对图片进行放缩:

 //注意这里要以手势中心为缩放中心
        mMatrix.postScale(scaleFactor,scaleFactor,
                scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY());

        transBoderAndCenter();

        setImageMatrix(mMatrix);

在代码中有主意到在真正传参设置之前,有一个方法:transBoderAndCenter():


    //在缩放时候进行边界控制和中心点的控制
    private void transBoderAndCenter() {
        //设置x和y的边界值
        float Dx = 0;
        float Dy = 0;
        RectF rectF = getRectF();
        int width = getWidth();
        int height = getHeight();

        float l = rectF.left;
        float r = rectF.right;
        float t = rectF.top;
        float b = rectF.bottom;


        float w = rectF.width();
        float h = rectF.height();

        if (w >= width){
            if (l > 0){
                Dx = -l;
            }
            if (r < width){
                Dx = width - r;
            }
        }

        if (h >= height){
            if (t > 0){
                Dy = -t;
            }
            if (b < height){
                Dy = height - b;
            }
        }

        if (w < width){
            Dx = width/2 - r + w/2;
        }
        if (h < height){
            Dy = height/2 - b + h/2;
        }

        mMatrix.postTranslate(Dx,Dy);


    }

是为了防止在放缩过程中会有空白边界产生;

3.处理放大之后的图片滑动:

在放大之后全屏无法展示图片,所以会有图片滑动预览的操作;自然要实现自定义控件的onTouch事件:
在init()中注册监听 类继承touch的接口View.OnTouchListener并且初始化滑动的有效距离mTouchslop:

setOnTouchListener(this);
mTouchslop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

ok~ 直接上核心代码:

 @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {


        mScaleGestureDetector.onTouchEvent(motionEvent);

        /**-------------------------------------------
         * 设置自由移动*/

        float x = 0;
        float y = 0;

        int pointerCount = motionEvent.getPointerCount();
        for (int i = 0; i < pointerCount; i++) {
            x += motionEvent.getX(i);
            y += motionEvent.getY(i);
        }

        x /= pointerCount;
        y /= pointerCount;


        if (mPointCount != pointerCount){
            mLastX = x;
            mLastY = y;
            isMove = false;
        }

        mPointCount = pointerCount;

        RectF rect = getRectF();





            case MotionEvent.ACTION_MOVE:
                isMoveX = true;
                isMoveY = true;
                float mx = 0;
                float my = 0;

                int count = motionEvent.getPointerCount();
                for (int i = 0; i < count; i++) {
                    mx += motionEvent.getX(i);
                    my += motionEvent.getY(i);
                }
                    mx /= count;
                    my /= count;
                float dx = mx - mLastX;
                float dy = my - mLastY;
                if (!isMove){
                   isMove = isMoveZoom(dx, dy);
                }

                if (isMove){
                    RectF rectF = getRectF();
                    float w = rectF.width();
                    float h = rectF.height();
                    int width = getWidth();
                    int height = getHeight();

                    if (getDrawable() != null){
                        if (w < width){
                            isMoveX = false;
                            dx = 0;
                        }
                        if (h < height){
                            isMoveY = false;
                            dy = 0;
                        }
                        mMatrix.postTranslate(dx,dy);
                        transBoder();
                        setImageMatrix(mMatrix);
                    }
                }
                mLastX = x;
                mLastY = y
            }

                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mPointCount = 0;
                break;
        }
        return true;
    }

由于在滑动过程中也是多点触控,所以不能直接监听滑动的x.y;需要计算中心点:


        int pointerCount = motionEvent.getPointerCount();
        for (int i = 0; i < pointerCount; i++) {
            x += motionEvent.getX(i);
            y += motionEvent.getY(i);
        }

        x /= pointerCount;
        y /= pointerCount;


        if (mPointCount != pointerCount){
            mLastX = x;
            mLastY = y;
            isMove = false;
        }

在滑动过程中,若图片缩小为宽和高均小于srceen的宽和高,则不能滑动;位移变量dx= 0;dy = 0:

    if (w < width){
                            isMoveX = false;
                            dx = 0;
                        }
                        if (h < height){
                            isMoveY = false;
                            dy = 0;
                        }

除此之外均可以滑动,而且 在滑动过程中要加限制 不然会出现无限滑动,最后设置mMatrix完成滑动的事件:

        transBoder();
        setImageMatrix(mMatrix);
        .....
        .....
private void transBoder() {
        //设置x和y的边界值
        float Dx = 0;
        float Dy = 0;
        RectF rectF = getRectF();
        int width = getWidth();
        int height = getHeight();

        float l = rectF.left;
        float r = rectF.right;
        float t = rectF.top;
        float b = rectF.bottom;


        float w = rectF.width();
        float h = rectF.height();

        if (l > 0 && isMoveX){
            Dx = -l;
        }
        if (r < width && isMoveX){
            Dx = width - r;
        }

        if (t > 0 && isMoveY){
            Dy = -t;
        }
        if (b < height && isMoveY){
            Dy = height - b;
        }

        mMatrix.postTranslate(Dx,Dy);
    }

    private boolean isMoveZoom(float dx, float dy) {
        return Math.sqrt(dx * dx + dy * dy) > mTouchslop;
    }

4双击放缩的处理

双击要用到GestureDetector;首先在onTouch中改变return的boolean,防止点击时候滑动:

   if (mGes.onTouchEvent(motionEvent)){
            return true;
        }

同样可以通过实现接口来实现;但是接口中方法实现复写的量太多,比较基类所以在init()中实现他的一个子类:

        mGes = new GestureDetector(getContext(),new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                float x = e.getX();
                float y = e.getY();
                float scale = getScale();

                if (scale < mTouchScale){

                    //放大的逻辑---注意要渐变
                    if (!isClick){
                        isClick = true;
                        postDelayed(new ChangeRunnable(mTouchScale,x,y),16);
                    }

                }else {

                    if (!isClick){
                        isClick = true;
                        //缩小的逻辑---注意要渐变
                        postDelayed(new ChangeRunnable(minScale,x,y),16);
                    }



                }


                return true;
            }
        });

同样通过getScale来获取当前的scale,通过scale来控制放大和缩小(以上代码逻辑一目了然);但是这样的放大缩小比较迅速所以要想提高交互需要设置线程来控制放缩的梯度来给双击放缩带来一个缓冲:

 public class ChangeRunnable implements Runnable{

        //目标值
        private float mTargetScale;
        //放缩的中心
        private float x;
        private float y;
        //放缩的梯度
        private float mTemp;
        private final float UP = 1.1f;
        private final float DOWN = 0.6f;

        public ChangeRunnable(float mTargetScale,float x,float y) {
            this.mTargetScale = mTargetScale;
            this.x = x;
            this.y = y;
            float scale = getScale();
            if (scale < mTargetScale){
                mTemp = UP;
            }
            if (scale > mTargetScale){
                mTemp = DOWN;
            }
        }

        @Override
        public void run() {
            if (!isClick)
                return;


            float currScale = getScale();

            if ((mTemp > 1.0f && currScale < mTargetScale) || (mTemp < 1.0f && currScale > mTargetScale)){
                //scale是在原图基础上来计算的
                mMatrix.postScale(mTemp,mTemp,x,y);
                transBoderAndCenter();
                setImageMatrix(mMatrix);

                postDelayed(this,16);
            }else if ((mTemp > 1.0f && currScale >= mTargetScale) || (mTemp < 1.0f && currScale <= mTargetScale)){
                mMatrix.postScale(mTargetScale/currScale,mTargetScale/currScale,x,y);
                transBoderAndCenter();
                setImageMatrix(mMatrix);

                isClick = false;
            }




        }
    }

这样双击放缩的功能也已经完成了;

5.设置viewPager滑动,并且解决滑动冲突:

viewPager在xml中的添加和setAdapter比较常见没有什么问题,那么我们直接来看滑动事件的冲突解决;
在onTouch中通过判断图片宽高和screen的对比来控制父类的拦截;即当图片款或着高大于screen的时候请求父类不拦截:

switch (motionEvent.getAction()){
            case MotionEvent.ACTION_DOWN:

                if (rect.width() > getWidth()){
                    if (getParent() instanceof ViewPager){
                        //请求父类不拦截
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }

                break;


            case MotionEvent.ACTION_MOVE:


                if (rect.width() > getWidth()){
                    if (getParent() instanceof ViewPager){
                        //请求父类不拦截
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }

这样多点触控放缩兼具滑动和双击放缩,以及流畅viewpager滑动的图片预览就大功告成了;

6完整代码

//MainActivity

package com.example.customimg;

import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import com.example.customimg.adapter.MyAdpter;

public class MainActivity extends AppCompatActivity {

    private ViewPager mViewPager;
    //初始化图片集合
    private int[] mResId = new int[]{R.drawable.img_1,R.drawable.img_2,R.drawable.img_3};
    //初始化iv集合
    private ImageView[] mIvs = new ImageView[mResId.length];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initEvent();
    }

    private void initView() {
        mViewPager = (ViewPager) findViewById(R.id.viewPager);
    }

    private void initEvent() {
        mViewPager.setAdapter(new MyAdpter(this,mResId,mIvs));


    }
}

//ZoomImageView

package com.example.customimg.widget;

import android.content.Context;
import android.content.Loader;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
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;


/**
 * Created by houruixiang on 2017/8/22.
 */

public class ZoomImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener
        ,View.OnTouchListener, ScaleGestureDetector.OnScaleGestureListener {

    private boolean mOnce;
    private float minScale;
    private float maxScale;
    private float mTouchScale;
    private Matrix mMatrix;
    /**捕获多指触摸的手势*/
    private ScaleGestureDetector mScaleGestureDetector;
    private Object rectF;



    /**------------------------------------------------
     * 自由移动*/
    private float mLastX;
    private float mLastY;

    private boolean isMoveX;
    private boolean isMoveY;

    private boolean isMove;

    private int mPointCount;
    private int mTouchslop;



    /**------------------------------------------------
     * 双击放大+缩小*/
    private GestureDetector mGes;
    private boolean isClick;

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

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

    public ZoomImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {


        mMatrix = new Matrix();
        setScaleType(ScaleType.MATRIX);
        mScaleGestureDetector = new ScaleGestureDetector(getContext(), this);
        setOnTouchListener(this);
        mTouchslop = ViewConfiguration.get(getContext()).getScaledTouchSlop();

        mGes = new GestureDetector(getContext(),new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                float x = e.getX();
                float y = e.getY();
                float scale = getScale();

                if (scale < mTouchScale){

                    Log.i("==currScale",String.valueOf(mTouchScale) + ":::" + String.valueOf(scale));
                    //放大的逻辑---注意要渐变
                    if (!isClick){
                        isClick = true;
                        postDelayed(new ChangeRunnable(mTouchScale,x,y),16);
                    }

                }else {

                    Log.i("==currScale2",String.valueOf(minScale) + ":::" + String.valueOf(scale));
                    if (!isClick){
                        isClick = true;
                        //缩小的逻辑---注意要渐变
                        postDelayed(new ChangeRunnable(minScale,x,y),16);
                    }



                }


                return true;
            }
        });

    }

    public class ChangeRunnable implements Runnable{

        //目标值
        private float mTargetScale;
        //放缩的中心
        private float x;
        private float y;
        //放缩的梯度
        private float mTemp;
        private final float UP = 1.1f;
        private final float DOWN = 0.6f;

        public ChangeRunnable(float mTargetScale,float x,float y) {
            this.mTargetScale = mTargetScale;
            this.x = x;
            this.y = y;
            float scale = getScale();
            if (scale < mTargetScale){
                mTemp = UP;
            }
            if (scale > mTargetScale){
                mTemp = DOWN;
            }
        }

        @Override
        public void run() {
            if (!isClick)
                return;


            float currScale = getScale();

            if ((mTemp > 1.0f && currScale < mTargetScale) || (mTemp < 1.0f && currScale > mTargetScale)){
                //scale是在原图基础上来计算的
                mMatrix.postScale(mTemp,mTemp,x,y);
                transBoderAndCenter();
                setImageMatrix(mMatrix);

                postDelayed(this,16);
            }else if ((mTemp > 1.0f && currScale >= mTargetScale) || (mTemp < 1.0f && currScale <= mTargetScale)){
                mMatrix.postScale(mTargetScale/currScale,mTargetScale/currScale,x,y);
                transBoderAndCenter();
                setImageMatrix(mMatrix);

                isClick = false;
            }




        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        //注册监听 绘制完成设置图片
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);

    }

    @Override
    public void onGlobalLayout() {

        if (!mOnce){
            float scale = 1;
            //获取控件的宽和高
            int width = getWidth();
            int height = getHeight();
            //获取图片 及其宽和高
            Drawable drawable = getDrawable();
            if (drawable == null)
                return;
            int intrinsicWidth = drawable.getIntrinsicWidth();
            int intrinsicHeight = drawable.getIntrinsicHeight();

            //控件的宽高和img比较确定缩放的比例
            if (intrinsicWidth > width && intrinsicHeight < height){
                scale = width * 1.0f / intrinsicWidth;
            }
            if (intrinsicHeight > height && intrinsicWidth < width){
                scale = height * 1.0f / intrinsicHeight;
            }
            if ((intrinsicHeight > height && intrinsicWidth > width) || (intrinsicHeight < height && intrinsicWidth < width)){
                scale =  Math.min(height * 1.0f / intrinsicHeight ,width * 1.0f / intrinsicWidth);
            }

            minScale = scale;
            maxScale = 4 * minScale;
            mTouchScale = 2 * minScale;
            float dx = width/2 - intrinsicWidth/2;
            float dy = height/2 - intrinsicHeight/2;
            mMatrix.postTranslate(dx,dy);
            mMatrix.postScale(minScale,minScale,width/2,height/2);

            setImageMatrix(mMatrix);




            mOnce =true;
        }
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {


        if (mGes.onTouchEvent(motionEvent)){
            return true;
        }

        mScaleGestureDetector.onTouchEvent(motionEvent);

        /**-------------------------------------------
         * 设置自由移动*/

        float x = 0;
        float y = 0;

        int pointerCount = motionEvent.getPointerCount();
        for (int i = 0; i < pointerCount; i++) {
            x += motionEvent.getX(i);
            y += motionEvent.getY(i);
        }

        x /= pointerCount;
        y /= pointerCount;


        if (mPointCount != pointerCount){
            mLastX = x;
            mLastY = y;
            isMove = false;
        }

        mPointCount = pointerCount;

        RectF rect = getRectF();


        switch (motionEvent.getAction()){
            case MotionEvent.ACTION_DOWN:

                if (rect.width() > getWidth()){
                    if (getParent() instanceof ViewPager){
                        //请求父类不拦截
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                }

                break;


            case MotionEvent.ACTION_MOVE:


                if (rect.width() > getWidth()){
                    if (getParent() instanceof ViewPager){
                        //请求父类不拦截
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }

                isMoveX = true;
                isMoveY = true;
                float mx = 0;
                float my = 0;


                int count = motionEvent.getPointerCount();
                for (int i = 0; i < count; i++) {
                    mx += motionEvent.getX(i);
                    my += motionEvent.getY(i);
                }
                    mx /= count;
                    my /= count;
                float dx = mx - mLastX;
                float dy = my - mLastY;


                if (!isMove){
                   isMove = isMoveZoom(dx, dy);
                }

                if (isMove){
                    RectF rectF = getRectF();
                    float w = rectF.width();
                    float h = rectF.height();
                    int width = getWidth();
                    int height = getHeight();

                    if (getDrawable() != null){
                        if (w < width){
                            isMoveX = false;
                            dx = 0;
                        }
                        if (h < height){
                            isMoveY = false;
                            dy = 0;
                        }
                        mMatrix.postTranslate(dx,dy);

                        transBoder();
                        setImageMatrix(mMatrix);
                    }

                }

                mLastX = x;
                mLastY = y;

            }

                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mPointCount = 0;
                break;
        }


        //这不会拦截么?
        return true;
    }

    private void transBoder() {
        //设置x和y的边界值
        float Dx = 0;
        float Dy = 0;
        RectF rectF = getRectF();
        int width = getWidth();
        int height = getHeight();

        float l = rectF.left;
        float r = rectF.right;
        float t = rectF.top;
        float b = rectF.bottom;


        float w = rectF.width();
        float h = rectF.height();

        if (l > 0 && isMoveX){
            Dx = -l;
        }
        if (r < width && isMoveX){
            Dx = width - r;
        }

        if (t > 0 && isMoveY){
            Dy = -t;
        }
        if (b < height && isMoveY){
            Dy = height - b;
        }

        mMatrix.postTranslate(Dx,Dy);
    }

    private boolean isMoveZoom(float dx, float dy) {
        return Math.sqrt(dx * dx + dy * dy) > mTouchslop;
    }


    //放缩过程中
    @Override
    public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

        float scale = getScale();
        float scaleFactor = scaleGestureDetector.getScaleFactor();

        //判断drawable是否为空
        if (getDrawable() == null)
            return true;
        //判断范围
        if ((scale < maxScale && scale >= 1.0f) || (scale > minScale && scale <= 1.0f)){
            //Log.i("==onScale",String.valueOf(scale) + ":::" + String.valueOf(scaleFactor) + ":::" + String.valueOf(maxScale));
            if (scale * scaleFactor > maxScale){
                //Log.i("==scaleFactor",String.valueOf(scale * scaleFactor));
                    scaleFactor = maxScale/scale;
                }
            if (scale * scaleFactor < minScale){
                    scaleFactor = minScale/scale;
                }
        }
        /**
         * 限定图片放缩的最大值和最小值
         * */
        else if (scale >= maxScale && scaleFactor > 1.0f){
            scaleFactor = maxScale/scale;
        }else if (scale <= minScale/2 && scaleFactor < 1.0f){
            //Log.i("==scale",String.valueOf(scaleFactor));
            scaleFactor = minScale/(2*scale);
        }

        //Log.i("==scaleFactor 333",String.valueOf(scale * scaleFactor));

        //注意这里要以手势中心为缩放中心
        mMatrix.postScale(scaleFactor,scaleFactor,
                scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY());

        transBoderAndCenter();

        setImageMatrix(mMatrix);

        return true;
    }


    //在缩放时候进行边界控制和中心点的控制
    private void transBoderAndCenter() {
        //设置x和y的边界值
        float Dx = 0;
        float Dy = 0;
        RectF rectF = getRectF();
        int width = getWidth();
        int height = getHeight();

        float l = rectF.left;
        float r = rectF.right;
        float t = rectF.top;
        float b = rectF.bottom;


        float w = rectF.width();
        float h = rectF.height();

        if (w >= width){
            if (l > 0){
                Dx = -l;
            }
            if (r < width){
                Dx = width - r;
            }
        }

        if (h >= height){
            if (t > 0){
                Dy = -t;
            }
            if (b < height){
                Dy = height - b;
            }
        }

        if (w < width){
            Dx = width/2 - r + w/2;
        }
        if (h < height){
            Dy = height/2 - b + h/2;
        }

        mMatrix.postTranslate(Dx,Dy);


    }

    private float getScale() {
        float[] values = new float[9];
        mMatrix.getValues(values);
        float value = values[Matrix.MSCALE_X];
        return value;
    }

    //放缩开始时候
    @Override
    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
        return true;
    }


    //放缩结束时候
    @Override
    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

    }


    //实时获取drawable尺寸的监听
    public RectF getRectF() {

        Matrix matrix = mMatrix;
        RectF rectF = new RectF();
        Drawable drawable = getDrawable();
        if (drawable != null){
            rectF.set(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());
            matrix.mapRect(rectF);
        }


        return rectF;
    }
}

//Mydapter

package com.example.customimg.adapter;

import android.app.Activity;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.example.customimg.MainActivity;
import com.example.customimg.widget.ZoomImageView;

/**
 * Created by houruixiang on 2017/8/23.
 */

public class MyAdpter extends PagerAdapter {


    private final ImageView[] mIvs;
    private final Activity mActivity;
    private final int[] mResId;

    public MyAdpter(Activity mainActivity, int[] mResId, ImageView[] mIvs) {
        this.mActivity = mainActivity;
        this.mResId = mResId;
        this.mIvs = mIvs;
    }

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ZoomImageView zoomImageView = new ZoomImageView(mActivity);
        zoomImageView.setImageResource(mResId[position]);
        container.addView(zoomImageView);
        mIvs[position] = zoomImageView;

        return zoomImageView;
    }

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

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

//layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.customimg.MainActivity"
    android:gravity="center">


    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />



</LinearLayout>

感谢阅读,期待一起进步,谢谢.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值