Android 实现可以自由移动缩放的图片控件

一、概述:

1、需要实现的功能
1)可以自由的缩放
2)双击可以缩放
3)自动边界检测
4)放大后可以自由的移动
5)处理与viewpager之间的事件冲突

2、需要用到的技术
1)Handle事件分发机制
2)Matrix矩阵
3)ScaleGuestureDetector:手势缩放
4)GuestureDetector:手势移动
5)postdelay + runnable:实现缩放动画
6)根据矩阵Matrix获得x,y轴的缩放值
7)放大后的图片宽度大于屏幕宽度时,父控件消费掉事件

if (rectF.width() > getWidth()) {
    getParent().requestDisallowInterceptTouchEvent(true);
}

3、如何实现?
通过继承ImageView来实现

二、创建基本的框架

1、创建自定义的视图
package com.android.imooc.imagescale;

import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;

/**
 * @描述         实现可以自由移动缩放的图片控件
 * @项目名称      App_imooc
 * @包名         com.android.imooc.imagescale
 * @类名         ScaleView
 * @author      chenlin
 * @date        2013年6月10日 下午10:11:11
 * @version     1.0
 */

public class ScaleView extends ImageView implements OnGlobalLayoutListener{


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

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

    public ScaleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void onGlobalLayout() {

    }

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

    /**
     * 移除全局事件
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }



}

2、把视图添加到布局里

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.android.imooc.imagescale.ScaleView
        android:id="@+id/scaleView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

3、创建主页

package com.android.imooc.imagescale;

import com.android.imooc.R;

import android.app.Activity;
import android.os.Bundle;

/**
 * @描述         TODO
 * @项目名称      App_imooc
 * @包名         com.android.imooc.imagescale
 * @类名         ScaleActivity
 * @author      chenlin
 * @date        2013年6月10日 下午10:17:16
 * @version     1.0
 */

public class ScaleActivity extends Activity {

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

三、添加基本的事件处理

1、设置合适的图片大小
1)图片只加载一此,所以用isOnce来判断是否加载了图片
2)首先获得控件的大小
3)获得图片的大小
4)进行对比

//如果还没有加载图片
        if (!isOne) {

            //获得控件的宽高
            int width = getWidth();
            int height = getHeight();

            Drawable drawable = getDrawable();
            if (drawable == null) {
                return;
            }
            //获得图片的宽高
            int bitmapWidth = drawable.getIntrinsicWidth();
            int bitmapHeight = drawable.getIntrinsicHeight();

            //设定比例值
            float scale = 0.0f;

            //如果图片的宽度>控件的宽度,缩小
            if (bitmapWidth > width && bitmapHeight < height) {
                scale =  width * 1.0f / bitmapWidth;
            }
            //如果图片的高度>控件的高度,缩小
            if (bitmapHeight > height && bitmapWidth < width) {
                scale =  height * 1.0f / bitmapHeight;
            }
            //如果图片的宽高度>控件的宽高度,缩小  或者  如果图片的宽高度<控件的宽高度,放大
            if ((bitmapWidth > width && bitmapHeight > height) || (bitmapWidth < width && bitmapHeight < height) ) {
                float f1 = width * 1.0f / bitmapWidth;
                float f2 =  height * 1.0f / bitmapHeight;
                scale = Math.min(f1, f2);
            }

            isOne = true;
        }

2、让图片居中缩放显示
1)定义三个缩放的变量

/**初始时的缩放值*/
    private float mInitScale;
    /**双击时 的缩放值*/
    private float mClickScale ;
    /**最大的缩放值*/
    private float mMaxScale;

2)在onGlobalLayout里初始化

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

3)得到图片移动到中心点时的距离
这里写图片描述

//得到移动的距离
int dx = width/2 - bitmapWidth/2;
int dy = height/2 - bitmapHeight/2;

4)将图片移动到控件的中心

