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使用
- 创建一个监听回调
- 创建一个检测器
- 给检测器设置数据源
例子:
//监听回调
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);
}
});
从上面的图大概可以看出每个回调方法的作用以及调用时刻:
方法 | 时间 |
---|---|
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