源码分析:
首先熟悉一下GestureDetector文件接口。
frameworks/base/core/java/android/view/ GestureDetector.java
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);
}
private final OnGestureListener mListener;
public boolean onTouchEvent(MotionEvent ev) {
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
//当一个手指按下去后,触发的是ACTION_DOWN,只要手指不抬起来,当另一个手指按下去并且抬起来的时候,触发的是ACTION_POINTER_DOWN。
break;
case MotionEvent.ACTION_POINTER_UP:
//当一个手指抬起后,屏幕上还有另一个手指按下去的时候,触发的是ACTION_POINTER_DOWN。
break;
case MotionEvent.ACTION_DOWN:
if (mIsLongpressEnabled) {
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
//当一个手指按下去后,触发的是ACTION_DOWN,回调函数onDown()。
handled |= mListener.onDown(ev);
break;
case MotionEvent.ACTION_MOVE:
final float scrollX = mLastFocusX - focusX;
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap 如果是双击事件触发onDoubleTapEvent
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
//如果是在点击同一个位置,并且符合滑动的距离,触发onscroll事件
final int deltaX = (int) (focusX - mDownFocusX);
final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
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;
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap 如果是双击触发onDoubleTapEvent事件
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion) {
handled = mListener.onSingleTapUp(ev);
} else {
//判断是不是要触发onFling方法
// A fling must travel the minimum tap distance
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);
}
}
if (mPreviousUpEvent != null) {
mPreviousUpEvent.recycle();
}
// Hold the event we obtained above - listeners may have changed the original.
mPreviousUpEvent = currentUpEvent;
if (mVelocityTracker != null) {
// This may have been cleared when we called out to the
// application above.
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mIsDoubleTapping = false;
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;
}
return handled;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PRESS:
mListener.onShowPress(mCurrentDownEvent);
break;
case LONG_PRESS:
dispatchLongPress();
break;
}
private void dispatchLongPress() {
mHandler.removeMessages(TAP);
mInLongPress = true;
mListener.onLongPress(mCurrentDownEvent);
}
}
通过分析类GestureDetector,获悉内部接口OnGestureListener成员函数onDown;onShowPress;onSingleTapUp;onScroll;onLongPress;onFling;当类GestureDetector中的函数onTouchEvent被调用时,通过接口 mListener分别对内部接口OnGestureListener成员函数onDown;onShowPress;onSingleTapUp;onScroll;onLongPress;onFling进行调用。
针对以上log信息分析如下:
最后输出log如下:
1,点击屏幕后log输出如下
04-11 13:48:18.572: V/MYVIEW(5846): onTouch()-------------------pass
04-11 13:48:18.572: V/MYVIEW(5846): onDown()-------------------pass
04-11 13:48:18.616: V/MYVIEW(5846): onTouch()-------------------pass
04-11 13:48:18.616: V/MYVIEW(5846): onSingleTapUp()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,触摸屏按下操作被释放,即动作为 MotionEvent.ACTION_UP,由于没有对触摸屏做双击和长按操作,所以 mIsDoubleTapping, mInLongPress为false,点击触摸屏是同一个位置,所以 mAlwaysInTapRegion为true,进入mListener.onSingleTapUp(ev)并回调操作。
2,小幅度滑动屏幕后log输出如下
04-11 13:50:22.711: V/MYVIEW(6129): onDown()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onScroll()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,滑动触摸屏操作,即动作为 MotionEvent.ACTION_MOVE,当滑动的距离符合以下两个条件 distance > mTouchSlopSquare或(Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)则进入 mListener.onScroll并回调操作
3,大幅度滑动屏幕后log输出如下
04-11 13:50:22.711: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.711: V/MYVIEW(6129): onDown()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.777: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.777: V/MYVIEW(6129): onFling()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,滑动触摸屏操作,即动作为 MotionEvent.ACTION_MOVE,当滑动的距离符合以下两个条件 distance > mTouchSlopSquare或(Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)则进入 mListener.onScroll并回调操作;
步骤三,当 满足条件Math.abs(velocityY) > mMinimumFlingVelocity时,则进入 mListener.onFling并回调操作。
4,长按屏幕后log输出如下
04-11 14:05:01.006: V/MYVIEW(6349): onTouch()-------------------pass
04-11 14:05:01.006: V/MYVIEW(6349): onDown()-------------------pass
04-11 14:05:01.127: V/MYVIEW(6349): onShowPress()-------------------pass
04-11 14:05:01.626: V/MYVIEW(6349): onLongPress()-------------------pass
04-11 14:05:04.690: V/MYVIEW(6349): onTouch()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,触摸屏一直被按下,并在mCurrentDownEvent.getDownTime() + TAP_TIMEOUT 时间段内没有动作 MotionEvent.ACTION_MOVE,故mHandler成功发送消息SHOW_PRESS,由 handleMessage处理,进入 mListener.onShowPress并回调操作,
步骤三,在触摸屏被按下后 mCurrentDownEvent.getDownTime()+TAP_TIMEOUT+ LONGPRESS_TIMEOUT后的时间内,触摸屏一直保持该状态,则mHandler成功发送消息LONG_PRESS,由 handleMessage处理,进入 mListener.onLongPress 并回调操作。
首先熟悉一下GestureDetector文件接口。
frameworks/base/core/java/android/view/ GestureDetector.java
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);
}
private final OnGestureListener mListener;
public boolean onTouchEvent(MotionEvent ev) {
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
//当一个手指按下去后,触发的是ACTION_DOWN,只要手指不抬起来,当另一个手指按下去并且抬起来的时候,触发的是ACTION_POINTER_DOWN。
break;
case MotionEvent.ACTION_POINTER_UP:
//当一个手指抬起后,屏幕上还有另一个手指按下去的时候,触发的是ACTION_POINTER_DOWN。
break;
case MotionEvent.ACTION_DOWN:
if (mIsLongpressEnabled) {
mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);
}
mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
//当一个手指按下去后,触发的是ACTION_DOWN,回调函数onDown()。
handled |= mListener.onDown(ev);
break;
case MotionEvent.ACTION_MOVE:
final float scrollX = mLastFocusX - focusX;
final float scrollY = mLastFocusY - focusY;
if (mIsDoubleTapping) {
// Give the move events of the double-tap 如果是双击事件触发onDoubleTapEvent
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mAlwaysInTapRegion) {
//如果是在点击同一个位置,并且符合滑动的距离,触发onscroll事件
final int deltaX = (int) (focusX - mDownFocusX);
final int deltaY = (int) (focusY - mDownFocusY);
int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
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;
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
// Finally, give the up event of the double-tap 如果是双击触发onDoubleTapEvent事件
handled |= mDoubleTapListener.onDoubleTapEvent(ev);
} else if (mInLongPress) {
mHandler.removeMessages(TAP);
mInLongPress = false;
} else if (mAlwaysInTapRegion) {
handled = mListener.onSingleTapUp(ev);
} else {
//判断是不是要触发onFling方法
// A fling must travel the minimum tap distance
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);
}
}
if (mPreviousUpEvent != null) {
mPreviousUpEvent.recycle();
}
// Hold the event we obtained above - listeners may have changed the original.
mPreviousUpEvent = currentUpEvent;
if (mVelocityTracker != null) {
// This may have been cleared when we called out to the
// application above.
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mIsDoubleTapping = false;
mHandler.removeMessages(SHOW_PRESS);
mHandler.removeMessages(LONG_PRESS);
break;
}
return handled;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_PRESS:
mListener.onShowPress(mCurrentDownEvent);
break;
case LONG_PRESS:
dispatchLongPress();
break;
}
private void dispatchLongPress() {
mHandler.removeMessages(TAP);
mInLongPress = true;
mListener.onLongPress(mCurrentDownEvent);
}
}
通过分析类GestureDetector,获悉内部接口OnGestureListener成员函数onDown;onShowPress;onSingleTapUp;onScroll;onLongPress;onFling;当类GestureDetector中的函数onTouchEvent被调用时,通过接口 mListener分别对内部接口OnGestureListener成员函数onDown;onShowPress;onSingleTapUp;onScroll;onLongPress;onFling进行调用。
针对以上log信息分析如下:
最后输出log如下:
1,点击屏幕后log输出如下
04-11 13:48:18.572: V/MYVIEW(5846): onTouch()-------------------pass
04-11 13:48:18.572: V/MYVIEW(5846): onDown()-------------------pass
04-11 13:48:18.616: V/MYVIEW(5846): onTouch()-------------------pass
04-11 13:48:18.616: V/MYVIEW(5846): onSingleTapUp()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,触摸屏按下操作被释放,即动作为 MotionEvent.ACTION_UP,由于没有对触摸屏做双击和长按操作,所以 mIsDoubleTapping, mInLongPress为false,点击触摸屏是同一个位置,所以 mAlwaysInTapRegion为true,进入mListener.onSingleTapUp(ev)并回调操作。
2,小幅度滑动屏幕后log输出如下
04-11 13:50:22.711: V/MYVIEW(6129): onDown()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onScroll()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,滑动触摸屏操作,即动作为 MotionEvent.ACTION_MOVE,当滑动的距离符合以下两个条件 distance > mTouchSlopSquare或(Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)则进入 mListener.onScroll并回调操作
3,大幅度滑动屏幕后log输出如下
04-11 13:50:22.711: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.711: V/MYVIEW(6129): onDown()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.736: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.746: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.766: V/MYVIEW(6129): onScroll()-------------------pass
04-11 13:50:22.777: V/MYVIEW(6129): onTouch()-------------------pass
04-11 13:50:22.777: V/MYVIEW(6129): onFling()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,滑动触摸屏操作,即动作为 MotionEvent.ACTION_MOVE,当滑动的距离符合以下两个条件 distance > mTouchSlopSquare或(Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)则进入 mListener.onScroll并回调操作;
步骤三,当 满足条件Math.abs(velocityY) > mMinimumFlingVelocity时,则进入 mListener.onFling并回调操作。
4,长按屏幕后log输出如下
04-11 14:05:01.006: V/MYVIEW(6349): onTouch()-------------------pass
04-11 14:05:01.006: V/MYVIEW(6349): onDown()-------------------pass
04-11 14:05:01.127: V/MYVIEW(6349): onShowPress()-------------------pass
04-11 14:05:01.626: V/MYVIEW(6349): onLongPress()-------------------pass
04-11 14:05:04.690: V/MYVIEW(6349): onTouch()-------------------pass
步骤一,点击触摸屏,触摸屏被按下,即动作为 MotionEvent.ACTION_DOWN,进入mListener.onDown(ev)并回调操作;
步骤二,触摸屏一直被按下,并在mCurrentDownEvent.getDownTime() + TAP_TIMEOUT 时间段内没有动作 MotionEvent.ACTION_MOVE,故mHandler成功发送消息SHOW_PRESS,由 handleMessage处理,进入 mListener.onShowPress并回调操作,
步骤三,在触摸屏被按下后 mCurrentDownEvent.getDownTime()+TAP_TIMEOUT+ LONGPRESS_TIMEOUT后的时间内,触摸屏一直保持该状态,则mHandler成功发送消息LONG_PRESS,由 handleMessage处理,进入 mListener.onLongPress 并回调操作。