文章目录
0 简单认识
0.1 应用场景
我们平常在使用App时,会作出各种手势,例如:单击进入新闻详情——单击、双击放大图片——双击、长按弹出菜单——长按、滑动新闻列表——滑动、多个手指放大缩小图片——缩放等等。我们如何判断一个控件是被作出了何种手势呢?这便用到了手势检测(GestureDetector)和缩放手势检测(ScaleGestureDetector)。
0.2 MotionEvent
MotionEvent:直译是“动作事件”,它指的是在手指接触屏幕后所产生的一系列事件。典型的如:
ACTION_DOWN
:手指刚接触屏幕;ACTION_MOVE
:手指在屏幕上移动;ACTION_UP
:手指从屏幕上松开的一瞬间。
通过MotionEvent对象我们可以得到点击事件发生的x和y坐标,为此系统提供了两种方法:
getX/getY
:返回的是相对于当前View左上角的x和y坐标;getRawX/getRawY
:返回的是相对于手机屏幕左上角的x和y坐标。
1 GestureDetector
1.1 构造方法
在源码中,GestureDetector一共有5种构造函数,如下图所示:其中2种已被废弃,1种是重复的,所以只需关注其中2种构造方法即可。
- 第1种构造方法里需要传递两个参数,上下文(Context)和手势监听器(OnGestureListener)
- 第2种构造方法则需要在第一种的基础上多传递一个Handler对象作为参数,这个Handler主要是为了给GestureDetector提供一个Looper。
通常情况下是不需要这个Handler的,因为在GestureDetector内部会自动创建一个Handler用于处理数据,但是若在子线程中创建,则需要Handler。
- 若在主线程中创建GestureDetector,那么它内部创建的Handler会自动获得主线程的Looper,不必创建Handler;
- 若在一个没有创建Looper的子线程中创建GestureDetector,则需要传递一个带有Looper的Handler给它,否则就会因为无法获取到Looper导致创建失败。而若要在子线程中创建GestureDetector则有3种方式:
- 在主线程中创建Handler;
- 在子线程中创建Handler并指定Looper;
- 若子线程准备了Looper,则不必传递Handler,可以直接使用第1种构造方法进行创建。
- 在主线程中创建GestureDetector,使用构造方法1即可
GestureDetector gestureDetector = new GestureDetector(this,
new GestureDetector.SimpleOnGestureListener());
- 在子线程中创建GestureDetector-方式1:在主线程中创建Handler
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
new GestureDetector.SimpleOnGestureListener(), handler);
...
}
}).start();
- 在子线程中创建GestureDetector-方式2:在子线程中创建Handler并指定Looper
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();
- 在子线程中创建GestureDetector-方式3:在子线程中准备Looper,直接使用第一种构造方法来进行创建
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//重点是这里,即在子线程中准备一个Looper
GestureDetector gestureDetector = new GestureDetector(MainActivity.this,
new GestureDetector.SimpleOnGestureListener());
...
}
}).start();
1.2 监听器
既然是手势检测,那么自然会在对应的手势出现时通知调用者,这便需要监听器了。在源码中,GestureDetector有4种监听器:OnGestureListener、OnDoubleTapListener、OnContextClickListener以及继承以上三个接口的空实现类SimpleOnGestureListener
- OnContextClickListener:用于检测外部设备上的按钮是否按下(例如蓝牙触控笔上的按钮)。
- onContextClick(MotionEvent e):若要监听此该事件,则必须在
View.onGenericMotionEvent(MotionEvent)
中调用GestureDetector.OnGenericMotionEvent(MotionEvent)
;
- onContextClick(MotionEvent e):若要监听此该事件,则必须在
- OnDoubleTapListener:监听单击和双击事件。
- onSingleTapConfirmed(MotionEvent e):确认不是双击(通过单击DOWN后300ms没有下一个DOWN事件确认)而是单击事件发生时会回调;
- onDoubleTap(MotionEvent e):确认是一个双击事件后会回调,并且在第二次按下手指时,即便不抬起也会触发 onDoubleTap 和 onDoubleTapEvent 的 DOWN
- onDoubleTapEvent(MotionEvent e):在双击事件确定发生时会对第2次按下产生的MotionEvent(DOWN、MOVE、UP)信息进行回调,这个方法用于在双击后进行更细微的控制。
- OnGestureListener:监听一些手势:如单击、长按、滑动等动作。
- onDown(MotionEvent e):用户按下屏幕时的回调;
- onShowPress(MotionEvent e):用户按下按键后100ms还没有松开或者移动就会回调,主要作用是给用户提供一种视觉反馈,可以在监听到该事件时让控件换一种颜色或者产生一些变化,告诉用户它的动作已被识别;
- onLongPress(MotionEvent e):用户长按后会触发(默认为100ms+500ms),触发之后不会触发其他回调,直至松开(UP事件);
- onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):监听滚动事件,在手指滑动的时候回调——接收到MOVE事件,且位移大于一定距离。其中,e1和e2分别是手指按下时的Event和手指抬起时的Event。distanceX和distanceY就是在 X 轴上划过的距离和在 Y 轴上划过的距离。
- onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):直译是——扔、抛、甩,它是在MOVE事件之后手松开(UP事件)那一瞬间在x/y方向上仍有一定的速度,如果达到一定的速度(默认50px/s),就是该操作。最常见的场景是:在ListView或者RecyclerView上快速滑动时手指抬起后列表还会滚动一段事件才会停止,因此有onFling必有onScroll。与上面的面的onScroll类似,只不过onScroll后两个参数是距离,而onFling后两个参数是速度;
- onSingleTapUp(MotionEvent e):用户单击抬起时的回调。
onSingleTapUp和onSingleTapConfirmed有何不同呢?具体看下面两个表格:
单击事件触发:
类型 | 触发次数 | 说明 |
---|---|---|
onSingleTapUp | 1 | 单击抬起 |
onSingleTapConfirmed | 1 | 单击确认 |
onClick | 1 | 单击事件 |
双击事件触发:
类型 | 触发次数 | 说明 |
---|---|---|
onSingleTapUp | 1 | 在双击的第一次抬起时触发 |
onSingleTapConfirmed | 0 | 双击发生时不会触发 |
onClick | 2 | 在双击事件时触发两次 |
补充:onShowPress和onSingleTapConfirmed类似,也是一种延时回调,延迟时间是180ms,若用户按下后立即抬起或时间立即被拦截,没有超过180ms的话,这条消息挥别remove掉,则不会触发该回调。
- SimpleOnGestureListener:是上述3个接口的空实现,一般情况下使用这个比较多,也比较方便。
总结:以上重用的9种回调方法的调用时机如下表所示
回调/输入事件 | DOWN事件 | MOVE事件 | UP事件 |
---|---|---|---|
onDown | ✔ | ❌ | ❌ |
onShowPress | ✔ | ❌ | ❌ |
onLongPress | ✔ | ❌ | ❌ |
onScroll | ❌ | ✔ | ❌ |
onFling | ❌ | ❌ | ✔ |
onSingleTapUp | ❌ | ❌ | ✔ |
onSingleTapConfirmed | ❌ | ❌ | ✔ |
onDoubleTap | ✔ | ❌ | ❌ |
onDoubleTapEvent | ✔ | ✔ | ✔ |
1.3 常用方法
- setIsLongpressEnabled:设置是否启用长按,如果在用户按住时启用了长按,您将获得一个长按事件,没有其他事情。如果它被禁用,用户可以按住,然后移动他们的手指,你会得到滚动事件。默认情况下,长按处于启用状态。
- isLongpressEnabled:判断当前是否允许触发长按事件,true 表示允许,false 表示不允许。
- onTouchEvent:分析给定的运动事件,如果适用,则触发所提供的
GestureDetector.OnGestureListener
上的相应回调。如果GestureDetector.OnGestureListener
消费了事件则返回true,否则返回false。 - onGenericMotionEvent:若要监听外部设备上的按钮是否被按下,则需要调用该方法。主要是为
OnContextClickListener
服务的,暂时不用关注。 - setContextClickListener:设置 ContextClickListener。
- setOnDoubleTapListener:设置 OnDoubleTapListener。
1.4 简单使用
手势检测的使用大致分为3个步骤:
- 创建一个监听器,并在其中重写需要监听的事件(一般使用
SimpleOnGestureListener
比较方便); - 创建一个GestureDetector对象(根据在主线程或子线程中创建而使用不同的构造方法);
- 给控件设置监听器,并重写onTouch方法。
//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);
}
});
2 ScaleGestureDetector
2.1 构造方法
同GestureDetector类似,ScaleGestureDetector也有2种构造方法:
ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener)
ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener, Handler handler)
2.2 缩放手势监听器及常用方法
- OnScaleGestureListener:缩放手势监听器
- onScaleBegin(ScaleGestureDetector detector):缩放手势开始,当两个手指放在屏幕上的时候会调用该方法(只调用一次)。如果返回 false 则表示不使用当前这次缩放手势。
- onScale(ScaleGestureDetector detector):缩放被触发(会调用0次或者多次)如果返回 true 则表示当前缩放事件已经被处理,检测器会重新积累缩放因子,返回 false 则会继续积累缩放因子。
- onScaleEnd(ScaleGestureDetector detector):缩放手势结束。
- SimpleOnScaleGestureListener:缩放手势监听器的空实现
注:有关ScaleGestureDetector的使用,请参见上面的GestureDetector
参考资料: