滑轮控件研究二、GestureDetector的深入研究

来源:互联网 发布:淘宝卖电子书 编辑:程序博客网 时间:2024/05/13 06:29

  所谓手势,就是指用户的手指或者触摸笔在触摸屏上的连续触摸行为,比如在屏幕上从左至右划出的一个动作,就是手势。在比如在屏幕上画一个圆圈也是一个手势。手势的这种连续的触碰会形成某个方向上的移动趋势,也会形成一个不规则的几何图形。Android对两种手势行为都提供了支持:

  对于第一种手势行为而言,Android提供了手势检测,并为手势检测提供了相应的监听器。

  对于第二种手势行为,Android允许开发者添加自己的手势,并提供相应的API识别手势。

一、GestureDetector类

public class GestureDetector {// TODO: ViewConfigurationprivate 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 belowprivate static final int SHOW_PRESS = 1;//短按标志private static final int LONG_PRESS = 2;//长按标志private static final int TAP = 3;//轻击标志private final Handler mHandler;// Handlerprivate 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。只能为真 如果有双击事件的监听器就只能为trueprivate 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());}@Overridepublic 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 tapif (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) */@Deprecatedpublic GestureDetector(OnGestureListener listener, Handler handler) {this(null, listener, handler);}/** * 在一个非UI线程中创建一个GestureDetector * 已经过时了,用下面的构造方法代替 * public GestureDetector(Context context, OnGestureListener listener, Handler handler) */@Deprecatedpublic 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 releasesint touchSlop, doubleTapSlop;//touch超时,双击超时//对于一些超时操作需要变量的定义,通俗点说,就是不同事件的转变时间的问题//比如  按住多长,变为longPress事件,双击事件之间的时间间隔if (context == null) {// noinspection deprecationtouchSlop = ViewConfiguration.getTouchSlop();doubleTapSlop = ViewConfiguration.getDoubleTapSlop();// noinspection deprecationmMinimumFlingVelocity = 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 fingerif (mIgnoreMultitouch && ev.getPointerCount() == 2) {//获得触点的idint 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();//回收mVelocityTrackermVelocityTracker = 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 tapmIsDoubleTapping = true;// Give a callback with the first tap of the double-taphandled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);// Give a callback with down event of the double-taphandled |= mDoubleTapListener.onDoubleTapEvent(ev);} else {// This is a first tapmHandler.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-taphandled |= 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-taphandled |= 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 distancefinal 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

如果对上面的事件发生的时间不太清楚的话,可以自己试验下,可以看下篇的小例子:滑轮控件研究三、GestureDetector的中手势事件的测试

对于上面的VelocityTracker,如何追踪touch事件的速率不明白的话,可看下下面的分析:滑轮控件研究四、VelocityTracker的简单研究

对于上面的ViewConfiguration某些变量不明白的,看以看下下面的分析:滑轮控件研究五、ViewConfiguration的简单研究


如果,想直接看下GestureDetector的实际用法,可以看下面的:滑轮控件研究六、GestureDetector的简单应用





原创粉丝点击