(4.1.40.5)Android手势检测GestureDecetor详解

手势检测还有一个 ScaleGestureDetector 也是为手势检测服务的,限于篇幅本文未讲述

在开发 Android 手机应用过程中,可能需要对一些手势作出响应,如:单击、双击、长按、滑动、缩放等,这些都是很常用的手势。 如果我们对触摸事件自己去做处理,估计要写一堆代码,所幸,系统早就封装了一些方法给我们用

一、示例

  1. 创建一个 GestureDetectorListener手势检测监听实例
  2. 创建一个 GestureDetector 手势检测实例,并绑定自定义的监听
  3. 在onTouchEvent(MotionEvent)方法中,确保调用 GestureDetector 实例的 onTouchEvent(MotionEvent)。回调中定义的方法将在事件发生时执行。
  4. 如果侦听 onContextClick(MotionEvent),则必须在 View 的 onGenericMotionEvent(MotionEvent)中调用 GestureDetector OnGenericMotionEvent(MotionEvent)
// 1.创建一个监听回调
SimpleOnGestureListener listener = new SimpleOnGestureListener() {
    @Override public boolean onDoubleTap(MotionEvent e) {
        Toast.makeText(MainActivity.this, "双击666", Toast.LENGTH_SHORT).show();
        return super.onDoubleTap(e);
    }
};

// 2.创建一个检测器
final GestureDetector detector = new GestureDetector(this, listener);

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

二、GestureDetector 手势检测

GestureDetector(Context context, GestureDetector.OnGestureListener listener)
GestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler)

第 1 种构造函数里面需要传递两个参数,上下文(Context) 和 手势监听器(OnGestureListener),这个很容易理解,就不再过多叙述,上面的例子中使用的就是这一种

第 2 种构造函数则需要多传递一个 Handler 作为参数,这个有什么作用呢?其实作用也非常简单,这个 Handler 主要是为了给 GestureDetector 提供一个 Looper

在通常情况下是不需这个 Handler 的,因为它会在内部自动创建一个 Handler 用于处理数据,如果你在主线程中创建 GestureDetector,那么它内部创建的 Handler 会自动获得主线程的 Looper,然而如果你在一个没有创建 Looper 的子线程中创建 GestureDetector 则需要传递一个带有 Looper 的 Handler 给它,否则就会因为无法获取到 Looper 导致创建失败

// 方式一、在主线程创建 Handler
final Handler handler = new Handler();
new Thread(new Runnable() {
    @Override public void run() {
        final GestureDetector detector = new GestureDetector(MainActivity.this, new
                GestureDetector.SimpleOnGestureListener() , handler);
        // ... 省略其它代码 ...
    }
}).start();

// 方式二、在子线程创建 Handler,并且指定 Looper
new Thread(new Runnable() {
    @Override public void run() {
        final Handler handler = new Handler(Looper.getMainLooper());
        final GestureDetector detector = new GestureDetector(MainActivity.this, new
                GestureDetector.SimpleOnGestureListener() , handler);
        // ... 省略其它代码 ...
    }
}).start();

三、GestureDetectorListener手势检测监听器

既然是手势检测,自然要在对应的手势出现的时候通知调用者,最合适的自然是事件监听器模式。目前 GestureDetecotr 有四种监听器。

监听器简介
OnContextClickListener这个很容易让人联想到ContextMenu,然而它和ContextMenu并没有什么关系,它是在Android6.0(API 23)才添加的一个选项,是用于检测外部设备上的按钮是否按下的,例如蓝牙触控笔上的按钮,一般情况下,忽略即可。
OnDoubleTapListener双击事件,有三个回调类型:双击(DoubleTap)、单击确认(SingleTapConfirmed) 和 双击事件回调(DoubleTapEvent)
OnGestureListener手势检测,主要有以下类型事件:按下(Down)、 一扔(Fling)、长按(LongPress)、滚动(Scroll)、触摸反馈(ShowPress) 和 单击抬起(SingleTapUp)
SimpleOnGestureListener这个是上述三个接口的空实现,一般情况下使用这个比较多,也比较方便

3.1 OnGestureListener手势检测监听

这个是手势检测中较为核心的一个部分了,主要检测以下类型事件:

  • 按下(Down)、
  • 一扔(Fling)
  • 长按(LongPress)
  • 滚动(Scroll)
  • 触摸反馈(ShowPress)
  • 单击抬起(SingleTapUp)

3.1.1 onDown按下

@Override public boolean onDown(MotionEvent e) {
    return true;
}

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

  • 让该 View 可以点击,因为可点击状态会默认消费 down 事件
  • 手动消费掉 down 事件,返回true

由于图片、文本等一些控件默认是不可点击的,所以我们要么声明它们的 clickable 为 true,要么在发生 down 事件是返回 true。所以 onDown 在这里的作用就很明显了,就是为了保证让该控件能拥有消费事件的能力,以接受后续的事件

3.1.2 Fling滑动

Fling 中文直接翻译过来就是一扔、抛、甩,最常见的场景就是在 ListView 或者 RecyclerView 上快速滑动时手指抬起后它还会滚动一段时间才会停止。onFling 就是检测这种手势的

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

e1 和 e2 获取到手指按下和抬起时的坐标、时间等相关信息,通过 velocityX 和 velocityY 获取到在这段时间内的运动速度,单位是像素/秒(即 1 秒内滑动的像素距离)

参数简介
e1手指按下时的 Event。
e2手指抬起时的 Event。
velocityX在 X 轴上的运动速度(像素/秒)。
velocityY在 Y 轴上的运动速度(像素/秒)。

3.1.4 LongPress长按

这个是检测长按事件的,即手指按下后不抬起,在一段时间后会触发该事件

@Override 
public void onLongPress(MotionEvent e) {
}

3.1.5 Scroll滚动

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

onScroll 就是监听滚动事件的,它看起来和 onFling 比较像,不同的是,onSrcoll 后两个参数不是速度,而是滚动的距离

参数x
e1手指按下时的Event
e2手指抬起时的Event
distanceX在 X 轴上划过的距离
distanceY在 Y 轴上划过的距离

3.1.6 onShowPress触摸反馈

@Override 
public void onShowPress(MotionEvent e) {
}

它是用户按下时的一种回调,主要作用是给用户提供一种视觉反馈,可以在监听到这种事件时可以让控件换一种颜色,或者产生一些变化,告诉用户他的动作已经被识别

不过这个消息和 onSingleTapConfirmed 类似,也是一种延时回调,延迟时间是 180 ms,假如用户手指按下后立即抬起或者事件立即被拦截,时间没有超过 180 ms的话,这条消息会被 remove 掉,也就不会触发这个回调

3.1.7 onSingleTapUp单击抬起

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

这个也很容易理解,就是用户单击抬起时的回调,但是它和上面的 onSingleTapConfirmed 之间有何不同呢?和 onClick 又有何不同呢?

单击事件触发:

GCS: onSingleTapUp
GCS: onClick
GCS: onSingleTapConfirmed
类型触发次数摘要
onSingleTapUp1单击抬起
onSingleTapConfirmed1单击确认
onClick1单击事件

双击事件触发:

GCS: onSingleTapUp
GCS: onClick
GCS: onDoubleTap // <- 双击
GCS: onClick
类型触发次数摘要
onSingleTapUp1在双击的第一次抬起时触发
onSingleTapConfirmed0双击发生时不会触发。
onClick2在双击事件时触发两次。

3.2 OnDoubleTapListener双击事件监听

这个很明显就是用于检测双击事件的,它有三个回调接口,分别是 onDoubleTap、onDoubleTapEvent 和 onSingleTapConfirmed

3.2.1 onDoubleTap双击监听 与 onSingleTapConfirmed单击监听

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

有人可能会有疑问,监听单击事件为什么要使用 onSingleTapConfirmed,使用 OnClickListener 不行吗?从理论上是可行的,但是并不推荐这样使用,主要有两个原因:

  1. 它们两个是存在一定冲突的,如果你看过 事件分发机制详解 就会知道,如果想要两者同时被触发,则 setOnTouchListener 不能消费事件,如果 onTouchListener 消费了事件,就可能导致 OnClick 无法正常触发。
  2. 需要同时监听单击和双击,则说明单击和双击后响应逻辑不同,然而使用 > OnClickListener 会在双击事件发生时触发两次,这显然不是我们想要的结果。而使用 onSingleTapConfirmed 就不用考虑那么多了,你完全可以把它当成单击事件来看待,而且在双击事件发生时,onSingleTapConfirmed 不会被调用,这样就不会引发冲突。
  • onDoubleTap 和 onSingleTapConfirmed 只会有一个被触发
    • onSingleTapConfirmed 这一个回调函数在单击事件发生后300ms后触发(注意,不是立即触发的),只有在确定不会有后续的事件后,既当前事件肯定是单击事件才触发 onSingleTapConfirmed

3.2.2 onDoubleTapEvent双击后半击事件监听

它在双击事件确定发生时会对第二次按下产生的 MotionEvent 信息进行回调,是一种实时回调

GCS-LOG: onDoubleTap
GCS-LOG: onDoubleTapEvent - down
GCS-LOG: onDoubleTapEvent - move
GCS-LOG: onDoubleTapEvent - move
GCS-LOG: onDoubleTapEvent - up

第二次按下手指时,即便不抬起也会触发 onDoubleTap 和 onDoubleTapEvent 的 down,而且源码中逻辑也表明 onDoubleTapEvent 是一种实时回调

3.4 SimpleOnGestureListener

对其他几种坚挺的空实现

四、相关方法

方法摘要
setIsLongpressEnabled通过布尔值设置是否允许触发长按事件,true 表示允许,false 表示不允许。
isLongpressEnabled判断当前是否允许触发长按事件,true 表示允许,false 表示不允许。
onTouchEvent这个是其中一个重要的方法,在最开始已经演示过使用方式了。
onGenericMotionEvent这个是在 API 23 之后才添加的内容,主要是为 OnContextClickListener 服务的,暂时不用关注。
setContextClickListener设置 ContextClickListener 。
setOnDoubleTapListener设置 OnDoubleTapListener 。

参考文献

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值