一、概述
GestureDetector,放进去一个触摸事件,提供回调一系列触摸动作接口的用于手势检测的工具类(大概是怎么个东西吧)。
二、实例化对象
上面三个呢,就不说了,我们主要来看第四个。
@Deprecated
public GestureDetector(OnGestureListener listener, Handler handler) {
this(null, listener, handler);
}
public GestureDetector(OnGestureListener listener) {
this(null, listener, null);
}
public GestureDetector(Context context, OnGestureListener listener) {
this(context, listener, null);
}
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
//这里对handler判空,构造GestureHandler对象。
if (handler != null) {
mHandler = new GestureHandler(handler);
} else {
mHandler = new GestureHandler();
}
mListener = listener;
//这里下面两个if我不太能理解是什么意思,在我看来这两个应该永远都if不进去。因为OnGestureListener和 OnDoubleTapListener以及OnContextClickListener是平级的接口,不存在相互的继承关系。
if (listener instanceof OnDoubleTapListener) {
setOnDoubleTapListener((OnDoubleTapListener) listener);
}
if (listener instanceof OnContextClickListener) {
setContextClickListener((OnContextClickListener) listener);
}
//在这里初始化一些东西啦
init(context);
}
我们来看构造的GestureHandler对象是干嘛的,
private class GestureHandler extends Handler {
GestureHandler() {
super();
}
GestureHandler(Handler handler) {
super(handler.getLooper());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PRESS:
//这里回调了一个onShowPress方法(轻触摸屏)
mListener.onShowPress(mCurrentDownEvent);
break;
case LONG_PRESS:
//这里里面回调了一个onLongPress方法(长按触摸屏)
dispatchLongPress();
break;
case TAP:
if (mDoubleTapListener != null) {
if (!mStillDown) {
//这里回调了一个onSingleTapConfirmed方法(单击事件)
mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
} else {
mDeferConfirmSingleTap = true;
}
}
break;
default:
throw new RuntimeException("Unknown message " + msg); //never
}
}
}
来看看初始化了啥东西
private void init(Context context) {
//常规的判空抛异常
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
//长按,可
mIsLongpressEnabled = true;
int touchSlop, doubleTapSlop, doubleTapTouchSlop;
//这里context为空想不出应用场景跳过不看
if (context == null) {
touchSlop = ViewConfiguration.getTouchSlop();
doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
} else {
//这里用View的常量配置类ViewConfiguration获取View的一些基本配置参数,保存备用。
final ViewConfiguration configuration = ViewConfiguration.get(context);
//表示系统滑动距离的最小值(大于该值,可以认为滑动)
touchSlop = configuration.getScaledTouchSlop();
//表示点击的时候,手指移动距离的最大值(大于该值,就被认为不可能是双击)
doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
//表示第二次点击的时候,和第一次的点击点位置距离的最大值(大于该值,就被认为不是双击)
doubleTapSlop = configuration.getScaledDoubleTapSlop();
//获得允许执行fling (抛)的最小速度值
mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
//获得允许执行fling (抛)的最大速度值
mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
}
//这里存储它们平方值,后面做移动距离平方的比较,不清楚为什么要用平方,可能是精度之类的吧
mTouchSlopSquare = touchSlop * touchSlop;
mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
}
三、事件回调
OnGestureListener
onDown(按下)
是常见的那种按下事件回调。
@Override
public boolean onDown(MotionEvent e) {
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
handled |= mListener.onDown(ev);
break;
}
return handled;
}
onShowPress(按下后100ms不动)
按下后定时回调,TAP_TIMEOUT为系统默认100ms
@Override
public void onShowPress(MotionEvent e) {
}
public boolean onTouchEvent(MotionEvent ev) {
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//getDownTime()按下的当前系统时间,TAP_TIMEOUT为系统默认100ms
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
break;
}
}
onSingleTapUp(轻按抬起)
手指抬起时,如果没有执行onScroll()和onLongPress()的话,会回调这个,但是注意这里指的是抬起,意味着双击事件的抬起也会触发这个。
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
//mAlwaysInTapRegion: ACTION_MOVE、ACTION_CANCEL、ACTION_POINTER_DOWN时会赋值false
//mIgnoreNextUpEvent: 鼠标右键等外设设备会赋值true
if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) {
handled = mListener.onSingleTapUp(ev);
}
break;
}
return handled;
}
onScroll(滑动)
手指滑动的时候执行的回调,e1,e2分别是之前DOWN事件和当前的MOVE事件,distanceX和distanceY就是当前MOVE事件和上一个MOVE事件的位移量。
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE:
//mAlwaysInTapRegion: ACTION_MOVE、ACTION_CANCEL、ACTION_POINTER_DOWN时会赋值false
if (mAlwaysInTapRegion) {
//X轴和Y轴方向移动距离的平方和大于最小滑动距离的平方,则滑动开始
if (distance > slopSquare) {
//滑动开始
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mAlwaysInTapRegion = false;
}
}else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
//滑动ing
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
}
break;
}
return handled;
}
onLongPress(长按)
是常见的那种长按事件回调。
@Override
public void onLongPress(MotionEvent e) {
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//是否可长按
if (mIsLongpressEnabled) {
//去除掉之前的LongPress
mHandler.removeMessages(LONG_PRESS);
//定时触发新的的LongPress, LONGPRESS_TIMEOUT: 100ms+500ms
mHandler.sendEmptyMessageAtTime(LONG_PRESS,
mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
}
break;
}
return handled;
}
onFling(抛)
这里的滑动速度的判断不是整个冲down开始到up,是你设置的computeCurrentVelocity()来看的,这里设置1000ms,就是up事件之前的1000ms内的移动距离。
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
//这里有个滑动速度跟踪器VelocityTracker,一般用于自定义控件的一些粘性操作效果。
final VelocityTracker velocityTracker = mVelocityTracker;
final int pointerId = ev.getPointerId(0);
//设置检测时间1000ms
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
//获取1000ms内Y轴上移动的像素
final float velocityY = velocityTracker.getYVelocity(pointerId);
//获取1000ms内X轴上移动的像素
final float velocityX = velocityTracker.getXVelocity(pointerId);
//判断一下X轴和Y轴上的移动速度,确定是否是抛操作
if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
}
break;
}
return handled;
}
OnDoubleTapListener
onSingleTapConfirmed(单击)
这里单击之间的间隔时间要大于300ms,慢一点,单击点太快了就变成了双击事件咯
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//如果300ms没有下一个down则当前为单击事件
if (mDoubleTapListener != null) {
//判断有没有down事件
boolean hadTapMessage = mHandler.hasMessages(TAP);
//有的话就Remove掉
if (hadTapMessage) mHandler.removeMessages(TAP);
//这里进去双击
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
mIsDoubleTapping = true;
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
//这里进去单击,延时300ms,期间没有第二次down则为单击事件
} else {
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
break;
case MotionEvent.ACTION_UP:
//如果300ms没有下一个down则当前为单击事件
if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
mDoubleTapListener.onSingleTapConfirmed(ev);
}
break;
}
return handled;
}
onDoubleTap(双击事件第二次单击时的按下)
这里并不是我们想象的那种双击,即第二次点击抬起触发,它是在双击中第二次单击时的按下回调的,所以我这里的标题写的是双击中第二次单击时的按下,而不是双击
@Override
public boolean onDoubleTap(MotionEvent e) {
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//如果300ms没有下一个down则当前为单击事件
if (mDoubleTapListener != null) {
//判断有没有down事件
boolean hadTapMessage = mHandler.hasMessages(TAP);
//有的话就Remove掉
if (hadTapMessage) mHandler.removeMessages(TAP);
//这里进去双击
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
mIsDoubleTapping = true;
//触发双击事件第二次单击时的按下
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
//这里进去单击,延时300ms,期间没有第二次down则为单击事件
} else {
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
break;
return handled;
}
onDoubleTapEvent(双击事件第二次单击时的不同事件)
这里的回调都是在onDoubleTap()回调之后的输入事件(DOWN、MOVE、UP),可以做一些如双击拖动之类的特殊操作
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
//如果300ms没有下一个down则当前为单击事件
if (mDoubleTapListener != null) {
//判断有没有down事件
boolean hadTapMessage = mHandler.hasMessages(TAP);
//有的话就Remove掉
if (hadTapMessage) mHandler.removeMessages(TAP);
//这里进去双击
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
mIsDoubleTapping = true;
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
//触发双击事件第二次单击时的按下
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
//这里进去单击,延时300ms,期间没有第二次down则为单击事件
} else {
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}
break;
case MotionEvent.ACTION_MOVE:
//如果300ms没有下一个down则当前为单击事件
if (mIsDoubleTapping) {
//触发双击事件第二次单击时的移动
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
}
break;
case MotionEvent.ACTION_UP:
//如果300ms没有下一个down则当前为单击事件
if (mIsDoubleTapping) {
//触发双击事件第二次单击时的抬起
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
}
break;
return handled;
}
OnDoubleTapListener
onContextClick(外设设备动作,如当鼠标/触摸板的右键点击)
这个估计很难用的上,目前我知道的是鼠标/触摸板的右键点击,其他的外设不清楚会不会触发。
@Override
public boolean onContextClick(MotionEvent e) {
return false;
}
public boolean onGenericMotionEvent(MotionEvent ev) {
final int actionButton = ev.getActionButton();
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_BUTTON_PRESS:
if (mContextClickListener != null && !mInContextClick && !mInLongPress
&& (actionButton == MotionEvent.BUTTON_STYLUS_PRIMARY
|| actionButton == MotionEvent.BUTTON_SECONDARY)) {
//在这里触发
if (mContextClickListener.onContextClick(ev)) {
mInContextClick = true;
mHandler.removeMessages(LONG_PRESS);
mHandler.removeMessages(TAP);
return true;
}
}
break;
}
return false;
}
SimpleOnGestureListener(简单实现)
这种Simple开头的Listener基本都是包含接口的简单实现,这个SimpleOnGestureListener包含了上述3个接口的所有简单实现,可以通过这个类去拿自己想要的接口。
四、总结
GestureDetector是一个用于手势检测的工具类,通过分析源码实现,能看出它实现的逻辑和方式,在自己自定义控件时能借鉴这种思路,还有一个之前没接触到的类VelocityTracker滑动速度跟踪器,利用这个类可以实现一些滑动控件时粘性,惯性操作。结束撒花。
接口实现 | 方法 |
---|---|
OnGestureListener | onDown()、onShowPress()、onSingleTapup()、onScroll()、onLongPress()、onFling() |
OnDoubleTapListener | onSingleTapConfirmed()、onDoubleTap()、onDoubleTapEvent() |
OnContextClickListener | onContextClick() |
SimpleOnGestureListener | 上述所有() |