欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-24 17:41:28
...

本文会使用到的类 : 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);
    }


}