GestureDetector的源码分析,以及使用详解

Gesture:手势
Detector:侦查,识别
GestureDetector:手势识别
这个类主要是用来处理手指在屏幕上的一些动作的,比如scroll,fling,down,press等。通过查看这个类的文档,发现,它内部定义了三个接口,分别是OnGestureListener,OnDoubleTapListener,OnContextClickListener以及一个静态内部类,SimpleOnGestureListener,这个静态内部类实现了,前面提到的三个接口。
这个静态内部类的好处是:
1.当我们不想重写某个接口的那么多方法时,就可以使用这个静态内部类,由于这个静态内部类已经对三个接口做了空实现,所以我们不必再对这三个接口的方法都做实现,只需要重写我们关心的的方法即可。
2.如果要监听多个手势动作,比如我们既要监听单击,又要监听双击屏幕的手势,则需要给
gestureDetector对象设置多个监听对象。这时为了简化这些步骤,可以直接使用给gestureDetector对象设置一个SimpleOnGestureListener监听对象即可。
为了能够更好的使用GestureDetector,我们就需要仔细了解这三个接口响应手势的回调方法,首先来看OnGestureListener接口中的各个方法。

    public interface OnGestureListener {

        //每次触摸屏幕,这个方法都会触发
        boolean onDown(MotionEvent e);

        //如果按下的时间超过180 ms,并且按下后,没有松开或者滑动,这个方法就会执行
	//看文档上的注释是,用户已经执行了down 这个motionevent,并且用户还未执行移动或者抬起手指,
	//这个事件通常是用于给用户一个视觉反馈,让用户知道他们的这个动作已经识别了。
        void onShowPress(MotionEvent e);

	//一次单独的轻敲,这个方法就会执行
	//触发顺序:
        //点击一下非常快的(不滑动)Touchup:
        //onDown->onSingleTapUp->onSingleTapConfirmed 
   	//点击一下稍微慢点的(不滑动)Touchup:
   	//onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
        boolean onSingleTapUp(MotionEvent e);

	//手指在屏幕上移动,或者手指在屏幕上掠过(按下后,快速的滑动后松开),都会触发这个方法
	//触发顺序:  
	//手指在屏幕缓慢移动  onDown -> onShowPress(可能会有) -> 多个 onScroll
	//手机在屏幕快速掠过  onDown -> 多个 onScroll -> onFling

	// 参数解释:
	// e1:第1个ACTION_DOWN MotionEvent
	// e2:最后一个ACTION_MOVE MotionEvent
	// velocityX:X轴上的移动速度,像素/秒
	// velocityY:Y轴上的移动速度,像素/秒   
        boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

	//手指在屏幕上长按,就会触发这个方法
	// 触发顺序:onDown->onShowPress->onLongPress
        void onLongPress(MotionEvent e);

	//用户手指在屏幕掠过(按下后,快速的滑动后松开)
        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
    }

    public interface OnDoubleTapListener {
	
	//当手指在屏幕上单击时,这个方法会执行
	//触发顺序:onDown -> onShowPress(可能有) -> onSingleTapUp -> onSingleTapConfirmed
	//onSingleTapUp方法和onSingleConfirmed方法的区别是:不管是双击还是单击,手机离开屏幕的第一次,
	//都会触发onSingleUp方法,如果手指单击屏幕,则会触发onSingleConfirmed方法,用于确定这次的手势就是单击
	//如果手指双击屏幕,则不会触发onSinlgeConfirmed方法
        boolean onSingleTapConfirmed(MotionEvent e);
 
	//当手指双击屏幕时,会执行这个方法
	//触发顺序是:onDown -> onSingleTapUp ->onDoubleUp
        boolean onDoubleTap(MotionEvent e);

	//当手指双击屏幕时,如果在onDoubleUp方法触发后发生了down,move,up等事件,则会执行这个方法
	//触发顺序:onDown -> onSingleUp -> onDoubleUp ->onDoubleTapEvent ->onDown -> 多个onDoubleTapEvent
        boolean onDoubleTapEvent(MotionEvent e);
    }

    //这个接口是用于监听一些外部设备的手势
    public interface OnContextClickListener {
        boolean onContextClick(MotionEvent e);
    }

了解完GestureDetector类的三个内部接口和一个静态内部类后,下面来看看,GestureDetector的构造方法

 public GestureDetector(Context context, OnGestureListener listener) {
        this(context, listener, null);
    }

    public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
        if (handler != null) {
            mHandler = new GestureHandler(handler);
        } else {
            mHandler = new GestureHandler();
        }
        mListener = listener;
        if (listener instanceof OnDoubleTapListener) {
            setOnDoubleTapListener((OnDoubleTapListener) listener);
        }
        if (listener instanceof OnContextClickListener) {
            setContextClickListener((OnContextClickListener) listener);
        }
        init(context);
    }

    //这个构造函数有些莫名其妙的,虽然要传入unused参数,但是传入的参数,并未使用,不知道是不是google的bug
    public GestureDetector(Context context, OnGestureListener listener, Handler handler,
            boolean unused) {
        this(context, listener, handler);
    }

从上面这三个构造函数来看,最终都是调用了三个参数的构造函数。下面来看看三个参数的构造函数的具体实现:

    public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
        if (handler != null) {
            mHandler = new GestureHandler(handler);
        } else {
            mHandler = new GestureHandler();
        }
        mListener = listener;
        if (listener instanceof OnDoubleTapListener) {
            setOnDoubleTapListener((OnDoubleTapListener) listener);
        }
        if (listener instanceof OnContextClickListener) {
            setContextClickListener((OnContextClickListener) listener);
        }
        init(context);
    }

