我们平常在使用App时,会作出各种手势,例如:单击进入新闻详情——单击、双击放大图片——双击、长按弹出菜单——长按、滑动新闻列表——滑动、多个手指放大缩小图片——缩放等等。我们如何判断一个控件是被作出了何种手势呢?这便用到了手势检测(GestureDetector)和缩放手势检测(ScaleGestureDetector)。
MotionEvent:直译是“动作事件”,它指的是在手指接触屏幕后所产生的一系列事件。典型的如:
ACTION_DOWN
:手指刚接触屏幕;ACTION_MOVE
:手指在屏幕上移动;ACTION_UP
:手指从屏幕上松开的一瞬间。通过MotionEvent对象我们可以得到点击事件发生的x和y坐标,为此系统提供了两种方法:
getX/getY
:返回的是相对于当前View左上角的x和y坐标;getRawX/getRawY
:返回的是相对于手机屏幕左上角的x和y坐标。在源码中,GestureDetector一共有5种构造函数,如下图所示:其中2种已被废弃,1种是重复的,所以只需关注其中2种构造方法即可。
通常情况下是不需要这个Handler的,因为在GestureDetector内部会自动创建一个Handler用于处理数据,但是若在子线程中创建,则需要Handler。
- 若在主线程中创建GestureDetector,那么它内部创建的Handler会自动获得主线程的Looper,不必创建Handler;
- 若在一个没有创建Looper的子线程中创建GestureDetector,则需要传递一个带有Looper的Handler给它,否则就会因为无法获取到Looper导致创建失败。而若要在子线程中创建GestureDetector则有3种方式:
- 在主线程中创建Handler;
- 在子线程中创建Handler并指定Looper;
- 若子线程准备了Looper,则不必传递Handler,可以直接使用第1种构造方法进行创建。
GestureDetector gestureDetector = new GestureDetector(this,
new GestureDetector.SimpleOnGestureListener());
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
new GestureDetector.SimpleOnGestureListener(), handler);
...
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Handler handler = new Handler(Looper.getMainLooper());
GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
new GestureDetector.SimpleOnGestureListener(), handler);
...
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//重点是这里,即在子线程中准备一个Looper
GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
new GestureDetector.SimpleOnGestureListener());
...
}
}).start();
既然是手势检测,那么自然会在对应的手势出现时通知调用者,这便需要监听器了。在源码中,GestureDetector有4种监听器:OnGestureListener、OnDoubleTapListener、OnContextClickListener以及继承以上三个接口的空实现类SimpleOnGestureListener
View.onGenericMotionEvent(MotionEvent)
中调用GestureDetector.OnGenericMotionEvent(MotionEvent)
;单击事件触发:
类型 | 触发次数 | 说明 |
---|---|---|
onSingleTapUp | 1 | 单击抬起 |
onSingleTapConfirmed | 1 | 单击确认 |
onClick | 1 | 单击事件 |
双击事件触发:
类型 | 触发次数 | 说明 |
---|---|---|
onSingleTapUp | 1 | 在双击的第一次抬起时触发 |
onSingleTapConfirmed | 0 | 双击发生时不会触发 |
onClick | 2 | 在双击事件时触发两次 |
补充:onShowPress和onSingleTapConfirmed类似,也是一种延时回调,延迟时间是180ms,若用户按下后立即抬起或时间立即被拦截,没有超过180ms的话,这条消息挥别remove掉,则不会触发该回调。
总结:以上重用的9种回调方法的调用时机如下表所示
回调/输入事件 | DOWN事件 | MOVE事件 | UP事件 |
---|---|---|---|
onDown | ✔ | ❌ | ❌ |
onShowPress | ✔ | ❌ | ❌ |
onLongPress | ✔ | ❌ | ❌ |
onScroll | ❌ | ✔ | ❌ |
onFling | ❌ | ❌ | ✔ |
onSingleTapUp | ❌ | ❌ | ✔ |
onSingleTapConfirmed | ❌ | ❌ | ✔ |
onDoubleTap | ✔ | ❌ | ❌ |
onDoubleTapEvent | ✔ | ✔ | ✔ |
GestureDetector.OnGestureListener
上的相应回调。如果GestureDetector.OnGestureListener
消费了事件则返回true,否则返回false。OnContextClickListener
服务的,暂时不用关注。手势检测的使用大致分为3个步骤:
SimpleOnGestureListener
比较方便);//1.创建监听回调
GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDoubleTap(MotionEvent e) {
Toast.makeText(MainActivity.this, "双击", Toast.LENGTH_SHORT).show();
return super.onDoubleTap(e);
}
};
//2.创建一个GestureDetector
final GestureDetector gestureDetector = new GestureDetector(this, listener);
//3.给控件设置监听器,并重写onTouch方法
testButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
同GestureDetector类似,ScaleGestureDetector也有2种构造方法:
ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener)
ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener, Handler handler)
注:有关ScaleGestureDetector的使用,请参见上面的GestureDetector
参考资料: