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

来源:互联网 发布:三大会计软件 编辑:程序博客网 时间:2024/05/22 03:10

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

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

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

一、GestureDetector类

[java] view plaincopy
  1. public class GestureDetector {  
  2.   
  3.     // TODO: ViewConfiguration  
  4.     private int mBiggerTouchSlopSquare = 20 * 20;//touch事件最大超时时间的平方  
  5.   
  6.     private int mTouchSlopSquare;//touch事件超时的时间平方  
  7.     private int mDoubleTapSlopSquare;//双击事件超时时间的平方  
  8.     private int mMinimumFlingVelocity;//最小滑动速率  
  9.     private int mMaximumFlingVelocity;//最大滑动速率  
  10.   
  11.     private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();//长按超时  
  12.     private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();//单击超时  
  13.     private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();//双击超时  
  14.   
  15.     // constants for Message.what used by GestureHandler below  
  16.     private static final int SHOW_PRESS = 1;//短按标志  
  17.     private static final int LONG_PRESS = 2;//长按标志  
  18.     private static final int TAP = 3;//轻击标志  
  19.   
  20.     private final Handler mHandler;// Handler  
  21.     private final OnGestureListener mListener;// 普通手势监听器  
  22.     private OnDoubleTapListener mDoubleTapListener;// 双击或快速单击监听器  
  23.   
  24.     private boolean mStillDown;//是否按下就不动了  
  25.     private boolean mInLongPress;//是否在长按过程中   
  26.     private boolean mAlwaysInTapRegion;//是否一直点击同一个位置  
  27.     private boolean mAlwaysInBiggerTapRegion;//是否在更大的范围内点击  
  28.   
  29.     private MotionEvent mCurrentDownEvent;// 这次手势按下的事件  
  30.     private MotionEvent mPreviousUpEvent;// 上次手势抬起的事件  
  31.   
  32.       
  33.     //如果用户仍然处于第二次点击的过程(按下,滑动,抬起),就为true。只能为真 如果有双击事件的监听器就只能为true      
  34.     private boolean mIsDoubleTapping;  
  35.   
  36.     private float mLastMotionY;//最后一次动作的Y坐标  
  37.     private float mLastMotionX;//最后一次动作的X坐标  
  38.   
  39.     private boolean mIsLongpressEnabled;//长按事件是否启用  
  40.   
  41.     /** 
  42.      * 如果我们试用的API的版本级别>=Froyo,或者开发人员去显示的设置它,就为true。 
  43.      * 如果为true,输入事件>1个触摸点,将会被忽略。 
  44.      * 那么我们就能更好并排着的检测多点触控手势。 
  45.      */  
  46.     private boolean mIgnoreMultitouch;//是否支持多点touch事件  
  47.   
  48.     /** 
  49.      * 解决滑动持续时候的速度 
  50.      */  
  51.     private VelocityTracker mVelocityTracker;// 追踪触摸事件的速率  
  52.   
  53.     /** 
  54.      *处理某些指定的手势 
  55.      */  
  56.     private class GestureHandler extends Handler {  
  57.         GestureHandler() {  
  58.             super();  
  59.         }  
  60.   
  61.         GestureHandler(Handler handler) {  
  62.             super(handler.getLooper());  
  63.         }  
  64.   
  65.         @Override  
  66.         public void handleMessage(Message msg) {  
  67.             switch (msg.what) {  
  68.             case SHOW_PRESS:  
  69.                 mListener.onShowPress(mCurrentDownEvent);  
  70.                 break;  
  71.   
  72.             case LONG_PRESS:  
  73.                 dispatchLongPress();  
  74.                 break;  
  75.   
  76.             case TAP:  
  77.                 // If the user's finger is still down, do not count it as a tap  
  78.                 if (mDoubleTapListener != null && !mStillDown) {  
  79.                     mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);  
  80.                 }  
  81.                 break;  
  82.   
  83.             default:  
  84.                 throw new RuntimeException("Unknown message " + msg); // never  
  85.             }  
  86.         }  
  87.     }  
  88.   
  89.     /** 
  90.      * 在一个非UI线程中创建一个GestureDetector 
  91.      * 已经过时了,用下面的构造方法代替 
  92.      * public GestureDetector(Context context, OnGestureListener listener, Handler handler) 
  93.      */  
  94.     @Deprecated  
  95.     public GestureDetector(OnGestureListener listener, Handler handler) {  
  96.         this(null, listener, handler);  
  97.     }  
  98.       
  99.       
  100.     /** 
  101.      * 在一个非UI线程中创建一个GestureDetector 
  102.      * 已经过时了,用下面的构造方法代替 
  103.      * public GestureDetector(Context context, OnGestureListener listener, Handler handler) 
  104.      */  
  105.     @Deprecated  
  106.     public GestureDetector(OnGestureListener listener) {  
  107.         this(null, listener, null);  
  108.     }  
  109.   
  110.     /** 
  111.      * 在UI线程中创建一个GestureDetector 
  112.      */  
  113.     public GestureDetector(Context context, OnGestureListener listener) {  
  114.         this(context, listener, null);  
  115.     }  
  116.   
  117.     /** 
  118.      * 在UI线程中创建一个GestureDetector 
  119.      */  
  120.     public GestureDetector(Context context, OnGestureListener listener, Handler handler) {  
  121.         this(context, listener, handler, context != null  
  122.                 && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);  
  123.     }  
  124.   
  125.     /** 
  126.      * 在UI线程中创建一个GestureDetector 
  127.      * 不管你用的是哪个方法产生实例,都会调用这个构造器 
  128.      */  
  129.     public GestureDetector(Context context, OnGestureListener listener, Handler handler,  
  130.             boolean ignoreMultitouch) {  
  131.         if (handler != null) {  
  132.             mHandler = new GestureHandler(handler);  
  133.         } else {  
  134.             mHandler = new GestureHandler();  
  135.         }  
  136.         mListener = listener;  
  137.         //如果,这个listener只是实现了OnDoubleTapListener接口,就调用setOnDoubleTapListener方法  
  138.         //,初始化mDoubleTapListener对象  
  139.         if (listener instanceof OnDoubleTapListener) {  
  140.             setOnDoubleTapListener((OnDoubleTapListener) listener);  
  141.         }  
  142.         init(context, ignoreMultitouch);  
  143.     }  
  144.   
  145.     /** 
  146.      * 初始化信息 
  147.      */  
  148.     private void init(Context context, boolean ignoreMultitouch) {  
  149.         //如果没有手势检测类,将抛出异常  
  150.         if (mListener == null) {  
  151.             throw new NullPointerException("OnGestureListener must not be null");  
  152.         }  
  153.         mIsLongpressEnabled = true;//默认长按事件开启  
  154.         mIgnoreMultitouch = ignoreMultitouch;//对多点触摸的处理  
  155.   
  156.         // Fallback to support pre-donuts releases  
  157.         int touchSlop, doubleTapSlop;//touch超时,双击超时  
  158.         //对于一些超时操作需要变量的定义,通俗点说,就是不同事件的转变时间的问题  
  159.         //比如  按住多长,变为longPress事件,双击事件之间的时间间隔  
  160.         if (context == null) {  
  161.             // noinspection deprecation  
  162.             touchSlop = ViewConfiguration.getTouchSlop();  
  163.             doubleTapSlop = ViewConfiguration.getDoubleTapSlop();  
  164.             // noinspection deprecation  
  165.             mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();  
  166.             mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();  
  167.         } else {  
  168.             final ViewConfiguration configuration = ViewConfiguration.get(context);  
  169.             touchSlop = configuration.getScaledTouchSlop();  
  170.             doubleTapSlop = configuration.getScaledDoubleTapSlop();  
  171.             mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();  
  172.             mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();  
  173.         }  
  174.         mTouchSlopSquare = touchSlop * touchSlop;  
  175.         mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;  
  176.     }  
  177.   
  178.     /** 
  179.      * 设置回调双击事件和解释手势行为的监听器 
  180.      */  
  181.     public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {  
  182.         mDoubleTapListener = onDoubleTapListener;  
  183.     }  
  184.   
  185.     /** 
  186.      * 如果你设置true的话就是开启了长按键,当你长时间触屏不动就能得到 onLongPress 手势, 
  187.      * 如果设置false 那么你长时间触屏不移动也得不到这个手势的支持 
  188.      * 默认设置为true 
  189.      */  
  190.     public void setIsLongpressEnabled(boolean isLongpressEnabled) {  
  191.         mIsLongpressEnabled = isLongpressEnabled;  
  192.     }  
  193.   
  194.     /** 
  195.      * 返回长按事件传播的true或者false 
  196.      */  
  197.     public boolean isLongpressEnabled() {  
  198.         return mIsLongpressEnabled;  
  199.     }  
  200.   
  201.     /** 
  202.      * 分析给出的事件,如何适用的话,就会去触发我们所提供的OnGestureListener中的 
  203.      * 回调方法 
  204.      */  
  205.     public boolean onTouchEvent(MotionEvent ev) {  
  206.         final int action = ev.getAction();//事件的类型  
  207.         final float y = ev.getY();//事件的x坐标  
  208.         final float x = ev.getX();//事件的y坐标  
  209.   
  210.         //初始化速率追踪者,并将事件添加进去  
  211.         if (mVelocityTracker == null) {  
  212.             mVelocityTracker = VelocityTracker.obtain();  
  213.         }  
  214.         mVelocityTracker.addMovement(ev);  
  215.   
  216.         boolean handled = false;//是否要发消失  
  217.   
  218.         switch (action & MotionEvent.ACTION_MASK) {//判断事件的类型  
  219.         case MotionEvent.ACTION_POINTER_DOWN://非主触点按下的时候  
  220.             if (mIgnoreMultitouch) {//如果忽略多点触摸,那么就调用cancel()方法  
  221.                 // Multitouch event - abort.  
  222.                 cancel();  
  223.             }  
  224.             break;  
  225.   
  226.         case MotionEvent.ACTION_POINTER_UP://非主触点抬起的时候  
  227.             // Ending a multitouch gesture and going back to 1 finger  
  228.             if (mIgnoreMultitouch && ev.getPointerCount() == 2) {  
  229.                 //获得触点的id  
  230.                 int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1  
  231.                         : 0;  
  232.                 mLastMotionX = ev.getX(index);//得到x,作为上一次的事件x坐标保存  
  233.                 mLastMotionY = ev.getY(index);//得到y,作为上一次的事件y坐标保存  
  234.                 mVelocityTracker.recycle();//回收mVelocityTracker  
  235.                 mVelocityTracker = VelocityTracker.obtain();//重现得到  
  236.             }  
  237.             break;  
  238.   
  239.         case MotionEvent.ACTION_DOWN://按下的事件  
  240.             if (mDoubleTapListener != null) {  
  241.                 boolean hadTapMessage = mHandler.hasMessages(TAP);  
  242.                 if (hadTapMessage)  
  243.                     mHandler.removeMessages(TAP);  
  244.                 if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage  
  245.                         && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {  
  246.                     // This is a second tap  
  247.                     mIsDoubleTapping = true;  
  248.                     // Give a callback with the first tap of the double-tap  
  249.                     handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);  
  250.                     // Give a callback with down event of the double-tap  
  251.                     handled |= mDoubleTapListener.onDoubleTapEvent(ev);  
  252.                 } else {  
  253.                     // This is a first tap  
  254.                     mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);  
  255.                 }  
  256.             }  
  257.   
  258.             mLastMotionX = x;  
  259.             mLastMotionY = y;  
  260.             if (mCurrentDownEvent != null) {  
  261.                 mCurrentDownEvent.recycle();  
  262.             }  
  263.             mCurrentDownEvent = MotionEvent.obtain(ev);  
  264.             mAlwaysInTapRegion = true;  
  265.             mAlwaysInBiggerTapRegion = true;  
  266.             mStillDown = true;  
  267.             mInLongPress = false;  
  268.   
  269.             if (mIsLongpressEnabled) {  
  270.                 mHandler.removeMessages(LONG_PRESS);  
  271.                 mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()  
  272.                         + TAP_TIMEOUT + LONGPRESS_TIMEOUT);  
  273.             }  
  274.             mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime()  
  275.                     + TAP_TIMEOUT);  
  276.             handled |= mListener.onDown(ev);  
  277.             break;  
  278.   
  279.         case MotionEvent.ACTION_MOVE:  
  280.             if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {  
  281.                 break;  
  282.             }  
  283.             final float scrollX = mLastMotionX - x;  
  284.             final float scrollY = mLastMotionY - y;  
  285.             if (mIsDoubleTapping) {  
  286.                 // Give the move events of the double-tap  
  287.                 handled |= mDoubleTapListener.onDoubleTapEvent(ev);  
  288.             } else if (mAlwaysInTapRegion) {  
  289.                 final int deltaX = (int) (x - mCurrentDownEvent.getX());  
  290.                 final int deltaY = (int) (y - mCurrentDownEvent.getY());  
  291.                 int distance = (deltaX * deltaX) + (deltaY * deltaY);  
  292.                 if (distance > mTouchSlopSquare) {  
  293.                     handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);  
  294.                     mLastMotionX = x;  
  295.                     mLastMotionY = y;  
  296.                     mAlwaysInTapRegion = fal6se;  
  297.                     mHandler.removeMessages(TAP);  
  298.                     mHandler.removeMessages(SHOW_PRESS);  
  299.                     mHandler.removeMessages(LONG_PRESS);  
  300.                 }  
  301.                 if (distance > mBiggerTouchSlopSquare) {  
  302.                     mAlwaysInBiggerTapRegion = false;  
  303.                 }  
  304.             } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {  
  305.                 handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);  
  306.                 mLastMotionX = x;  
  307.                 mLastMotionY = y;  
  308.             }  
  309.             break;  
  310.   
  311.         case MotionEvent.ACTION_UP:  
  312.             mStillDown = false;  
  313.             MotionEvent currentUpEvent = MotionEvent.obtain(ev);  
  314.             if (mIsDoubleTapping) {  
  315.                 // Finally, give the up event of the double-tap  
  316.                 handled |= mDoubleTapListener.onDoubleTapEvent(ev);  
  317.             } else if (mInLongPress) {  
  318.                 mHandler.removeMessages(TAP);  
  319.                 mInLongPress = false;  
  320.             } else if (mAlwaysInTapRegion) {  
  321.                 handled = mListener.onSingleTapUp(ev);  
  322.             } else {  
  323.   
  324.                 // A fling must travel the minimum tap distance  
  325.                 final VelocityTracker velocityTracker = mVelocityTracker;  
  326.                 velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);  
  327.                 final float velocityY = velocityTracker.getYVelocity();  
  328.                 final float velocityX = velocityTracker.getXVelocity();  
  329.   
  330.                 if ((Math.abs(velocityY) > mMinimumFlingVelocity)  
  331.                         || (Math.abs(velocityX) > mMinimumFlingVelocity)) {  
  332.                     handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);  
  333.                 }  
  334.             }  
  335.             if (mPreviousUpEvent != null) {  
  336.                 mPreviousUpEvent.recycle();  
  337.             }  
  338.             // Hold the event we obtained above - listeners may have changed the  
  339.             // original.  
  340.             mPreviousUpEvent = currentUpEvent;  
  341.             mVelocityTracker.recycle();  
  342.             mVelocityTracker = null;  
  343.             mIsDoubleTapping = false;  
  344.             mHandler.removeMessages(SHOW_PRESS);  
  345.             mHandler.removeMessages(LONG_PRESS);  
  346.             break;  
  347.         case MotionEvent.ACTION_CANCEL:  
  348.             cancel();  
  349.         }  
  350.         return handled;  
  351.     }  
  352.   
  353.     /** 
  354.      * 私有方法,取消的方法,移动消息队列中的消息,释放内存 
  355.      */  
  356.     private void cancel() {  
  357.         mHandler.removeMessages(SHOW_PRESS);  
  358.         mHandler.removeMessages(LONG_PRESS);  
  359.         mHandler.removeMessages(TAP);  
  360.         mVelocityTracker.recycle();  
  361.         mVelocityTracker = null;  
  362.         mIsDoubleTapping = false;  
  363.         mStillDown = false;  
  364.         if (mInLongPress) {  
  365.             mInLongPress = false;  
  366.         }  
  367.     }  
  368.   
  369.       
  370.     /** 
  371.      * 判断双击事件中,两次点击的位置关系。 
  372.      * 如果间隔很远,就不触发双击的事件 
  373.      */  
  374.     private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,  
  375.             MotionEvent secondDown) {  
  376.         if (!mAlwaysInBiggerTapRegion) {  
  377.             return false;  
  378.         }  
  379.   
  380.         if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {  
  381.             return false;  
  382.         }  
  383.   
  384.         int deltaX = (int) firstDown.getX() - (int) secondDown.getX();  
  385.         int deltaY = (int) firstDown.getY() - (int) secondDown.getY();  
  386.         return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);  
  387.     }  
  388.   
  389.     private void dispatchLongPress() {  
  390.         mHandler.removeMessages(TAP);  
  391.         mInLongPress = true;  
  392.         mListener.onLongPress(mCurrentDownEvent);  
  393.     }  
  394. }  

源码中也没啥详细的注释,看了一段时间,实在是看的头大,就放弃了。可以去看下GestureDetector类中文API:http://www.cnblogs.com/over140/archive/2011/01/17/1937044.html

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

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

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


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