Matrix matrix = new Matrix();
//记得必须先平移才缩放,否则没效果
matrix.postTranslate(dx, dy);
//在控件的中心缩放
mMatrix.postScale(scale, scale, width/2, height/2);

//设置矩阵
setImageMatrix(matrix);

//关于matrix,就是个3*3的矩阵
/**
 *   xscale xskew   xtrans
 *   yskew  yscale  ytrans
 *   0       0        0
 */

5)添加图片测试:

public class ScaleActivity extends Activity {
    private ScaleView mScaleView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scale);
        mScaleView = (ScaleView) findViewById(R.id.scaleView);
        mScaleView.setImageResource(R.drawable.pic_1);
    }
}

如图:图片居中,并缩小了
这里写图片描述

3、实现图片以屏幕中心进行缩放
1)使用到的类:ScaleGestureDetector
2)初始化:
pirvate ScaleGestureDetector mScaleGesture = new ScaleGestureDetector (context, this);
3)让类实现OnScaleGestureListener,并实现下面三个方法:

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        // 必须返回true
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        // TODO Auto-generated method stub

    }

4)要实现屏幕触摸,必须实现OnTouchListener, 把触屏事件给缩放手势处理

@Override
    public boolean onTouch(View v, MotionEvent event) {
        mScaleGesture.onTouchEvent(event);
        //必须返回true
        return true;
    }

5)在onScale方法里实现缩放
先取得缩放值

/**
     * 获得缩放值
     * @return
     */
    public float getScale(){
        /**
         *   xscale xskew   xtrans
         *   yskew  yscale  ytrans
         *   0       0        0
         */
        float[] values = new float[9];
        mMatrix.getValues(values);
        return values[Matrix.MSCALE_X];
    }

控制缩放:

 @Override
    public boolean onScale(ScaleGestureDetector detector) {
        //如果没有图片,返回
        if (getDrawable() == null) {
            return true;
        }
        //缩放因子,>0表示正在放大,<0表示正在缩小
        float intentScale = detector.getScaleFactor();
        float scale = getScale();

        //进行缩放范围的控制
        //判断,如果<最大缩放值,表示可以放大,如果》最小缩放,说明可以缩小
        if ((scale < mMaxScale && intentScale > 1.0f )|| (scale > mInitScale && intentScale < 1.0f)) {

            //scale 变小时, intentScale变小
            if (scale * intentScale < mInitScale) {
                //intentScale * scale = mInitScale ;
                intentScale = mInitScale / scale;
            }

            //scale 变大时, intentScale变大
            if (scale * intentScale > mMaxScale) {
                //intentScale * scale = mMaxScale ;
                intentScale = mMaxScale / scale;
            }

            mMatrix.postScale(intentScale, intentScale, getWidth()/2, getHeight()/2);
            setImageMatrix(mMatrix);
        }


        return true;
    }

4、实现图片多点缩放
1)首先不能以控件为中心缩放了,改为以手势为中心缩放

mMatrix.postScale(intentScale, intentScale, detector.getFocusX(), detector.getFocusY());

2)调整边界
首先得到图片缩放后的矩阵

/**
     * 获得图片缩放后的矩阵
     * @return
     */
    public RectF getMatrixRectF(){
        Matrix matrix = mMatrix;
        RectF rectF = new RectF();
        Drawable drawable = getDrawable();
        if (drawable != null) {
            //初始化矩阵
            rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
            //移动s
            matrix.mapRect(rectF);
        }
        return rectF;
    }

如图:三种情况
这里写图片描述

private void checkSideAndCenter() {
        RectF rectF = getMatrixRectF();
        float deltaX = 0f;
        float deltaY = 0f;
        int width = getWidth();
        int height = getHeight();

        //情况1, 如果图片的宽度大于控件的宽度
        if (rectF.width() >= width) {
            if (rectF.left > 0) {
                deltaX = - rectF.left;//如果图片没有左边对齐,就往左边移动
            }
            if (rectF.right < width) {
                deltaX = width - rectF.right;//如果图片没有右边对齐,就往右边移动
            }
        }
        //情况2, 如果图片的宽度大于控件的宽度
        if (rectF.height() >= height) {
            if (rectF.top > 0) {
                deltaY = - rectF.top;//
            }
            if (rectF.bottom < height) {
                deltaY = height - rectF.bottom;//往底部移动
            }
        }

        //情况3,如图图片在控件内,则让其居中
        if (rectF.width() < width ) {
            //deltaX = width/2-rectF.left - rectF.width()/2;
            //或
            deltaX = width/2-rectF.right + rectF.width()/2;
        }

        if (rectF.height() < height) {
            deltaY = height/2-rectF.bottom + rectF.height()/2;
        }

        mMatrix.postTranslate(deltaX, deltaY);
        setImageMatrix(mMatrix);
    }

情况三公式:
这里写图片描述

5、实现图片的自由移动
1)事件处理在onTouchEvent里,首先定义变量记录上次手指的个数;

private int lastPointerCount;

2)求得手指的中心点

float x = event.getX();
float y = event.getY();

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

3)判断记录

// 说明手指改变
if (mLastPointerCount != pointerCount) {
    mLastX = x;
    mLastY = y;
}
mLastPointerCount = pointerCount;

4)事件处理

switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE:
            float dx = x - mLastX;
            float dy = y - mLastX;

            if (!isCanDrag) {
                isCanDrag = isMoveAction(dx, dy);
            }
            /**
             * 如果能移动
             */
            if (isCanDrag) {
                RectF rectF = getMatrixRectF();
                if (getDrawable() == null) {
                    return true;
                }

                //如果图片在控件内,不允许移动
                if (rectF.width() < getWidth()) {
                    dx = 0f;
                }
                if (rectF.height() < getHeight()) {
                    dy = 0f;
                }

                mMatrix.postTranslate(dx, dy);
                //移动事检测边界
                checkSideAndCenterWhenTrans();

                setImageMatrix(mMatrix);
            }

            mLastX = x;
            mLastY = y;

            break;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            //清楚手指
            mLastPointerCount = 0;

            break;

5)测试,发现图片可有自由移到,所以必须重新检测边界
定义变量

    /** 判断是否检测了x,y轴 */
    private boolean isCheckX;
    private boolean isCheckY;

//移到
private void checkSideAndCenterWhenTransate() {
        RectF rectF = getMatrixRectF();
        float deltaX = 0f;
        float deltaY = 0f;
        int width = getWidth();
        int height = getHeight();

        if (rectF.top > 0 && isCheckY) {
            deltaY = - rectF.top;//往上边移动
        }
        if (rectF.bottom < height && isCheckY) {
            deltaY = height - rectF.bottom;//往底部移动
        }

        if (rectF.left > 0 && isCheckX) {
            deltaX = - rectF.left;//往左边移动
        }
        if (rectF.right < width && isCheckY) {
            deltaX = width - rectF.right;//往右边移动
        }
        //移动
        mMatrix.postTranslate(deltaX, deltaY);
    }

6、实现图片双击放大与缩小
1)主要使用的类

private GestureDetector mGesture;

在构造方法里初始化

mGesture = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            return true;
        }
    });

2)传递事件,在onTouchEvent里

//把事件传递给双击手势
if (mGesture.onTouchEvent(event)) {
    return true;
}

3)实现:

float px = e.getX();
float py = e.getY();
//只有小于最大缩放比例才能放大
float scale = getScale();
if (scale < mMaxScale) {
    mMatrix.postScale(mClickScale/scale, mClickScale/scale, px, py);
}else {
    mMatrix.postScale(mInitScale/scale, mInitScale/scale, px, py);
}
setImageMatrix(mMatrix);

4)测试发现,图片瞬间变大缩小,这不是我们需要的效果,我们要的是慢慢放大,慢慢缩小,有个动画的过程
如何实现?可有使用一个类,继承runnable,在run方法里进行缩放

