您的位置:首页 > 其它

GestureDetector源码分析

2015-06-02 17:45 309 查看
eclipse下快捷键ctrl + shift + t 搜索 GestureDetector 查看源码

注:源码中的部分英文注释已经删去,中文注释为个人理解

package android.view;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

public class GestureDetector {

public interface OnGestureListener {

boolean onDown(MotionEvent e);

void onShowPress(MotionEvent e);

boolean onSingleTapUp(MotionEvent e);

boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

void onLongPress(MotionEvent e);

boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}

public interface OnDoubleTapListener {
//action_down发送,DOUBLE_TAP_TIMEOUT(300ms)内抬起视为单击,以区别于双击
boolean onSingleTapConfirmed(MotionEvent e);

//参数e 是双击第一次action_down的Motionevent
boolean onDoubleTap(MotionEvent e);

boolean onDoubleTapEvent(MotionEvent e);
}

/**
* A convenience class to extend when you only want to listen for a subset
* of all the gestures. This implements all methods in the
* {@link OnGestureListener} and {@link OnDoubleTapListener} but does
* nothing and return {@code false} for all applicable methods.
*/
public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener {

//action_up 在单击范围内且不是双击,不是长按
public boolean onSingleTapUp(MotionEvent e) {
return false;
}

//action_down  LONGPRESS_TIMEOUT(500ms)+TAP_TIMEOUT(180ms) 内,不抬起
public void onLongPress(MotionEvent e) {
}

//action_move 非长按,非双击,超出 单击范围
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
return false;
}

//action_up 中 非双击 长按,不在点击区域,速度>mMinimumFlingVelocity
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}

//action_down TAP_TIMEOUT(180ms) 没有移动scroll距离,没有抬起
public void onShowPress(MotionEvent e) {
}

//action_down
public boolean onDown(MotionEvent e) {
return false;
}

//参数 e 是双击第一次 action_down Motionevent
public boolean onDoubleTap(MotionEvent e) {
return false;
}

//参数 e 是双击第二次 action_down Motionevent ,双击 第二次 抬起action_up 中也会发送 Motionevent
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}

//action_down 单击 300内 未滑动超出 scroll判定范围
//singleTap的msg 延时300 并且300内抬起 没有在点下去(点下去视双击判断),且非双击抬起
public boolean onSingleTapConfirmed(MotionEvent e) {
return false;
}
}

private int mTouchSlopSquare;    //action_move 中判定 scroll 的距离限制
private int mDoubleTapTouchSlopSquare;
private int mDoubleTapSlopSquare;
private int mMinimumFlingVelocity;    //fling 判定的最小速度
private int mMaximumFlingVelocity;    //fling 判定的最大速度

private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();    // 500 长按最小值
private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();    //180 等待时间判断是 敲击还是滑动
private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();    //300 双击认定时间
private static final int DOUBLE_TAP_MIN_TIME = ViewConfiguration.getDoubleTapMinTime();    //40

// constants for Message.what used by GestureHandler below
private static final int SHOW_PRESS = 1;
private static final int LONG_PRESS = 2;
private static final int TAP = 3;

private final Handler mHandler;
private final OnGestureListener mListener;    //构造函数中得到的接口类
private OnDoubleTapListener mDoubleTapListener;    //构造函数中得到。

private boolean mStillDown;
private boolean mDeferConfirmSingleTap;
private boolean mInLongPress;
private boolean mAlwaysInTapRegion;    //是否在单击范围内 action_down中设置为true action_move 中设置为false
private boolean mAlwaysInBiggerTapRegion;    //是否在双击范围内

private MotionEvent mCurrentDownEvent;
private MotionEvent mPreviousUpEvent;

/**
* True when the user is still touching for the second tap (down, move, and
* up events). Can only be true if there is a double tap listener attached.
*/
private boolean mIsDoubleTapping;

private float mLastFocusX;
private float mLastFocusY;
private float mDownFocusX;
private float mDownFocusY;

private boolean mIsLongpressEnabled;    //是否可以长按

/**
* Determines speed during touch scrolling
* 通过MotionEvent 获取 滑动速度
*/
private VelocityTracker mVelocityTracker;

/**
* Consistency verifier for debugging purposes.
*/
private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
InputEventConsistencyVerifier.isInstrumentationEnabled() ?
new InputEventConsistencyVerifier(this, 0) : null;

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:
mListener.onShowPress(mCurrentDownEvent);
break;

case LONG_PRESS:
dispatchLongPress();
break;

//TAP msg 在每次 action_down 中都会重置
case TAP:
// If the user's finger is still down, do not count it as a tap
if (mDoubleTapListener != null) {
if (!mStillDown) {
mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
} else {
mDeferConfirmSingleTap = true;
}
}
break;

default:
throw new RuntimeException("Unknown message " + msg); //never
}
}
}

/**
* Creates a GestureDetector with the supplied listener.
* This variant of the constructor should be used from a non-UI thread
* (as it allows specifying the Handler).
*
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use
*
* @throws NullPointerException if either {@code listener} or
* {@code handler} is null.
*
* @deprecated Use {@link #GestureDetector(android.content.Context,
*      android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
*/
@Deprecated
public GestureDetector(OnGestureListener listener, Handler handler) {
this(null, listener, handler);
}

/**
* Creates a GestureDetector with the supplied listener.
* You may only use this constructor from a UI thread (this is the usual situation).
* @see android.os.Handler#Handler()
*
* @param listener the listener invoked for all the callbacks, this must
* not be null.
*
* @throws NullPointerException if {@code listener} is null.
*
* @deprecated Use {@link #GestureDetector(android.content.Context,
*      android.view.GestureDetector.OnGestureListener)} instead.
*/
@Deprecated
public GestureDetector(OnGestureListener listener) {
this(null, listener, null);
}

/**
* Creates a GestureDetector with the supplied listener.
* You may only use this constructor from a {@link android.os.Looper} thread.
* @see android.os.Handler#Handler()
*
* @param context the application's context
* @param listener the listener invoked for all the callbacks, this must
* not be null.
*
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener) {
this(context, listener, null);
}

/**
* Creates a GestureDetector with the supplied listener that runs deferred events on the
* thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
* @param context the application's context
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use for running deferred listener events.
*
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
if (handler != null) {
mHandler = new GestureHandler(handler);
} else {
mHandler = new GestureHandler();
}
mListener = listener;
if (listener instanceof OnDoubleTapListener) {
setOnDoubleTapListener((OnDoubleTapListener) listener);
}
init(context);
}

/**
* Creates a GestureDetector with the supplied listener that runs deferred events on the
* thread associated with the supplied {@link android.os.Handler}.
* @see android.os.Handler#Handler()
*
* @param context the application's context
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use for running deferred listener events.
* @param unused currently not used.
*
* @throws NullPointerException if {@code listener} is null.
*/
public GestureDetector(Context context, OnGestureListener listener, Handler handler,
boolean unused) {
this(context, listener, handler);
}

private void init(Context context) {
if (mListener == null) {
throw new NullPointerException("OnGestureListener must not be null");
}
mIsLongpressEnabled = true;

// Fallback to support pre-donuts releases
int touchSlop, doubleTapSlop, doubleTapTouchSlop;
if (context == null) {
//noinspection deprecation
touchSlop = ViewConfiguration.getTouchSlop();
doubleTapTouchSlop = touchSlop; // Hack rather than adding a hiden method for this
doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
//noinspection deprecation
mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
} else {
//包含标准UI timeouts,sizes,distances
final ViewConfiguration configuration = ViewConfiguration.get(context);
touchSlop = configuration.getScaledTouchSlop();    //8 pix 区别于 滑动 scroll 的距离
doubleTapTouchSlop = configuration.getScaledDoubleTapTouchSlop();
doubleTapSlop = configuration.getScaledDoubleTapSlop();    //双击 判定距离
mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();    //fling 开始的最小速度
mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();    //fling 开始的最大速度
}
mTouchSlopSquare = touchSlop * touchSlop;
mDoubleTapTouchSlopSquare = doubleTapTouchSlop * doubleTapTouchSlop;
mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
}

/**
* Sets the listener which will be called for double-tap and related
* gestures.
*
* @param onDoubleTapListener the listener invoked for all the callbacks, or
*        null to stop listening for double-tap gestures.
*/
public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
mDoubleTapListener = onDoubleTapListener;
}

/**
* Set whether longpress is enabled, if this is enabled when a user
* presses and holds down you get a longpress event and nothing further.
* If it's disabled the user can press and hold down and then later
* moved their finger and you will get scroll events. By default
* longpress is enabled.
*
* @param isLongpressEnabled whether longpress should be enabled.
*/
public void setIsLongpressEnabled(boolean isLongpressEnabled) {
mIsLongpressEnabled = isLongpressEnabled;
}

/**
* @return true if longpress is enabled, else false.
*/
public boolean isLongpressEnabled() {
return mIsLongpressEnabled;
}

/**
* Analyzes the given motion event and if applicable triggers the
* appropriate callbacks on the {@link OnGestureListener} supplied.
*
* @param ev The current motion event.
* @return true if the {@link OnGestureListener} consumed the event,
*              else false.
*/
public boolean onTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
}

final int action = ev.getAction();

//初始化 速度测量器类,在每一个事件开始都初始
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
//速度测试器添加 事件event
mVelocityTracker.addMovement(ev);

//局部变量,当前touch 是否是 ACTION_POINTER_UP
final boolean pointerUp =
(action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
//如果 pointerUp true, action的index,抬起的是哪个点
final int skipIndex = pointerUp ? ev.getActionIndex() : -1;

// Determine focal point
float sumX = 0, sumY = 0;
final int count = ev.getPointerCount();    //count为触摸点数量
//把所以点x坐标相加,y坐标相加,直到抬起点(如果当前事件是其中一个点的抬起事件)
for (int i = 0; i < count; i++) {
if (skipIndex == i) continue;
sumX += ev.getX(i);
sumY += ev.getY(i);
}
final int div = pointerUp ? count - 1 : count;
//得到x、y的平均值
final float focusX = sumX / div;
final float focusY = sumY / div;

//判断该点击事件是否被消耗
boolean handled = false;

switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;
// Cancel long press and taps
cancelTaps();
break;

case MotionEvent.ACTION_POINTER_UP:
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;

// Check the dot product of current velocities.
// If the pointer that left was opposing another velocity vector, clear.
// 如果余下点的速度矢量与抬起点想法,则重置速度跟踪
mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
final int upIndex = ev.getActionIndex();
final int id1 = ev.getPointerId(upIndex);
final float x1 = mVelocityTracker.getXVelocity(id1);
final float y1 = mVelocityTracker.getYVelocity(id1);
for (int i = 0; i < count; i++) {
if (i == upIndex) continue;

final int id2 = ev.getPointerId(i);
final float x = x1 * mVelocityTracker.getXVelocity(id2);
final float y = y1 * mVelocityTracker.getYVelocity(id2);

final float dot = x + y;
if (dot < 0) {
mVelocityTracker.clear();
break;
}
}
break;

case MotionEvent.ACTION_DOWN:
if (mDoubleTapListener != null) {
boolean hadTapMessage = mHandler.hasMessages(TAP);
if (hadTapMessage) mHandler.removeMessages(TAP);
if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
// This is a second tap
mIsDoubleTapping = true;
// Give a callback with the first tap of the double-tap
handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
// Give a callback with down event of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else {
//如果判断不是doubleTap 的话,就发送 singleTap的msg 延时300 并且300内抬起 没有在点下去
// This is a first tap
mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
}
}

//保存x、y平均值
mDownFocusX = mLastFocusX = focusX;
mDownFocusY = mLastFocusY = focusY;
//mCurrentDownEvent 初始化,回收上一个 mCurrentDownEvent
if (mCurrentDownEvent != null) {
mCurrentDownEvent.recycle();
}
mCurrentDownEvent = MotionEvent.obtain(ev);
mAlwaysInTapRegion = true;
mAlwaysInBiggerTapRegion = true;
mStillDown = true;    //保存 stilldown 状态
mInLongPress = false;    //还未进入 longpress 状态 等于 false
mDeferConfirmSingleTap = false;
//发送longpress 500+180和showPress 计时 180,
if (mIsLongpressEnabled) {
mHandler.removeMessages(LONG_PRESS);
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
handled |= mListener.onDown(ev);    //handled 是判断这个时间是否已经被消耗
break;

case MotionEvent.ACTION_MOVE:
//mInLongPress 在 longpress msg中设置为 ture
if (mInLongPress) {
break;
}
//mLastFocus 在 action_down 中保存下来的
final float scrollX = mLastFocusX - focusX;
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
//mAlwaysInTapRegion 总是在tap区域 在action_down 中设置
final int deltaX = (int) (focusX - mDownFocusX);
final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
//一旦判断为onScroll 则取消 singleTap ,longpress, showpress msg
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
mAlwaysInTapRegion = false;
mHandler.removeMessages(TAP);
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
}
if (distance > mDoubleTapTouchSlopSquare) {
mAlwaysInBiggerTapRegion = false;
}
} else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
mLastFocusX = focusX;
mLastFocusY = focusY;
}
break;

case MotionEvent.ACTION_UP:
mStillDown = false;    //将stilldown 状态更改
MotionEvent currentUpEvent = MotionEvent.obtain(ev);    //设置 upEvent
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion) {
handled = mListener.onSingleTapUp(ev);
if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
mDoubleTapListener.onSingleTapConfirmed(ev);
}
} else {

// A fling must travel the minimum tap distance
//计算 pointer 0 的速度 认为 是onFling 事件
final VelocityTracker velocityTracker = mVelocityTracker;
final int pointerId = ev.getPointerId(0);
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
final float velocityY = velocityTracker.getYVelocity(pointerId);
final float velocityX = velocityTracker.getXVelocity(pointerId);

if ((Math.abs(velocityY) > mMinimumFlingVelocity)
|| (Math.abs(velocityX) > mMinimumFlingVelocity)){
handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
}
}
//消耗掉previousUpEvent事件
if (mPreviousUpEvent != null) {
mPreviousUpEvent.recycle();
}
// Hold the event we obtained above - listeners may have changed the original.
//重置 previousUpEvent
mPreviousUpEvent = currentUpEvent;
//清理轨迹跟踪
if (mVelocityTracker != null) {
// This may have been cleared when we called out to the
// application above.
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mIsDoubleTapping = false;
mDeferConfirmSingleTap = false;
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;

case MotionEvent.ACTION_CANCEL:
cancel();
break;
}

if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
}
return handled;
}

private void cancel() {
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
mHandler.removeMessages(TAP);
mVelocityTracker.recycle();
mVelocityTracker = null;
mIsDoubleTapping = false;
mStillDown = false;
mAlwaysInTapRegion = false;
mAlwaysInBiggerTapRegion = false;
mDeferConfirmSingleTap = false;
if (mInLongPress) {
mInLongPress = false;
}
}

//action_pointer_down 取消所有长按,短长按,单击判定
private void cancelTaps() {
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
mHandler.removeMessages(TAP);
mIsDoubleTapping = false;
mAlwaysInTapRegion = false;
mAlwaysInBiggerTapRegion = false;
mDeferConfirmSingleTap = false;
if (mInLongPress) {
mInLongPress = false;
}
}

// 双击判断,DOUBLE_TAP_MIN_TIME < 间隔时间 < DOUBLE_TAP_TIMEOUT
//  双击点x距离平方 + y距离平方< 距离间隔 满足以上两点 则判断为双击
private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
MotionEvent secondDown) {
if (!mAlwaysInBiggerTapRegion) {
return false;
}

final long deltaTime = secondDown.getEventTime() - firstUp.getEventTime();
if (deltaTime > DOUBLE_TAP_TIMEOUT || deltaTime < DOUBLE_TAP_MIN_TIME) {
return false;
}

int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
}

private void dispatchLongPress() {
mHandler.removeMessages(TAP);
mDeferConfirmSingleTap = false;
mInLongPress = true;
mListener.onLongPress(mCurrentDownEvent);
}
}


1、GestureDetector、OnGestureListener和SimpleOnGestureListener

GestureDetector类对外提供了两个接口:OnGestureListener,OnDoubleTapListener,还有一个内部类SimpleOnGestureListener。SimpleOnGestureListener类是GestureDetector提供给我们的一个更方便的、经过简化的响应不同手势的类,这个类实现了上述两个类的接口(但是所有的方法都是空的),该类是static class,也就是说它实际上是一个外部类。我们可以在外部继承这个类,重写里面的手势处理方法。

2、onSingleTapUp和onSingleTapConfirmed

onSingleTapUp(MotionEvent e):一次点击up事件;在touch down后又没有滑动(onScroll),又没有长按(onLongPress),然后Touchup时触发。

点击一下非常快的(不滑动)Touchup:onDown->onSingleTapUp->onSingleTapConfirmed

点击一下稍微慢点的(不滑动)Touchup:onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed

onSingleTapConfirmed(MotionEvent e):用来判定该次点击是SingleTap而不是DoubleTap,如果连续点击两次就是DoubleTap手势,如果只点击一次,系统等待一段时间后没有收到第二次点击则判定该次点击为SingleTap而不是DoubleTap,然后触发SingleTapConfirmed事件。这个方法不同于onSingleTapUp,他是在GestureDetector确信用户在第一次触摸屏幕后,没有紧跟着第二次触摸屏幕,也就是不是“双击”的时候触发
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: