Android 图片随着手势缩放,平移,并且支持多点触控

效果图:

现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~

想要做到图片支持多点触控,自由的进行缩放、平移,需要了解几个知识点:Matrix , GestureDetector , ScaleGestureDetector 以及事件分发机制

1、Matrix

参考:http://geek.csdn.net/news/detail/89873   http://blog.csdn.net/flash129/article/details/8234599  http://blog.csdn.net/cquwentao/article/details/51445269

矩阵,看深入了都是3维矩阵的乘啊什么的,怪麻烦的~~

其实这么了解下就行了:

Matrix

数据结构:3维矩阵;

内部存储:new Float[9] ; 内部就是个一维数组,内部9个元素;可以进行setValues(float[] values)进行初始化

每个元素代表的意思

{  
        MSCALE_X, MSKEW_X, MTRANS_X,    
                MSKEW_Y, MSCALE_Y, MTRANS_Y,    
                MPERSP_0, MPERSP_1, MPERSP_2    
};   
字面上,应该能看出来哪个代表x方向缩放,哪个代表垂直方向的偏移量吧~~有不认识的3个,没事,请无视。

操作

比如你想要设置matrix的偏移量为200,100

你可以这么写:

    Matrix transMatrix = new Matrix();  
            float[] values = new float[] { 1.0, 0, 200, 0, 1.0, 100, 0, 0, 1.0 };  
            transMatrix.setValues(values);  
Android 图片随着手势缩放,平移,并且支持多点触控_第1张图片

如何获取值

当然了,我们对一个Matrix进行了各种操作,一会postScale,一会postTranslate;那么现在如何获得当前的缩放比例:
前面说setValues可以初始化,那么getValues就能拿到当前矩阵的值,拿到的是个一维数组,9个元素;再通过下标取对应值就可以。

比如我想知道现在x方向缩放比例:

    public final float getScale()  
        {  
            scaleMatrix.getValues(matrixValues);  
            return matrixValues[Matrix.MSCALE_X];  
        }  

好了,知道这些就够了~~

2、GestureDetector

参考:http://blog.csdn.net/xyz_lmn/article/details/16826669  http://blog.csdn.net/hpk1994/article/details/51224228

嗯,自己看API,能够捕捉到长按、双击什么的;用法会在例子中

3、ScaleGestureDetector 

参考:http://blog.csdn.net/u010410408/article/details/39577399 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0211/2467.html  

http://blog.csdn.net/true100/article/details/51141496

嗯,有点像继承来的,其实不是的,独立的一个类~用于检测缩放的手势~~~用法会在例子中

4、完整代码:

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

    private static final String TAG = ZoomImageView.class.getSimpleName();
    public static final float SCALE_MAX = 3.0f;
    private static final float SCALE_MID = 1.5f;

    /**
     * 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0
     */
    private float initScale = 1.0f;
    private boolean once = true;

    /**
     * 用于存放矩阵的9个值
     */
    private final float[] matrixValues = new float[9];

    /**
     * 缩放的手势检测
     */
    private ScaleGestureDetector mScaleGestureDetector = null;
    private final Matrix mScaleMatrix = new Matrix();

    /**
     * 用于双击检测
     */
    private GestureDetector mGestureDetector;
    private boolean isAutoScale;

    private int mTouchSlop;

    private float mLastX;
    private float mLastY;

    private boolean isCanDrag;
    private int lastPointerCount;

    private boolean isCheckTopAndBottom = true;
    private boolean isCheckLeftAndRight = true;

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

    public ZoomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setScaleType(ScaleType.MATRIX);
        mGestureDetector = new GestureDetector(context, new SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                if (isAutoScale == true)
                    return true;

                float x = e.getX();
                float y = e.getY();
                Log.e("DoubleTap", getScale() + " , " + initScale);
                if (getScale() < SCALE_MID) {
                    //postDelayed(); 16 :多久实现一次的定时器操作
                    ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MID, x, y), 16);
                    isAutoScale = true;
                } /*else if (getScale() >= SCALE_MID  //连续双击放大 可放开
                        && getScale() < SCALE_MAX) {
                    ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MAX, x, y), 16);
                    isAutoScale = true;
                }*/ else {
                    ZoomImageView.this.postDelayed(new AutoScaleRunnable(initScale, x, y), 16);
                    isAutoScale = true;
                }

                return true;
            }
        });
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
        this.setOnTouchListener(this);
    }

    /**
     * 自动缩放的任务
     * @author zhy
     */
    private class AutoScaleRunnable implements Runnable {
        static final float BIGGER = 1.07f;
        static final float SMALLER = 0.93f;
        private float mTargetScale;
        private float tmpScale;

        /**
         * 缩放的中心
         */
        private float x;
        private float y;

        /**
         * 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小
         * @param targetScale
         */
        public AutoScaleRunnable(float targetScale, float x, float y) {
            this.mTargetScale = targetScale;
            this.x = x;
            this.y = y;
            if (getScale() < mTargetScale) {
                tmpScale = BIGGER;
            } else {
                tmpScale = SMALLER;
            }
        }

        @Override
        public void run() {
            // 进行缩放
            mScaleMatrix.postScale(tmpScale, tmpScale, x, y);
            checkBorderAndCenterWhenScale();
            setImageMatrix(mScaleMatrix);

            final float currentScale = getScale();
            // 如果值在合法范围内,继续缩放
            if (((tmpScale > 1f) && (currentScale < mTargetScale)) || ((tmpScale < 1f) && (mTargetScale < currentScale))) {

                ZoomImageView.this.postDelayed(this, 16);
            } else {
                // 设置为目标的缩放比例
                final float deltaScale = mTargetScale / currentScale;
                mScaleMatrix.postScale(deltaScale, deltaScale, x, y);
                checkBorderAndCenterWhenScale();
                setImageMatrix(mScaleMatrix);
                isAutoScale = false;
            }
        }
    }

    /**
     * 对图片进行缩放的控制,首先进行缩放范围的判断,然后设置mScaleMatrix的scale值
     * @param detector
     * @return
     */
    @SuppressLint("NewApi")
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float scale = getScale();
        float scaleFactor = detector.getScaleFactor();

        if (getDrawable() == null)
            return true;

        /**
         * 缩放的范围控制
         */
        if ((scale < SCALE_MAX && scaleFactor > 1.0f) || (scale > initScale && scaleFactor < 1.0f)) {
            /**
             * 最大值最小值判断
             */
            if (scaleFactor * scale < initScale) {
                scaleFactor = initScale / scale;
            }
            if (scaleFactor * scale > SCALE_MAX) {
                scaleFactor = SCALE_MAX / scale;
            }
            /**
             * 设置缩放比例
             */
            mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.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.bottom;
            }
        }
        // 如果宽或高小于屏幕,则让其居中
        if (rect.width() < width) {
            deltaX = width * 0.5f - rect.right + 0.5f * rect.width();
        }
        if (rect.height() < height) {
            deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();
        }
        Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY);

        mScaleMatrix.postTranslate(deltaX, deltaY);

    }

    /**
     * 根据当前图片的Matrix获得图片的范围
     * @return
     */
    private RectF getMatrixRectF() {
        Matrix matrix = mScaleMatrix;
        RectF rect = new RectF();
        Drawable d = getDrawable();
        if (null != d) {
            rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            matrix.mapRect(rect);
        }
        return rect;
    }

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

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
    }

    /**
     * 我们让OnTouchListener的MotionEvent交给ScaleGestureDetector进行处理
     * public boolean onTouch(View v, MotionEvent event){
     * return mScaleGestureDetector.onTouchEvent(event);
     * }
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        if (mGestureDetector.onTouchEvent(event))
            return true;
        mScaleGestureDetector.onTouchEvent(event);

        float x = 0, y = 0;
        // 拿到触摸点的个数
        final int pointerCount = event.getPointerCount();
        // 得到多个触摸点的x与y均值
        for (int i = 0; i < pointerCount; i++) {
            x += event.getX(i);
            y += event.getY(i);
        }
        x = x / pointerCount;
        y = y / pointerCount;

        /**
         * 每当触摸点发生变化时,重置mLasX , mLastY
         */
        if (pointerCount != lastPointerCount) {
            isCanDrag = false;
            mLastX = x;
            mLastY = y;
        }

        lastPointerCount = pointerCount;
        RectF rectF = getMatrixRectF();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (rectF.width() > getWidth() || rectF.height() > getHeight()) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
                Log.e(TAG, "ACTION_MOVE");
                float dx = x - mLastX;
                float dy = y - mLastY;

                if (!isCanDrag) {
                    isCanDrag = isCanDrag(dx, dy);
                }
                if (isCanDrag) {

                    if (getDrawable() != null) {
                        // if (getMatrixRectF().left == 0 && dx > 0)
                        // {
                        // getParent().requestDisallowInterceptTouchEvent(false);
                        // }
                        //
                        // if (getMatrixRectF().right == getWidth() && dx < 0)
                        // {
                        // getParent().requestDisallowInterceptTouchEvent(false);
                        // }
                        isCheckLeftAndRight = isCheckTopAndBottom = true;
                        // 如果宽度小于屏幕宽度,则禁止左右移动
                        if (rectF.width() < getWidth()) {
                            dx = 0;
                            isCheckLeftAndRight = false;
                        }
                        // 如果高度小雨屏幕高度,则禁止上下移动
                        if (rectF.height() < getHeight()) {
                            dy = 0;
                            isCheckTopAndBottom = false;
                        }

                        //设置偏移量
                        mScaleMatrix.postTranslate(dx, dy);
                        //再次校验
                        checkMatrixBounds();
                        setImageMatrix(mScaleMatrix);
                    }
                }
                mLastX = x;
                mLastY = y;
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                Log.e(TAG, "ACTION_UP");
                lastPointerCount = 0;
                break;
        }
        return true;
    }

    /**
     * 获得当前的缩放比例
     * @return
     */
    public final float getScale() {
        mScaleMatrix.getValues(matrixValues);
        return matrixValues[Matrix.MSCALE_X];
    }

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

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

    /**
     * 根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心。
     * 如果图片很小,那就正常显示,不放大了~
     */
    @Override
    public void onGlobalLayout() {
        if (once) {
            Drawable d = getDrawable();
            if (d == null)
                return;
            Log.e(TAG, d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());
            int width = getWidth();
            int height = getHeight();
            // 拿到图片的宽和高
            int dw = d.getIntrinsicWidth();
            int dh = d.getIntrinsicHeight();
            float scale = 1.0f;
            // 如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高
            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) {
                scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);
            }
            initScale = scale;

            Log.e(TAG, "initScale = " + initScale);
            mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);
            mScaleMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);
            // 图片移动至屏幕中心
            setImageMatrix(mScaleMatrix);
            once = false;
        }
    }

    /**
     * 移动时,进行边界判断,主要判断宽或高大于屏幕的
     */
    private void checkMatrixBounds() {
        RectF rect = getMatrixRectF();

        float deltaX = 0, deltaY = 0;
        final float viewWidth = getWidth();
        final float viewHeight = getHeight();
        // 判断移动或缩放后,图片显示是否超出屏幕边界
        if (rect.top > 0 && isCheckTopAndBottom) {
            deltaY = -rect.top;
        }
        if (rect.bottom < viewHeight && isCheckTopAndBottom) {
            deltaY = viewHeight - rect.bottom;
        }
        if (rect.left > 0 && isCheckLeftAndRight) {
            deltaX = -rect.left;
        }
        if (rect.right < viewWidth && isCheckLeftAndRight) {
            deltaX = viewWidth - rect.right;
        }
        mScaleMatrix.postTranslate(deltaX, deltaY);
    }

    /**
     * 是否是推动行为
     *
     * @param dx
     * @param dy
     * @return
     */
    private boolean isCanDrag(float dx, float dy) {
        return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
    }

}

1、我们在onGlobalLayout的回调中,根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心。如果图片很小,那就正常显示,不放大了~

2、我们让OnTouchListener的MotionEvent交给ScaleGestureDetector进行处理

    @Override  
        public boolean onTouch(View v, MotionEvent event)  
        {  
            return mScaleGestureDetector.onTouchEvent(event);  
      
        }  

3、在onScale的回调中对图片进行缩放的控制,首先进行缩放范围的判断,然后设置mScaleMatrix的scale值

现在的效果:

1、小于屏幕的宽和高

Android 图片随着手势缩放,平移,并且支持多点触控_第2张图片
2、大于屏幕的宽和高


可是,可是,存在问题:

1、缩放的中心点,我们设置是固定的,屏幕中间

2、放大后,无法移动~

下面,我们先解决缩放的中心点问题,不能一直按屏幕中心么,像我这样的,我比较关注妹子的眼睛,我要放大那一块~~~

2、设置缩放中心

1、单纯的设置缩放中心

仅仅是设置中心很简单,直接修改下中心点 :

    /** 
                 * 设置缩放比例 
                 */  
                mScaleMatrix.postScale(scaleFactor, scaleFactor,  
                        detector.getFocusX(), detector.getFocusX());  
                setImageMatrix(mScaleMatrix);  

但是,随意的中心点放大、缩小,会导致图片的位置的变化,最终导致,图片宽高大于屏幕时,图片与屏幕间出现白边;图片小于屏幕,但是不居中。

2、控制缩放时图片显示的范围

所以我们在缩放的时候需要手动控制下范围:


    /** 
         * 在缩放时,进行图片显示范围的控制 
         */  
        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 * 0.5f - rect.right + 0.5f * rect.width();  
            }  
            if (rect.height() < height)  
            {  
                deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();  
            }  
            Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY);  
      
            mScaleMatrix.postTranslate(deltaX, deltaY);  
      
        }  
      
        /** 
         * 根据当前图片的Matrix获得图片的范围 
         *  
         * @return 
         */  
        private RectF getMatrixRectF()  
        {  
            Matrix matrix = mScaleMatrix;  
            RectF rect = new RectF();  
            Drawable d = getDrawable();  
            if (null != d)  
            {  
                rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());  
                matrix.mapRect(rect);  
            }  
            return rect;  
        }  
在onScale里面记得调用:
/** 
             * 设置缩放比例 
             */  
            mScaleMatrix.postScale(scaleFactor, scaleFactor,  
                    detector.getFocusX(), detector.getFocusY());  
            checkBorderAndCenterWhenScale();  
            setImageMatrix(mScaleMatrix); 

这样就好了,可以自由的放大任何地方,并且不会出现边界出现白边,也能很好的让图片显示在屏幕中间(当图片宽或高小于屏幕);


下面介绍:多点触控时移动,双击变大,双击变小

1.自由的进行移动

我们在onTouchEvent里面,加上移动的代码,当然了,必须长或宽大于屏幕才可以移动~~~

    @Override  
        public boolean onTouch(View v, MotionEvent event)  
        {  
            mScaleGestureDetector.onTouchEvent(event);  
      
            float x = 0, y = 0;  
            // 拿到触摸点的个数  
            final int pointerCount = event.getPointerCount();  
            // 得到多个触摸点的x与y均值  
            for (int i = 0; i < pointerCount; i++)  
            {  
                x += event.getX(i);  
                y += event.getY(i);  
            }  
            x = x / pointerCount;  
            y = y / pointerCount;  
      
            /** 
             * 每当触摸点发生变化时,重置mLasX , mLastY  
             */  
            if (pointerCount != lastPointerCount)  
            {  
                isCanDrag = false;  
                mLastX = x;  
                mLastY = y;  
            }  
              
      
            lastPointerCount = pointerCount;  
      
            switch (event.getAction())  
            {  
            case MotionEvent.ACTION_MOVE:  
                Log.e(TAG, "ACTION_MOVE");  
                float dx = x - mLastX;  
                float dy = y - mLastY;  
                  
                if (!isCanDrag)  
                {  
                    isCanDrag = isCanDrag(dx, dy);  
                }  
                if (isCanDrag)  
                {  
                    RectF rectF = getMatrixRectF();  
                    if (getDrawable() != null)  
                    {  
                        isCheckLeftAndRight = isCheckTopAndBottom = true;  
                        // 如果宽度小于屏幕宽度,则禁止左右移动  
                        if (rectF.width() < getWidth())  
                        {  
                            dx = 0;  
                            isCheckLeftAndRight = false;  
                        }  
                        // 如果高度小雨屏幕高度,则禁止上下移动  
                        if (rectF.height() < getHeight())  
                        {  
                            dy = 0;  
                            isCheckTopAndBottom = false;  
                        }  
                        mScaleMatrix.postTranslate(dx, dy);  
                        checkMatrixBounds();  
                        setImageMatrix(mScaleMatrix);  
                    }  
                }  
                mLastX = x;  
                mLastY = y;  
                break;  
      
            case MotionEvent.ACTION_UP:  
            case MotionEvent.ACTION_CANCEL:  
                Log.e(TAG, "ACTION_UP");  
                lastPointerCount = 0;  
                break;  
            }  
      
            return true;  
        }  
首先我们拿到触摸点的数量,然后求出多个触摸点的平均值,设置给我们的mLastX , mLastY , 然后在移动的时候,得到dx ,dy 进行范围检查以后,调用mScaleMatrix.postTranslate进行设置偏移量,当然了,设置完成以后,还需要再次校验一下,不能把图片移动的与屏幕边界出现白边,校验完成后,调用setImageMatrix.

这里:需要注意一下,我们没有复写ACTION_DOWM,是因为,ACTION_DOWN在多点触控的情况下,只要有一个手指按下状态,其他手指按下不会再次触发ACTION_DOWN,但是多个手指以后,触摸点的平均值会发生很大变化,所以我们没有用到ACTION_DOWN。每当触摸点的数量变化,我们就会跟新当前的mLastX,mLastY.

下面是上面用到的两个私有方法,一个用于检查边界,一个用于判断是否是拖动的操作:

    /** 
         * 移动时,进行边界判断,主要判断宽或高大于屏幕的 
         */  
        private void checkMatrixBounds()  
        {  
            RectF rect = getMatrixRectF();  
      
            float deltaX = 0, deltaY = 0;  
            final float viewWidth = getWidth();  
            final float viewHeight = getHeight();  
            // 判断移动或缩放后,图片显示是否超出屏幕边界  
            if (rect.top > 0 && isCheckTopAndBottom)  
            {  
                deltaY = -rect.top;  
            }  
            if (rect.bottom < viewHeight && isCheckTopAndBottom)  
            {  
                deltaY = viewHeight - rect.bottom;  
            }  
            if (rect.left > 0 && isCheckLeftAndRight)  
            {  
                deltaX = -rect.left;  
            }  
            if (rect.right < viewWidth && isCheckLeftAndRight)  
            {  
                deltaX = viewWidth - rect.right;  
            }  
            mScaleMatrix.postTranslate(deltaX, deltaY);  
        }  
      
        /** 
         * 是否是推动行为 
         *  
         * @param dx 
         * @param dy 
         * @return 
         */  
        private boolean isCanDrag(float dx, float dy)  
        {  
            return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;  
        }  
这样,我们就可以快乐的放大、缩小加移动了~~~


2.双击放大缩小

谈到双击事件,我们的GestureDetector终于要登场了,这哥们可以捕获双击事件~~

1.GestureDetector的使用:

因为GestureDetector设置监听器的话,方法一大串,而我们只需要onDoubleTap这个回调,所以我们准备使用它的一个内部类SimpleOnGestureListener,对接口的其他方法实现了空实现。

不过还有几个问题需要讨论下,才能开始我们的代码:

1、我们双击尺寸如何变化?

我是这样的,根据当前的缩放值,如果是小于2的,我们双击直接到变为原图的2倍;如果是2,4之间的,我们双击直接为原图的4倍;其他状态也就是4倍,双击后还原到最初的尺寸。

如果你觉得这样不合适,可以根据自己的爱好调整。

2、我们双击变化,需要一个动画~~比如我们上例的演示图,图片很大,全屏显示的时候initScale=0.5左后,如果双击后变为2,也就是瞬间大了四倍,没有一个过渡的效果的话,给用户的感觉会特别差。所以,我们准备使用postDelay执行一个Runnable,Runnable中再次根据的当然的缩放值继续执行。

首先我们在构造方法中,完成对GestureDetector的初始化,以及设置onDoubleTap监听

    public ZoomImageView(Context context, AttributeSet attrs)  
        {  
            super(context, attrs);  
            mScaleGestureDetector = new ScaleGestureDetector(context, this);  
            mGestureDetector = new GestureDetector(context,  
                    new SimpleOnGestureListener()  
                    {  
                        @Override  
                        public boolean onDoubleTap(MotionEvent e)  
                        {  
                            if (isAutoScale == true)  
                                return true;  
      
                            float x = e.getX();  
                            float y = e.getY();  
                            Log.e("DoubleTap", getScale() + " , " + initScale);  
                            if (getScale() < SCALE_MID)  
                            {  
                                ZoomImageView.this.postDelayed(  
                                        new AutoScaleRunnable(SCALE_MID, x, y), 16);  
                                isAutoScale = true;  
                            } else if (getScale() >= SCALE_MID  
                                    && getScale() < SCALE_MAX)  
                            {  
                                ZoomImageView.this.postDelayed(  
                                        new AutoScaleRunnable(SCALE_MAX, x, y), 16);  
                                isAutoScale = true;  
                            } else  
                            {  
                                ZoomImageView.this.postDelayed(  
                                        new AutoScaleRunnable(initScale, x, y), 16);  
                                isAutoScale = true;  
                            }  
      
                            return true;  
                        }  
                    });  
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
            super.setScaleType(ScaleType.MATRIX);  
            this.setOnTouchListener(this);  
        }  

1、当双击的时候,首先判断是否正在自动缩放,如果在,直接retrun ; 

2、然后就进入了我们的if,如果当然是scale小于2,则通过view.发送一个Runnable进行执行;其他类似;

下面看我们的Runnable的代码:

    /** 
         * 自动缩放的任务 
         */  
        private class AutoScaleRunnable implements Runnable  
        {  
            static final float BIGGER = 1.07f;  
            static final float SMALLER = 0.93f;  
            private float mTargetScale;  
            private float tmpScale;  
      
            /** 
             * 缩放的中心 
             */  
            private float x;  
            private float y;  
      
            /** 
             * 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小 
             *  
             * @param targetScale 
             */  
            public AutoScaleRunnable(float targetScale, float x, float y)  
            {  
                this.mTargetScale = targetScale;  
                this.x = x;  
                this.y = y;  
                if (getScale() < mTargetScale)  
                {  
                    tmpScale = BIGGER;  
                } else  
                {  
                    tmpScale = SMALLER;  
                }  
      
            }  
      
            @Override  
            public void run()  
            {  
                // 进行缩放  
                mScaleMatrix.postScale(tmpScale, tmpScale, x, y);  
                checkBorderAndCenterWhenScale();  
                setImageMatrix(mScaleMatrix);  
      
                final float currentScale = getScale();  
                //如果值在合法范围内,继续缩放  
                if (((tmpScale > 1f) && (currentScale < mTargetScale))  
                        || ((tmpScale < 1f) && (mTargetScale < currentScale)))  
                {  
                    ZoomImageView.this.postDelayed(this, 16);  
                } else//设置为目标的缩放比例  
                {  
                    final float deltaScale = mTargetScale / currentScale;  
                    mScaleMatrix.postScale(deltaScale, deltaScale, x, y);  
                    checkBorderAndCenterWhenScale();  
                    setImageMatrix(mScaleMatrix);  
                    isAutoScale = false;  
                }  
      
            }  
        }  

代码写完了,我们依然需要把我们的event传给它,依然是在onTouch方法:

    @Override  
        public boolean onTouch(View v, MotionEvent event)  
        {  
            if (mGestureDetector.onTouchEvent(event))  
                return true;  

连续双击放大,感觉不爽,代码已经上传,我就不重传了,如果你也觉得不爽,可以自行注释。

//                      else if (getScale() >= SCALE_MID  
//                              && getScale() < SCALE_MAX)  
//                      {  
//                          ZoomImageView.this.postDelayed(  
//                                  new AutoScaleRunnable(SCALE_MAX, x, y), 16);  
//                          isAutoScale = true;  
//                      }  

源码:http://download.csdn.net/download/lijinweii/9957495

你可能感兴趣的:(Android 图片随着手势缩放,平移,并且支持多点触控)