private class ScaleRunnale implements Runnable{
        //放大值
        private static final float BIGGER = 1.08f;
        //缩小值
        private static final float SMALLER = 0.96f;
        private float x;
        private float y;
        private float mTargetScale;
        private float mTempScale;

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

            if (getScale() < mTargetScale) {
                mTempScale = BIGGER;
            }else if (getScale() > mTargetScale) {
                mTempScale = SMALLER;
            }
        }

        @Override
        public void run() {
            //先进行缩放
            mMatrix.postScale(mTempScale, mTempScale, x, y);
            checkSideAndCenterWhenScale();
            setImageMatrix(mMatrix);

            float currentScale = getScale();

            //如果想放大,并且当前的缩放值小于目标值 
            if ((mTempScale > 1.0f && currentScale < mTargetScale) || (mTempScale < 1.0f && currentScale > mTargetScale)) {
                //递归执行run方法
                postDelayed(this, 16);
            }else {
                float scale = mTargetScale / currentScale;
                mMatrix.postScale(scale, scale, x, y);
                checkSideAndCenterWhenScale();
                setImageMatrix(mMatrix);

                isAutoScale = false;
            }
        }

    }

修改原来的缩放方法:

public boolean onDoubleTap(MotionEvent e) {

        //如果正在缩放时,不能放大
        if (isAutoScale) {
            return true;
        }

        float px = e.getX();
        float py = e.getY();
        //只有小于最大缩放比例才能放大
        float scale = getScale();
        if (scale < mClickScale) {
            //mMatrix.postScale(mClickScale/scale, mClickScale/scale, px, py);
            postDelayed(new ScaleRunnale(px, py, mClickScale), 16);
            isAutoScale = true;
        }else {
            //mMatrix.postScale(mInitScale/scale, mInitScale/scale, px, py);
            postDelayed(new ScaleRunnale(px, py, mInitScale), 16);
            isAutoScale = true;
        }
        //setImageMatrix(mMatrix);
        return true;
    }

效果图:

这里写图片描述

最后:全部代码

package com.android.imooc.imagescale;

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.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;

/**
 * @描述 实现可以自由移动缩放的图片控件
 * @项目名称 App_imooc
 * @包名 com.android.imooc.imagescale
 * @类名 ScaleView
 * @author chenlin
 * @date 2013年6月10日 下午10:11:11
 * @version 1.0
 */

public class ScaleView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener {

    /** 表示是否只有一次加载 */
    private boolean isOnce = false;
    /** 初始时的缩放值 */
    private float mInitScale;
    /** 双击时 的缩放值 */
    private float mClickScale;
    /** 最大的缩放值 */
    private float mMaxScale;
    /** 图片缩放矩阵 */
    private Matrix mMatrix;
    /** 图片缩放手势 */
    private ScaleGestureDetector mScaleGesture;

    // ----------------------------自由移动--------------------------------
    /** 可移动最短距离限制,大于这个值时就可移动 */
    private int mTouchSlop;
    /** 是否可以拖动 */
    private boolean isCanDrag;

    // ----------------------------双击放大--------------------------------
    private GestureDetector mGesture;
    // 是否自动缩放
    private boolean isAutoScale;

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

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

    public ScaleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // 必须设置才能触发
        this.setOnTouchListener(this);

        mMatrix = new Matrix();
        // 设置缩放模式
        super.setScaleType(ScaleType.MATRIX);

        mScaleGesture = new ScaleGestureDetector(context, this);
        mGesture = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onDoubleTap(MotionEvent e) {

                // 如果正在缩放时,不能放大
                if (isAutoScale) {
                    return true;
                }

                float px = e.getX();
                float py = e.getY();
                // 只有小于最大缩放比例才能放大
                float scale = getScale();
                if (scale < mClickScale) {
                    // mMatrix.postScale(mClickScale/scale, mClickScale/scale,
                    // px, py);
                    postDelayed(new ScaleRunnale(px, py, mClickScale), 16);
                    isAutoScale = true;
                } else {
                    // mMatrix.postScale(mInitScale/scale, mInitScale/scale, px,
                    // py);
                    postDelayed(new ScaleRunnale(px, py, mInitScale), 16);
                    isAutoScale = true;
                }
                // setImageMatrix(mMatrix);
                return true;
            }
        });

        /**
         * 是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如viewpager
         * 就是用这个距离来判断用户是否翻页。
         */
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    private class ScaleRunnale implements Runnable {
        // 放大值
        private static final float BIGGER = 1.08f;
        // 缩小值
        private static final float SMALLER = 0.96f;
        private float x;
        private float y;
        private float mTargetScale;
        private float mTempScale;

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

            if (getScale() < mTargetScale) {
                mTempScale = BIGGER;
            } else if (getScale() > mTargetScale) {
                mTempScale = SMALLER;
            }
        }

        @Override
        public void run() {
            // 先进行缩放
            mMatrix.postScale(mTempScale, mTempScale, x, y);
            checkSideAndCenterWhenScale();
            setImageMatrix(mMatrix);

            float currentScale = getScale();

            // 如果想放大,并且当前的缩放值小于目标值
            if ((mTempScale > 1.0f && currentScale < mTargetScale)
                    || (mTempScale < 1.0f && currentScale > mTargetScale)) {
                // 递归执行run方法
                postDelayed(this, 16);
            } else {
                float scale = mTargetScale / currentScale;
                mMatrix.postScale(scale, scale, x, y);
                checkSideAndCenterWhenScale();
                setImageMatrix(mMatrix);

                isAutoScale = false;
            }
        }

    }

    @Override
    public void onGlobalLayout() {
        // 如果还没有加载图片
        if (!isOnce) {

            // 获得控件的宽高
            int width = getWidth();
            int height = getHeight();

            Drawable drawable = getDrawable();
            if (drawable == null) {
                return;
            }
            // 获得图片的宽高
            int bitmapWidth = drawable.getIntrinsicWidth();
            int bitmapHeight = drawable.getIntrinsicHeight();

            // 设定比例值
            float scale = 0.0f;

            // 如果图片的宽度>控件的宽度,缩小
            if (bitmapWidth > width && bitmapHeight < height) {
                scale = width * 1.0f / bitmapWidth;
            }
            // 如果图片的高度>控件的高度,缩小
            if (bitmapHeight > height && bitmapWidth < width) {
                scale = height * 1.0f / bitmapHeight;
            }
            // 如果图片的宽高度>控件的宽高度,缩小 或者 如果图片的宽高度<控件的宽高度,放大
            if ((bitmapWidth > width && bitmapHeight > height) || (bitmapWidth < width && bitmapHeight < height)) {
                float f1 = width * 1.0f / bitmapWidth;
                float f2 = height * 1.0f / bitmapHeight;
                scale = Math.min(f1, f2);
            }

            // 初始化缩放值
            mInitScale = scale;
            mClickScale = mInitScale * 2;
            mMaxScale = mInitScale * 4;

            // 得到移动的距离
            int dx = width / 2 - bitmapWidth / 2;
            int dy = height / 2 - bitmapHeight / 2;

            // 平移
            mMatrix.postTranslate(dx, dy);

            // 在控件的中心缩放
            mMatrix.postScale(scale, scale, width / 2, height / 2);

            // 设置矩阵
            setImageMatrix(mMatrix);

            // 关于matrix,就是个3*3的矩阵
            /**
             * xscale xskew xtrans yskew yscale ytrans 0 0 0
             */

            isOnce = true;
        }
    }

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

    /**
     * 移除全局事件
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }

    /**
     * 获得缩放值
     * 
     * @return
     */
    public float getScale() {
        /**
         * xscale xskew xtrans yskew yscale ytrans 0 0 0
         */
        float[] values = new float[9];
        mMatrix.getValues(values);
        return values[Matrix.MSCALE_X];
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        // 如果没有图片,返回
        if (getDrawable() == null) {
            return true;
        }
        // 缩放因子,>0表示正在放大,<0表示正在缩小
        float intentScale = detector.getScaleFactor();
        float scale = getScale();

        // 进行缩放范围的控制
        // 判断,如果<最大缩放值,表示可以放大,如果》最小缩放,说明可以缩小
        if ((scale < mMaxScale && intentScale > 1.0f) || (scale > mInitScale && intentScale < 1.0f)) {

            // scale 变小时, intentScale变小
            if (scale * intentScale < mInitScale) {
                // intentScale * scale = mInitScale ;
                intentScale = mInitScale / scale;
            }

            // scale 变大时, intentScale变大
            if (scale * intentScale > mMaxScale) {
                // intentScale * scale = mMaxScale ;
                intentScale = mMaxScale / scale;
            }

            // 以控件为中心缩放
            // mMatrix.postScale(intentScale, intentScale, getWidth()/2,
            // getHeight()/2);
            // 以手势为中心缩放
            mMatrix.postScale(intentScale, intentScale, detector.getFocusX(), detector.getFocusY());

            // 检测边界与中心点
            checkSideAndCenterWhenScale();

            setImageMatrix(mMatrix);
        }

        return true;
    }

    /**
     * 获得图片缩放后的矩阵
     * 
     * @return
     */
    public RectF getMatrixRectF() {
        Matrix matrix = mMatrix;
        RectF rectF = new RectF();
        Drawable drawable = getDrawable();
        if (drawable != null) {
            // 初始化矩阵
            rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
            // 移动s
            matrix.mapRect(rectF);
        }
        return rectF;
    }

    private void checkSideAndCenterWhenScale() {
        RectF rectF = getMatrixRectF();
        float deltaX = 0f;
        float deltaY = 0f;
        int width = getWidth();
        int height = getHeight();

        // 情况1, 如果图片的宽度大于控件的宽度
        if (rectF.width() >= width) {
            if (rectF.left > 0) {
                deltaX = -rectF.left;// 如果图片没有左边对齐,就往左边移动
            }
            if (rectF.right < width) {
                deltaX = width - rectF.right;// 如果图片没有右边对齐,就往右边移动
            }
        }
        // 情况2, 如果图片的宽度大于控件的宽度
        if (rectF.height() >= height) {
            if (rectF.top > 0) {
                deltaY = -rectF.top;//
            }
            if (rectF.bottom < height) {
                deltaY = height - rectF.bottom;// 往底部移动
            }
        }

        // 情况3,如图图片在控件内,则让其居中
        if (rectF.width() < width) {
            // deltaX = width/2-rectF.left - rectF.width()/2;
            // 或
            deltaX = width / 2f - rectF.right + rectF.width() / 2f;
        }

        if (rectF.height() < height) {
            deltaY = height / 2f - rectF.bottom + rectF.height() / 2f;
        }

        mMatrix.postTranslate(deltaX, deltaY);
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        // TODO Auto-generated method stub
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        // TODO Auto-generated method stub

    }

    private float mLastX;
    private float mLastY;
    /** 上次手指的数量 */
    private int mLastPointerCount;

    /** 判断是否检测了x,y轴 */
    private boolean isCheckX;
    private boolean isCheckY;

    @Override
    public boolean onTouch(View v, MotionEvent event) {

        // 把事件传递给双击手势
        if (mGesture.onTouchEvent(event)) {
            return true;
        }
        // 把事件传递给缩放手势
        mScaleGesture.onTouchEvent(event);

        float x = event.getX();
        float y = event.getY();

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

        // 说明手指改变
        if (mLastPointerCount != pointerCount) {
            isCanDrag = false;
            mLastX = x;
            mLastY = y;
        }
        mLastPointerCount = pointerCount;

        RectF rectF = getMatrixRectF();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (rectF.width() > getWidth()) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }
            break;

        case MotionEvent.ACTION_MOVE:
            if (rectF.width() > getWidth()) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }

            float dx = x - mLastX;
            float dy = y - mLastY;

            if (!isCanDrag) {
                isCanDrag = isMoveAction(dx, dy);
            }
            /**
             * 如果能移动
             */
            if (isCanDrag) {
                //RectF rectF = getMatrixRectF();
                if (getDrawable() == null) {
                    return true;
                }

                isCheckX = isCheckY = true;

                // 如果图片在控件内,不允许移动
                if (rectF.width() < getWidth()) {
                    isCheckX = false;
                    dx = 0f;
                }
                if (rectF.height() < getHeight()) {
                    isCheckY = false;
                    dy = 0f;
                }

                mMatrix.postTranslate(dx, dy);

                // 移动事检测边界
                checkSideAndCenterWhenTransate();

                setImageMatrix(mMatrix);
            }

            mLastX = x;
            mLastY = y;

            break;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            // 清楚手指
            mLastPointerCount = 0;

            break;
        }

        return true;
    }

    private void checkSideAndCenterWhenTransate() {
        RectF rectF = getMatrixRectF();
        float deltaX = 0f;
        float deltaY = 0f;
        int width = getWidth();
        int height = getHeight();

        if (rectF.top > 0 && isCheckY) {
            deltaY = -rectF.top;// 往上边移动
        }
        if (rectF.bottom < height && isCheckY) {
            deltaY = height - rectF.bottom;// 往底部移动
        }

        if (rectF.left > 0 && isCheckX) {
            deltaX = -rectF.left;// 往左边移动
        }
        if (rectF.right < width && isCheckX) {
            deltaX = width - rectF.right;// 往右边移动
        }
        // 移动
        mMatrix.postTranslate(deltaX, deltaY);
    }

    private boolean isMoveAction(float dx, float dy) {
        // 求得两点的距离
        return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;
    }

}

主页

/**
 * @描述 TODO
 * @项目名称 App_imooc
 * @包名 com.android.imooc.imagescale
 * @类名 ScaleActivity
 * @author chenlin
 * @date 2013年6月10日 下午10:17:16
 * @version 1.0
 */

public class ScaleActivity extends Activity {
    // private ScaleView mScaleView;
    private ViewPager mPager;
    private ScalePagerAdapter mAdapter;

    private int resIds[] = new int[] { R.drawable.pic_1, R.drawable.pic_2, R.drawable.pic_3, R.drawable.pic_4,
            R.drawable.pic_5, R.drawable.pic_6 };
    private ScaleView[] mScaleViews;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scale);
        // mScaleView = (ScaleView) findViewById(R.id.scaleView);
        // mScaleView.setImageResource(R.drawable.pic_1);
        mScaleViews = new ScaleView[resIds.length];
//      for (int i = 0; i < resIds.length; i++) {
//          mScaleViews[i] = new ScaleView(this);
//          mScaleViews[i].setImageResource(resIds[i]);
//      }

        mPager = (ViewPager) findViewById(R.id.viewPager);
        mAdapter = new ScalePagerAdapter();
        mPager.setAdapter(mAdapter);

    }

    private class ScalePagerAdapter extends PagerAdapter {

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

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
//          if (object instanceof ScaleView) {
//              ScaleView scaleView = (ScaleView) object;
//              container.removeView(scaleView);
//          }
            container.removeView(mScaleViews[position]);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {

            ScaleView scaleView = new ScaleView(getApplicationContext());
            scaleView.setImageResource(resIds[position]);
            mScaleViews[position] = scaleView;
            container.addView(scaleView);
            return scaleView;
        }

    }
}

———————————————————————
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万)
有需求者请进站查看,非诚勿扰

https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084hc8wek&id=555888526201

01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
(送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门)
——————————————————————–

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lovoo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值