所谓手势,就是指用户的手指或者触摸笔在触摸屏上的连续触摸行为,比如在屏幕上从左至右划出的一个动作,就是手势。在比如在屏幕上画一个圆圈也是一个手势。手势的这种连续的触碰会形成某个方向上的移动趋势,也会形成一个不规则的几何图形。
Android
对两种手势行为都提供了支持:
对于第一种手势行为而言,Android提供了手势检测,并为手势检测提供了相应的监听器。
对于第二种手势行为,Android允许开发者添加自己的手势,并提供相应的API识别手势。
一、GestureDetector类
- public class GestureDetector {
- // TODO: ViewConfiguration
- private int mBiggerTouchSlopSquare = 20 * 20;//touch事件最大超时时间的平方
- private int mTouchSlopSquare;//touch事件超时的时间平方
- private int mDoubleTapSlopSquare;//双击事件超时时间的平方
- private int mMinimumFlingVelocity;//最小滑动速率
- private int mMaximumFlingVelocity;//最大滑动速率
- private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();//长按超时
- private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();//单击超时
- private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();//双击超时
- // 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;// Handler
- private final OnGestureListener mListener;// 普通手势监听器
- private OnDoubleTapListener mDoubleTapListener;// 双击或快速单击监听器
- private boolean mStillDown;//是否按下就不动了
- private boolean mInLongPress;//是否在长按过程中
- private boolean mAlwaysInTapRegion;//是否一直点击同一个位置
- private boolean mAlwaysInBiggerTapRegion;//是否在更大的范围内点击
- private MotionEvent mCurrentDownEvent;// 这次手势按下的事件
- private MotionEvent mPreviousUpEvent;// 上次手势抬起的事件
- //如果用户仍然处于第二次点击的过程(按下,滑动,抬起),就为true。只能为真 如果有双击事件的监听器就只能为true
- private boolean mIsDoubleTapping;
- private float mLastMotionY;//最后一次动作的Y坐标
- private float mLastMotionX;//最后一次动作的X坐标
- private boolean mIsLongpressEnabled;//长按事件是否启用
- /**
- * 如果我们试用的API的版本级别>=Froyo,或者开发人员去显示的设置它,就为true。
- * 如果为true,输入事件>1个触摸点,将会被忽略。
- * 那么我们就能更好并排着的检测多点触控手势。
- */
- private boolean mIgnoreMultitouch;//是否支持多点touch事件
- /**
- * 解决滑动持续时候的速度
- */
- private VelocityTracker mVelocityTracker;// 追踪触摸事件的速率
- /**
- *处理某些指定的手势
- */
- 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;
- case TAP:
- // If the user's finger is still down, do not count it as a tap
- if (mDoubleTapListener != null && !mStillDown) {
- mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
- }
- break;
- default:
- throw new RuntimeException("Unknown message " + msg); // never
- }
- }
- }
- /**
- * 在一个非UI线程中创建一个GestureDetector
- * 已经过时了,用下面的构造方法代替
- * public GestureDetector(Context context, OnGestureListener listener, Handler handler)
- */
- @Deprecated
- public GestureDetector(OnGestureListener listener, Handler handler) {
- this(null, listener, handler);
- }
- /**
- * 在一个非UI线程中创建一个GestureDetector
- * 已经过时了,用下面的构造方法代替
- * public GestureDetector(Context context, OnGestureListener listener, Handler handler)
- */
- @Deprecated
- public GestureDetector(OnGestureListener listener) {
- this(null, listener, null);
- }
- /**
- * 在UI线程中创建一个GestureDetector
- */
- public GestureDetector(Context context, OnGestureListener listener) {
- this(context, listener, null);
- }
- /**
- * 在UI线程中创建一个GestureDetector
- */
- public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
- this(context, listener, handler, context != null
- && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);
- }
- /**
- * 在UI线程中创建一个GestureDetector
- * 不管你用的是哪个方法产生实例,都会调用这个构造器
- */
- public GestureDetector(Context context, OnGestureListener listener, Handler handler,
- boolean ignoreMultitouch) {
- if (handler != null) {
- mHandler = new GestureHandler(handler);
- } else {
- mHandler = new GestureHandler();
- }
- mListener = listener;
- //如果,这个listener只是实现了OnDoubleTapListener接口,就调用setOnDoubleTapListener方法
- //,初始化mDoubleTapListener对象
- if (listener instanceof OnDoubleTapListener) {
- setOnDoubleTapListener((OnDoubleTapListener) listener);
- }
- init(context, ignoreMultitouch);
- }
- /**
- * 初始化信息
- */
- private void init(Context context, boolean ignoreMultitouch) {
- //如果没有手势检测类,将抛出异常
- if (mListener == null) {
- throw new NullPointerException("OnGestureListener must not be null");
- }
- mIsLongpressEnabled = true;//默认长按事件开启
- mIgnoreMultitouch = ignoreMultitouch;//对多点触摸的处理
- // Fallback to support pre-donuts releases
- int touchSlop, doubleTapSlop;//touch超时,双击超时
- //对于一些超时操作需要变量的定义,通俗点说,就是不同事件的转变时间的问题
- //比如 按住多长,变为longPress事件,双击事件之间的时间间隔
- if (context == null) {
- // noinspection deprecation
- touchSlop = ViewConfiguration.getTouchSlop();
- doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
- // noinspection deprecation
- mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
- mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
- } else {
- final ViewConfiguration configuration = ViewConfiguration.get(context);
- touchSlop = configuration.getScaledTouchSlop();
- doubleTapSlop = configuration.getScaledDoubleTapSlop();
- mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
- mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
- }
- mTouchSlopSquare = touchSlop * touchSlop;
- mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
- }
- /**
- * 设置回调双击事件和解释手势行为的监听器
- */
- public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
- mDoubleTapListener = onDoubleTapListener;
- }
- /**
- * 如果你设置true的话就是开启了长按键,当你长时间触屏不动就能得到 onLongPress 手势,
- * 如果设置false 那么你长时间触屏不移动也得不到这个手势的支持
- * 默认设置为true
- */
- public void setIsLongpressEnabled(boolean isLongpressEnabled) {
- mIsLongpressEnabled = isLongpressEnabled;
- }
- /**
- * 返回长按事件传播的true或者false
- */
- public boolean isLongpressEnabled() {
- return mIsLongpressEnabled;
- }
- /**
- * 分析给出的事件,如何适用的话,就会去触发我们所提供的OnGestureListener中的
- * 回调方法
- */
- public boolean onTouchEvent(MotionEvent ev) {
- final int action = ev.getAction();//事件的类型
- final float y = ev.getY();//事件的x坐标
- final float x = ev.getX();//事件的y坐标
- //初始化速率追踪者,并将事件添加进去
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
- boolean handled = false;//是否要发消失
- switch (action & MotionEvent.ACTION_MASK) {//判断事件的类型
- case MotionEvent.ACTION_POINTER_DOWN://非主触点按下的时候
- if (mIgnoreMultitouch) {//如果忽略多点触摸,那么就调用cancel()方法
- // Multitouch event - abort.
- cancel();
- }
- break;
- case MotionEvent.ACTION_POINTER_UP://非主触点抬起的时候
- // Ending a multitouch gesture and going back to 1 finger
- if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
- //获得触点的id
- int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1
- : 0;
- mLastMotionX = ev.getX(index);//得到x,作为上一次的事件x坐标保存
- mLastMotionY = ev.getY(index);//得到y,作为上一次的事件y坐标保存
- mVelocityTracker.recycle();//回收mVelocityTracker
- mVelocityTracker = VelocityTracker.obtain();//重现得到
- }
- 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 {
- // This is a first tap
- mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
- }
- }
- mLastMotionX = x;
- mLastMotionY = y;
- if (mCurrentDownEvent != null) {
- mCurrentDownEvent.recycle();
- }
- mCurrentDownEvent = MotionEvent.obtain(ev);
- mAlwaysInTapRegion = true;
- mAlwaysInBiggerTapRegion = true;
- mStillDown = true;
- mInLongPress = false;
- 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);
- break;
- case MotionEvent.ACTION_MOVE:
- if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
- break;
- }
- final float scrollX = mLastMotionX - x;
- final float scrollY = mLastMotionY - y;
- if (mIsDoubleTapping) {
- // Give the move events of the double-tap
- handled |= mDoubleTapListener.onDoubleTapEvent(ev);
- } else if (mAlwaysInTapRegion) {
- final int deltaX = (int) (x - mCurrentDownEvent.getX());
- final int deltaY = (int) (y - mCurrentDownEvent.getY());
- int distance = (deltaX * deltaX) + (deltaY * deltaY);
- if (distance > mTouchSlopSquare) {
- handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
- mAlwaysInTapRegion = fal6se;
- mHandler.removeMessages(TAP);
- mHandler.removeMessages(SHOW_PRESS);
- mHandler.removeMessages(LONG_PRESS);
- }
- if (distance > mBiggerTouchSlopSquare) {
- mAlwaysInBiggerTapRegion = false;
- }
- } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
- handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
- mLastMotionX = x;
- mLastMotionY = y;
- }
- break;
- case MotionEvent.ACTION_UP:
- mStillDown = false;
- MotionEvent currentUpEvent = MotionEvent.obtain(ev);
- 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);
- } else {
- // A fling must travel the minimum tap distance
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
- final float velocityY = velocityTracker.getYVelocity();
- final float velocityX = velocityTracker.getXVelocity();
- 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;
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- mIsDoubleTapping = false;
- mHandler.removeMessages(SHOW_PRESS);
- mHandler.removeMessages(LONG_PRESS);
- break;
- case MotionEvent.ACTION_CANCEL:
- cancel();
- }
- return handled;
- }
- /**
- * 私有方法,取消的方法,移动消息队列中的消息,释放内存
- */
- private void cancel() {
- mHandler.removeMessages(SHOW_PRESS);
- mHandler.removeMessages(LONG_PRESS);
- mHandler.removeMessages(TAP);
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- mIsDoubleTapping = false;
- mStillDown = false;
- if (mInLongPress) {
- mInLongPress = false;
- }
- }
- /**
- * 判断双击事件中,两次点击的位置关系。
- * 如果间隔很远,就不触发双击的事件
- */
- private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
- MotionEvent secondDown) {
- if (!mAlwaysInBiggerTapRegion) {
- return false;
- }
- if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
- 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);
- mInLongPress = true;
- mListener.onLongPress(mCurrentDownEvent);
- }
- }
源码中也没啥详细的注释,看了一段时间,实在是看的头大,就放弃了。可以去看下GestureDetector类中文API: http://www.cnblogs.com/over140/archive/2011/01/17/1937044.html