前言
当用户触摸屏幕的时候,会产生许多手势,例如down,up,scroll,filing等等。
为了监听上面各种手势,Android sdk给我们提供了GestureDetector类。
GestureDetector 是 Android 中,专门用来进行手势监听的一个对象,在他的监听器中,我们通过传入 MotionEvents 对象,就可以在各种事件的回调方法中各种手势进行监测。
下面我们学习一下GestureDetector的使用和注意事项。
GestureDetector的使用
GestureDetector这个类对外提供了两个接口(OnGestureListener和OnDoubleTapListener)和一个外部类(SimpleOnGestureListener)。
代码片段一
由于mGestureDetector的监听可能不同,所以在下面写其他的代码片段。
private GestureDetector mGestureDetector = null;
# 方式一
@Override
public boolean onTouch(View v, MotionEvent event) {
//mGestureDetector传入onTouchEvent事件
return mGestureDetector.onTouchEvent(event);
}
# 方式二
// 监听屏幕上响应的事件类型(按下,移动,抬起)
@Override
public boolean onTouchEvent(MotionEvent event) {
// 通过手势处理类,接收多种类型的事件,用作处理
mGestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
方式一和方式二看需求定义。
下面只简单的分析了单机和双击事件,只有滑动等暂没有分析。
OnGestureListener
监听一些手势,如单击、滑动、长按等操作。
- onDown(MotionEvent e):用户按下屏幕的时候的回调。
- onShowPress(MotionEvent e):用户按下按键后100ms(根据Android7.0源码)还没有松开或者移动就会回调,官方在源码的解释是说一般用于告诉用户已经识别按下事件的回调(我暂时想不出有什么用途,因为这个回调触发之后还会触发其他的,不像长按)。
- onLongPress(MotionEvent e):用户长按后(好像不同手机的时间不同,源码里默认是100ms+500ms)触发,触发之后不会触发其他回调,直至松开(UP事件)。
- onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):手指滑动的时候执行的回调(接收到MOVE事件,且位移大于一定距离),e1,e2分别是之前DOWN事件和当前的MOVE事件,distanceX和distanceY就是当前MOVE事件和上一个MOVE事件的位移量。
- onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY):用户执行抛操作之后的回调,MOVE事件之后手松开(UP事件)那一瞬间的x或者y方向速度,如果达到一定数值(源码默认是每秒50px),就是抛操作(也就是快速滑动的时候松手会有这个回调,因此基本上有onFling必然有onScroll)。
- onSingleTapUp(MotionEvent e):用户手指松开(UP事件)的时候如果没有执行onScroll()和onLongPress()这两个回调的话,就会回调这个,说明这是一个点击抬起事件,但是不能区分是否双击事件的抬起。
代码片段二
# 初始化mGestureDetector
mGestureDetector = new GestureDetector(mContext, new MyOnGestureListener());
代码片段三
/**
* OnGestureListener 接口的实现
*/
private class MyOnGestureListener implements GestureDetector.OnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
# 当按下时触发该方法,所有手势第一个必定触发该方法
# 如果不返回true,后续的事件就无法继续接收,所以这里需要置为true,表示继续监听。
return true;
}
@Override
public void onShowPress(MotionEvent e) {
# 手指按下,但没有移动时触发该方法
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
# 单击时触发弹起
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
# 当用户手指在屏幕上拖动时触发,后面两个变量时在X,Y上移动的距离
return false;
}
@Override
public void onLongPress(MotionEvent e) {
# 长按事件时触发
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
# 当用户手指拖动后,手指离开屏幕时触发
# 这个方法常用来使手指离开后页面仍然可以滑动(速度慢慢变小)
# 后两个变量表示手指在X,Y两个方向上的速度
return false;
}
}
- 如果onDown()返回的是false,后续收不到up事件,因此这个会当做一个长按事件来处理。
MyOnGestureListener onDown : 0 MyOnGestureListener onShowPress : 0 MyOnGestureListener onLongPress : 0
- 如果onDown()返回的是true,可以手动后续的up事件。
点击点击
MyOnGestureListener onDown : 0 MyOnGestureListener onShowPress : 0 MyOnGestureListener onSingleTapUp : 1
OnDoubleTapListener
监听双击和单击事件
- onSingleTapConfirmed(MotionEvent e):可以确认(通过单击DOWN后300ms没有下一个DOWN事件确认)这不是一个双击事件,而是一个单击事件的时候会回调。
- onDoubleTap(MotionEvent e):可以确认这是一个双击事件的时候回调。
- onDoubleTapEvent(MotionEvent e):onDoubleTap()回调之后的输入事件(DOWN、MOVE、UP)都会回调这个方法(这个方法可以实现一些双击后的控制,如让View双击后变得可拖动等)。
设置双击监听,有如下两种方式
- 新建一个类同时派生自OnGestureListener和OnDoubleTapListener
/**
* OnGestureListener 接口的实现 ,同时实现双击监听
*/
private class MyOnGestureListener implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {
//代码略
}
- 使用setOnDoubleTapListener()函数设置监听
mGestureDetector = new GestureDetector(mContext, new MyOnGestureListener()); mGestureDetector.setOnDoubleTapListener(ne yOnDoubleTapListener());
MyOnDoubleTapListener的监听(MyOnGestureListener的如上,不重复了)
/**
* 用于监听 双击还是单机
*/
private class MyOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
//确认是单击事件
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
//双击事件弹起
return false;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
// 双击事件(DOWN、MOVE、UP)
return false;
}
}
单机时的日志
MyOnGestureListener onDown : 0 MyOnGestureListener onShowPress : 0 MyOnGestureListener onSingleTapUp : 1 MyOnDoubleTapListener onSingleTapConfirmed : 0
双击时的日志
MyOnGestureListener onDown : 0 MyOnGestureListener onShowPress : 0 MyOnGestureListener onSingleTapUp : 1 MyOnDoubleTapListener onDoubleTap : 0 MyOnDoubleTapListener onDoubleTapEvent : 0 MyOnGestureListener onDown : 0 MyOnDoubleTapListener onDoubleTapEvent : 2 MyOnDoubleTapListener onDoubleTapEvent : 2 MyOnDoubleTapListener onDoubleTapEvent : 1
SimpleOnGestureListener
实现了上面三个接口的类,拥有上面三个的所有回调方法。
- 由于SimpleOnGestureListener不是抽象类,所以继承它的时候只需要选取我们所需要的回调方法来重写就可以了,非常方便,也减少了代码量,符合接口隔离原则,也是模板方法模式的实现。而实现上面的三个接口中的一个都要全部重写里面的方法,所以我们一般都是选择SimpleOnGestureListener。
集合了OnDoubleTapListener和OnGestureListener,用的频率比较多
mGestureDetector = new GestureDetector(mContext, new MySimpleOnGestureListener());
private class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onDown(MotionEvent e) {
// return super.onDown(e);
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return super.onDoubleTap(e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return super.onSingleTapUp(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return super.onSingleTapConfirmed(e);
}
@Override
public void onLongPress(MotionEvent e) {
super.onLongPress(e);
}
@Override
public void onShowPress(MotionEvent e) {
super.onShowPress(e);
}
}
