Google为了方便我们进行手势检测,早就封装了相关的内容提供我们使用。即Android手势检测,主要就是对GestureDetector类的使用和说明。

1.GestureDetector

GestureDetector可以使用MotionEvents检测各种手势和事件。其内部包含四个监听器(三个接口以及一个外部类):

接口 含义
OnGestureListener 手势检测:包括 按下(Down) 扔(Fling) 长按(LongPress) 滚动(Scroll) 触摸反馈(ShowPress) 单击抬起(SingleTapUp)
OnDoubleTapListener 双击事件:有三个回调方法 双击(DoubleTap) 单击确认(SingleTapConfirmed) 双击事件回调(DoubleTapEvent)
OnContextClickListener 它是在Android6.0(API 23)才添加的一个选项,是用于检测外部设备上的按钮是否按下的,例如蓝牙触控笔上的按钮,一般情况下,忽略即可。
SimpleOnGestureListener 三个接口的空实现,使用较为方便

GestureDetector常用的构造函数:

构造函数
public GestureDetector(Context context, OnGestureListener listener, Handler handler)
public GestureDetector(Context context, OnGestureListener listener)

第二种需要传递上下文和手势监听器,没什么好讲的;关于第一种,传递一个Handler只是为了给GestureDetector手势监听器提供一个Looper。通常不需要传递,因为在内部会自己生成一个Handler用来传递消息,如果在主线程创建GestureDetector则会自己获取主线程中的Looper。如果在没有创建Looper的子线程中创建GestureDetector则会报错。

第二种构造函数的使用以及注意事项:

方式一

	    //在主线程中创建Handler,Handler会自动获取主线程的Looper
        final Handler handler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                GestureDetector detector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener(){

                },handler);
            }
        }).start();

方式二

        //方式二 子线程中创建Handler并指定Looper
        new Thread(new Runnable() {
            @Override
            public void run() {
                Handler handler1 = new Handler(Looper.getMainLooper());
                GestureDetector detector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener(){

                },handler1);
            }
        }).start();

第一种构造函数在子线程种的使用

子线程中准备了Looper可使用第一种构造函数

 new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                GestureDetector detector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener(){

                });
            }
        }).start();

注意: 传递的Handler必须带有Looper

2.GestureDetector使用

  1. 创建一个监听回调
  2. 创建一个检测器
  3. 给检测器设置数据源

例子:

//监听回调
 GestureDetector.OnGestureListener listener = new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                Log.e(TAG,"onDown");
                return true;
            }

            @Override
            public void onShowPress(MotionEvent e) {
                Log.e(TAG,"onShowPress");
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                Log.e(TAG,"onSingleTapUp");
                return true;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                Log.e(TAG,"onScroll");
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                Log.e(TAG,"onLongPress");
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                Log.e(TAG,"onFling");
                return true;
            }
        };

		//创建监听器
        final GestureDetector detector = new GestureDetector(this,listener);
      

		//设置数据源
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return detector.onTouchEvent(event);
            }
        });

如果想单独设置OnDoubleTapListener的话直接调用:

  detector.setOnDoubleTapListener(new DoubleTapListener());

前面提到SimpleOnGestureListener实现了三个接口,因此我们使用时通常都是使用它来构建监听器的。。

接下来通过各种不同的手势动作来观察事件的回调顺序。使用SimpleOnGestureListener重写全部的空方法,并添加日志。

final GestureDetector detector = new GestureDetector(this,new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDown(MotionEvent e) {
                Log.e(TAG,"onDown");
                return true;
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                Log.e(TAG,"onFling");
                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                Log.e(TAG,"onLongPress");
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                Log.e(TAG,"onScroll");
                return false;
            }

            @Override
            public void onShowPress(MotionEvent e) {
                super.onShowPress(e);
                Log.e(TAG,"onShowPress");
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                Log.e(TAG,"onSingleTapUp");
                return super.onSingleTapUp(e);
            }

            @Override
            public boolean onDoubleTap(MotionEvent e) {
                Log.e(TAG,"onDoubleTap");
                return false;
            }

            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                Log.e(TAG,"onSingleTapConfirmed");
                return false;
            }

            @Override
            public boolean onDoubleTapEvent(MotionEvent e) {
                Log.e(TAG,"onDoubleTapEvent");
                return false;
            }
        });
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return detector.onTouchEvent(event);
            }
        });

Android自定义View-手势检测(GestureDetector)

Android自定义View-手势检测(GestureDetector)

从上面的图大概可以看出每个回调方法的作用以及调用时刻:

方法 时间
onDown(MotionEvent e) 按下屏幕就会触发
onShowPress(MotionEvent e) 如果是按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动
onLongPress(MotionEvent e) 长按触摸时触发
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) 滑动触发,x y 表示移动的距离
onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 快速移动时松开触发 x,y是XY轴的移动速度
onSingleTapUp(MotionEvent e) 单击松开触发
onDoubleTap(MotionEvent e) 双击第二次按下就会触发
onSingleTapConfirmed(MotionEvent e) 单击事件
onDoubleTapEvent(MotionEvent e) 双击事件触发(可判断move up down 情况)

如果你只想监听双击事件,那么只用关注 onDoubleTap 就行了,如果你同时要监听单击事件则需要关注 onSingleTapConfirmed 这个回调函数。

有人可能会有疑问,监听单击事件为什么要使用 onSingleTapConfirmed,使用 OnClickListener 不行吗?主要有两个原因:
1.它们两个是存在一定冲突的,如果想要两者同时被触发,则 setOnTouchListener 不能消费事件,如果 onTouchListener 消费了事件,就可能导致 OnClick 无法正常触发。
2.需要同时监听单击和双击,则说明单击和双击后响应逻辑不同,然而使用 OnClickListener 会在双击事件发生时触发两次,这显然不是我们想要的结果。而使用 onSingleTapConfirmed 就不用考虑那么多了,你完全可以把它当成单击事件来看待,而且在双击事件发生时,onSingleTapConfirmed 不会被调用,这样就不会引发冲突。

假如说我们想要在第二次按下抬起后才判定这是一个双击操作,触发后续的内容,则不能使用 onDoubleTap 了,需要使用 onDoubleTapEvent 来进行更细微的控制:

final GestureDetector detector = new GestureDetector(MainActivity.this, new GestureDetector.SimpleOnGestureListener() {
    @Override public boolean onDoubleTap(MotionEvent e) {
        Logger.e("第二次按下时触发");
        return super.onDoubleTap(e);
    }

    @Override public boolean onDoubleTapEvent(MotionEvent e) {
        switch (e.getActionMasked()) {
            case MotionEvent.ACTION_UP:
                Logger.e("第二次抬起时触发");
                break;
        }
        return super.onDoubleTapEvent(e);
    }
});

down 在事件分发体系中是一个较为特殊的事件,为了保证事件被唯一的 View 消费,哪个 View 消费了 down 事件,后续的内容就会传递给该 View。如果我们想让一个 View 能够接收到事件,有两种做法:

1、让该 View 可以点击,因为可点击状态会默认消费 down 事件。

 view.setClickable(true);

2、手动消费掉 down 事件。

@Override
            public boolean onDown(MotionEvent e) {
                Log.e(TAG,"onDown");
                return true;
            }

参考:
https://www.cnblogs.com/ldq2016/p/7000300.html
http://www.gcssloop.com/customview/gestruedector

相关文章: