通过自定义ImageView和实现OnTouchListener 来实现图片的手势滑动和缩放功能

本文会使用到的类 : PointF ,Matrix 以及 MutionEvent 。

下面就来介绍一下这几个类在本文中的作用


PointF :

顾名思义 Point就是点的意思 ,F则是Float ,所以这个类实现了一个以Float为单位的一个点的功能。

参数为x和y轴的坐标。

实现方法:PointF point = new PointF(0.2f,0.2f);


Matrix:

这个类表示的是一个3x3的矩阵,本文中不详细说明。

Matrix是实现了平行和旋转还有缩放功能的一个类,这里是用的是graphis包中的Matrix,

本文中会用到该类的两个方法

postTranslate(x,y);

postTranslate方法是平移,里面的参数x为横向平移距离,y为纵向平移距离。


postScale(scale,scale,mX,mY) 

postScale方法是缩放,这里的scale指的是缩放倍数,这里xy都缩放scale倍,而后两个参数则是缩放的中心点。


 MutionEvent:


mutionEvent则是所有触点的集合.他的对象是一个数组;我们可以通过get来获取到触点的信息。

以及一些动作常量。

ACTION.POINTER.DOWN:有pointer的为副触点 该变量指定的为两个或者两个以上的触点摁下的动作

ACTION.DOWN: 为单一触点摁下的动作

ACTION.MOVE:为 移动动作

等等 等下代码里可以看出来其他的用途


介绍完本文要用的类,来看一下怎么实现图片的平移和缩放的思路

1.继承ImageView并复写View.onTouchListener接口

2.在构造方法里触发onTouchListener的点击事件

3.在onTouch里面 对v.getAction进行判断 一共分为一下几种情况:

单指点下,单指松开,多指摁下,多指松开,进行不同的事件处理。



单指摁下,将触发模式设为拖拽模式,这种模式下,可以对图片进行拖拽 并对替换用的Matrix进行初始化,以及对恩下的点(起始点)进行初始化,代码如下

case MotionEvent.ACTION_DOWN:
                mode = MODE_DRAWE;
                currentMatrix.set(getImageMatrix());
                firstPoint.set(event.getX(),event.getY());
                break;


多指摁下,将触发模式设为缩放模式,这种模式下,可以对图片进行缩放,    我们对两点之间的距离进行计算(使用勾股定理)

如果大于10f 就进行缩放处理    并且初始化中间点(两点的横纵坐标相加除2)

 case MotionEvent.ACTION_POINTER_DOWN:
                mode = MODE_ZOOM;
                overDistance = distance(event);
                if(overDistance > 10f){
                    mindPoint = getMidPoint(event);
                    currentMatrix.set(getImageMatrix());
                }
                break;


单指松开和多指松开的思路是一样的,都是讲触发模式设为离开模式 就可以了

 case MotionEvent.ACTION_UP:
 case MotionEvent.ACTION_POINTER_UP:
                mode = MODE_LEAVE;
                break;

最后到了我们的重头戏,移动 移动要实现的逻辑分为主要两部。

1.判断触发模式(1.拖拽模式,2.缩放模式)

2.为各个模式设置事件


拖拽模式:1.计算拖拽的横纵长度(当前触点的坐标-原来的坐标),给图片的Matrix设置上替换的Matrix 2.使用拖拽方法。

缩放模式:1.计算拖拽的横纵长度,计算出两个触点之间的长度(也就是两个手之间的长度)2.如果两个触点间的长度大于10f视为缩放了,我们这里就要求出缩放的倍数(两个触点拖拽后的长度/原来两个触点的长度),然后使用缩放方法来进行缩放。

代码如下:

 case MotionEvent.ACTION_MOVE:
                if(mode == MODE_DRAWE){
                    float dx = event.getX() - firstPoint.x;
                    float dy = event.getY() - firstPoint.y;
                    mImageMatrix.set(currentMatrix);
                    mImageMatrix.postTranslate(dx,dy);
                }else if(mode == MODE_ZOOM){
                    float dx = event.getX() - firstPoint.x;
                    float dy = event.getY() - firstPoint.y;
                    float distance1 = distance(event);
                    if(distance1 > 10f){
                        float scale = distance1 / overDistance;
                        mImageMatrix.set(currentMatrix);
                        mImageMatrix.postScale(scale,scale,dx,dy);
                    }

                }
                break;



思路就是这些,下面放代码。希望大家可以慢慢琢磨,这个功能其实很容易,按照上面的思路一步步撸下来,就懂了!

public class NixoImageView2 extends android.support.v7.widget.AppCompatImageView  implements View.OnTouchListener{


    private float mode = 0;

    private static final float MODE_DRAWE = 1;

    private static final float MODE_ZOOM = 2;

    private static final float MODE_LEAVE = 0;

    private PointF firstPoint = new PointF();

    private android.graphics.Matrix mImageMatrix = new android.graphics.Matrix();

    private android.graphics.Matrix currentMatrix = new android.graphics.Matrix();

    private PointF mindPoint;

    private float overDistance;






    public NixoImageView2(Context context) {
        this(context,null);
        setOnTouchListener(this);
    }

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

    public NixoImageView2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnTouchListener(this);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK){
            case MotionEvent.ACTION_DOWN:
                mode = MODE_DRAWE;
                currentMatrix.set(getImageMatrix());
                firstPoint.set(event.getX(),event.getY());
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                mode = MODE_ZOOM;
                overDistance = distance(event);
                if(overDistance > 10f){
                    mindPoint = getMidPoint(event);
                    currentMatrix.set(getImageMatrix());
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(mode == MODE_DRAWE){
                    float dx = event.getX() - firstPoint.x;
                    float dy = event.getY() - firstPoint.y;
                    mImageMatrix.set(currentMatrix);
                    mImageMatrix.postTranslate(dx,dy);
                }else if(mode == MODE_ZOOM){
                    float dx = event.getX() - firstPoint.x;
                    float dy = event.getY() - firstPoint.y;
                    float distance1 = distance(event);
                    if(distance1 > 10f){
                        float scale = distance1 / overDistance;
                        mImageMatrix.set(currentMatrix);
                        mImageMatrix.postScale(scale,scale,dx,dy);
                    }

                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = MODE_LEAVE;
                break;
        }


        setImageMatrix(mImageMatrix);
        return true;
    }

    private float distance(MotionEvent event){
        float dx = (event.getX(0) - event.getX(1));
        float dy = (event.getY(0) - event.getY(1));
        float distance = (float) Math.sqrt(dx*dx + dy*dy);
        return distance;
    }

    private PointF getMidPoint(MotionEvent event){
        float dx = (event.getX(0) - event.getX(1));
        float dy = (event.getY(0) - event.getY(1));
        return  new PointF(dx,dy);
    }

}
 
 

下面再放一个带注释的版本可能写的有些乱

public class NixoImageView extends android.support.v7.widget.AppCompatImageView implements View.OnTouchListener{

    //记录当前的状态 是拖拉图片,还是放大缩小;
    private int mode = 0;

    //拖拉模式
    private static final int MODE_DRAG = 1;

    //放大缩小模式
    private static final int MODE_ZOOM = 2;

    //手指离开模式
    private static final int MODE_LEAVE = 0;

    //第一个手指的坐标
    private PointF startPoint = new PointF();

    //用于记录拖拉图片移动的距离
    private Matrix matrix = new Matrix();

    //用于记录图片拖拉的前的矩阵
    private Matrix currentMatrix = new Matrix();

    //两个手指的开始距离
    private float startDis;

    //两个手指的中间点坐标
    private PointF midPoint;



    public NixoImageView(Context context) {
        this(context,null);
        setOnTouchListener(this);
    }

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

    public NixoImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnTouchListener(this);

    }


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

        //event.getAction() & MotionEvent.ACTION_MASK保证了数据的精确
        switch (event.getAction() & MotionEvent.ACTION_MASK){
            // MotionEvent.ACTION_DOWN :手指压下屏幕的情况
            case MotionEvent.ACTION_DOWN:
                mode = MODE_DRAG;
                //记录ImageView当前的移动位置
                //getImageMatrix()获取图片的矩阵
                currentMatrix.set(getImageMatrix());
                //设置起点坐标
                startPoint.set(event.getX(),event.getY());
            break;
            //手指在屏幕上移动,该事件就会被不断触发
            case MotionEvent.ACTION_MOVE:
                //拖拉图片
                if(mode == MODE_DRAG){
                    //x轴的移动距离 = 触摸点跟控件的距离 - 开始拖拉的横坐标;
                    float dx = event.getX() - startPoint.x;
                    //y轴的移动距离 = 触摸点跟控件的距离 - 开始拖拉的纵坐标;
                    float dy = event.getY() - startPoint.y;
                    matrix.set(currentMatrix);
                    matrix.postTranslate(dx,dy);//转变(拖拉)后的位置
                }
                //放大缩小图片
                else if(mode == MODE_ZOOM){
                    //两个触点之间的距离
                    float distance = distance(event);
                    if(distance > 10f){ //如果两个手指距离大于10px
                        float scale = distance / startDis;//得到缩放倍数
                        matrix.set(currentMatrix);
                        matrix.postScale(scale,scale,midPoint.x,midPoint.y);//参数为X轴和Y轴缩放大小,其次是缩放的中心
                    }
                }
                break;
                //第一个手指离开屏幕
            case MotionEvent.ACTION_UP:
                //其他触点也离开屏幕
            case MotionEvent.ACTION_POINTER_UP:
                mode = MODE_LEAVE;
                break;
            //当屏幕上已经有触点(手指) ,再有一个触点压下屏幕(也就是第二个手指压下屏幕)
            case MotionEvent.ACTION_POINTER_DOWN:
                mode = MODE_ZOOM;
                //将两个触点之间的距离传递给startDis
                startDis = distance(event);

                if(startDis > 10f){
                    midPoint = mid(event);
                    currentMatrix.set(getImageMatrix());
                }
                break;
        }
        setImageMatrix(matrix);
        return true;
    }

    //计算连个手指间的距离
    private  float distance(MotionEvent event){
        float dx = (event.getX(1) - event.getX(0))/2;
        float dy = (event.getY(1) - event.getY(0))/2;
        return (float) Math.sqrt(dx*dx + dy*dy);//勾股定理求出两个触点之间的距离
    }

    private PointF mid(MotionEvent event){
        float midX = (event.getX(1) + event.getX(0)) / 2;
        float midY = (event.getY(1) + event.getY(0)) / 2;
        return  new PointF(midX,midY);
    }


}






猜你喜欢

转载自blog.csdn.net/qq_40033365/article/details/80160158
今日推荐