从构造方法中,可以看出,构造方法内部,如果传递了handler,则将这个handler作为GestureHandler这个类的构造方法的参数创建一个handler对象,如果未传入handler对象,则使用GestureHandler创建一个hanler对象。将传入的OnGestureListener赋值给mLister,并调用init(context)方法进行一些相关的初始化操作。从GestureDetector的内部类GestureHandler的源码就可以看到,这两种创建handler的方式的唯一区别是,使用的Looper不同。下面看看GestureHandler这个类,它是GestureDetector的内部类,继承了Hander,其实就是一个Handler。

private class GestureHandler extends Handler {
        GestureHandler() {
            super();
        }

        GestureHandler(Handler handler) {
            super(handler.getLooper());
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case SHOW_PRESS:
                mListener.onShowPress(mCurrentDownEvent);
                break;
                
            case LONG_PRESS:
                dispatchLongPress();
                break;
                
            case TAP:
                // If the user's finger is still down, do not count it as a tap
                if (mDoubleTapListener != null) {
                    if (!mStillDown) {
                        mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
                    } else {
                        mDeferConfirmSingleTap = true;
                    }
                }
                break;

            default:
                throw new RuntimeException("Unknown message " + msg); //never
            }
        }
    }

如果我们是在主线程中直接使用两个参数的方式创建GestureDetector对象,则GestureDetector内部的handler使用的就是主线程的handler。如果在子线程中,创建GestureDetector对象,如果是使用两个参数的构造方法创建,则会报错
Java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
所以,在子线程使用GestureDetector时,要特别注意。主要注意在子线程使用handler。如果要在子线程使用创建GestureDetector对象,则可以有如下三种方式:

方式一:这种方式使用时,要注意onShowPress,onLongPress,onSingleTapConfirmed这三 个方法是在handleMessage方法中进行回调的,如果handler创建的线程是在子线程,则这三个方法在子线程回调的,所以,这三个方法中,不能处理ui的操作。具体代码如下:

 new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                gestureDetector = new GestureDetector(GestureDetectorActivity2.this,gestureListener);
                gestureDetector.setOnDoubleTapListener(onDoubleTapListener);
                Looper.loop();
            }
        }).start();

方式二:(用主线程的looper来创建handler,然后用GestureDetector的三个参数的构造函数来创建GestureDetector对象)

 new Thread(new Runnable() {
            @Override
            public void run() {
                Handler handler = new Handler(Looper.getMainLooper());
                gestureDetector = new GestureDetector(GestureDetectorActivity2.this,gestureListener,handler);
                gestureDetector.setOnDoubleTapListener(onDoubleTapListener);
            }
        }).start();

方式三:直接在主线程创建handler对象,在用这个handler去创建GestureDetector对象。

        final Handler handler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                gestureDetector = new GestureDetector(GestureDetectorActivity2.this,gestureListener,handler);
                gestureDetector.setOnDoubleTapListener(onDoubleTapListener);
            }
        }).start();

下面是直接在主线程中创建GestureDetector,有如下两种方式:
方式一:

        GestureDetector gestureDetector = new GestureDetector(GestureDetectorActivity2.this,gestureListener);
        gestureDetector.setOnDoubleTapListener(onDoubleTapListener);

方式二:(SimpleOnGestureListener里面的方法,按需实现,如果用不到,可以不去实现)

GestureDetector  gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDown(MotionEvent e) {
                return super.onDown(e);
            }

            @Override
            public void onShowPress(MotionEvent e) {
                super.onShowPress(e);
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return super.onSingleTapUp(e);
            }

            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                return super.onSingleTapConfirmed(e);
            }

            @Override
            public boolean onDoubleTap(MotionEvent e) {
                return super.onDoubleTap(e);
            }

            @Override
            public boolean onDoubleTapEvent(MotionEvent e) {
                return super.onDoubleTapEvent(e);
            }

            @Override
            public void onLongPress(MotionEvent e) {
                super.onLongPress(e);
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                return super.onScroll(e1, e2, distanceX, distanceY);
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                return super.onFling(e1, e2, velocityX, velocityY);
            }
        });

GestureDetector的原理就是:通过拦截onTouchEvent中的各个MotionEvent,在将自己定义的一套规则,比如什么是单击,什么是双击,来将各个MotionEvent做更加细致的划分,通过自己内部的handler,发送这些自己定义的规则的(比如单击事件)消息,然后在handlerMessage
方法中,调用GestureListener接口中相应的回调方法,来实现onShowPress,onLongPress,onSingleTapConfirmed操作的监听。在GestureDetector的onTouchEvent方法中,进行onDown,onSingleTapUp,onDoubleTap,onDoubleTapEvent,onScroll,onFling这些方法的回调。

使用步骤是:
1.创建GestureDetector.SimpleOnGestureListener simpleLinener = new GestureDetector.SimpleOnGestureListener(){//实现自己关心的对调方法}对象
2.创建GestureDetector对象
GestureDetector gestureDetector = new GestureDetector(simpleLinener);
3.在要监听的view的onTouchListener中处理MotionEvent的动作
tv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
return false;
}
});

参考:
安卓自定义View进阶-手势检测(GestureDetector)
https://www.gcssloop.com/customview/gestruedector

用户手势检测-GestureDetector使用详解
https://blog.csdn.net/harvic880925/article/details/39520901

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值