免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1313 | 回复: 0

[Android] Android 多点手势识别 [复制链接]

论坛徽章:
0
发表于 2015-06-04 11:45 |显示全部楼层
google 提供的API中,有个类,大家都很熟悉,GestureDetector。使用它,我们可以识别用户通常会用的手势。但是,这个类不支持多点触摸(可能 google认为没有人会在几个手指都在屏幕上的时候,使用手势吧~),不过,最近和朋友们一起做的一个App,的确用到了多点手势(主要是 onScroll和onFling两个手势),所以,我就把这个类拓展了一下,来实现让多个控件各自跟着一跟手指实现拖动和滑动的效果。
  顺便说一下,大家应该都知道,在Android3.0以后,Android的触摸事件的分配机制和以前的版本是有区别的。从3.0开始,用户在不同控件上操作产生的touch消息不会相互干扰,touch消息会被分派到不同控件上的touchListener中处理。而
在以前的版本中,所有的touch消息,都会被分排到第一个碰到屏幕的手指所操作的控件的touchListener中处理,也就是说,会出现这样一个矛盾的现象:
  在界面上有A,B,C三个控件,然后,当你先用食指按住A,跟着又用中指和无名指(嘛,别的手指也行,不用在意中指还是无名指)按住B,C。当中指和无名指移动的时候,B和C都无法接收到这个ACTION_MOVE消息,而接收到消息的却是A。而在3.0以上版本中,并不存在这个问题。
  使用以下的这个类,可以实现从2.2到3.2平台上手势识别的兼容。
  1. package com.finger.utils;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import android.content.Context;
  5. import android.os.Handler;
  6. import android.os.Message;
  7. import android.view.MotionEvent;
  8. import android.view.VelocityTracker;
  9. import android.view.ViewConfiguration;
  10. public class MultiTouchGestureDetector {
  11.     @SuppressWarnings("unused")
  12.     private static final String MYTAG = "Ray";
  13.     public static final String CLASS_NAME = "MultiTouchGestureDetector";
  14.     /**
  15.      * 事件信息类 <br/>
  16.      * 用来记录一个手势
  17.      */
  18.     private class EventInfo {
  19.         private MultiMotionEvent mCurrentDownEvent;    //当前的down事件
  20.         private MultiMotionEvent mPreviousUpEvent;    //上一次up事件
  21.         private boolean mStillDown;                    //当前手指是否还在屏幕上
  22.         private boolean mInLongPress;                //当前事件是否属于长按手势
  23.         private boolean mAlwaysInTapRegion;            //是否当前手指仅在小范围内移动,当手指仅在小范围内移动时,视为手指未曾移动过,不会触发onScroll手势
  24.         private boolean mAlwaysInBiggerTapRegion;    //是否当前手指在较大范围内移动,仅当此值为true时,双击手势才能成立
  25.         private boolean mIsDoubleTapping;            //当前手势,是否为双击手势
  26.         private float mLastMotionY;                    //最后一次事件的X坐标
  27.         private float mLastMotionX;                    //最后一次事件的Y坐标
  28.         private EventInfo(MotionEvent e) {
  29.             this(new MultiMotionEvent(e));
  30.         }
  31.         private EventInfo(MultiMotionEvent me) {
  32.             mCurrentDownEvent = me;
  33.             mStillDown = true;
  34.             mInLongPress = false;
  35.             mAlwaysInTapRegion = true;
  36.             mAlwaysInBiggerTapRegion = true;
  37.             mIsDoubleTapping = false;
  38.         }
  39.         //释放MotionEven对象,使系统能够继续使用它们
  40.         public void recycle() {
  41.             if (mCurrentDownEvent != null) {
  42.                 mCurrentDownEvent.recycle();
  43.                 mCurrentDownEvent = null;
  44.             }
  45.             if (mPreviousUpEvent != null) {
  46.                 mPreviousUpEvent.recycle();
  47.                 mPreviousUpEvent = null;
  48.             }
  49.         }
  50.         @Override
  51.         public void finalize() {
  52.             this.recycle();
  53.         }
  54.     }
  55.     /**
  56.      * 多点事件类 <br/>
  57.      * 将一个多点事件拆分为多个单点事件,并方便获得事件的绝对坐标
  58.      * <br/> 绝对坐标用以在界面中找到触点所在的控件
  59.      * @author ray-ni
  60.      */
  61.     public class MultiMotionEvent {
  62.         private MotionEvent mEvent;
  63.         private int mIndex;
  64.         private MultiMotionEvent(MotionEvent e) {
  65.             mEvent = e;
  66.             mIndex = (e.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;  //等效于 mEvent.getActionIndex();
  67.         }
  68.         private MultiMotionEvent(MotionEvent e, int idx) {
  69.             mEvent = e;
  70.             mIndex = idx;
  71.         }
  72.         // 行为
  73.         public int getAction() {
  74.             int action = mEvent.getAction() & MotionEvent.ACTION_MASK;    //等效于 mEvent.getActionMasked();
  75.             switch (action) {
  76.             case MotionEvent.ACTION_POINTER_DOWN:
  77.                 action = MotionEvent.ACTION_DOWN;
  78.                 break;
  79.             case MotionEvent.ACTION_POINTER_UP:
  80.                 action = MotionEvent.ACTION_UP;
  81.                 break;
  82.             }
  83.             return action;
  84.         }
  85.         // 返回X的绝对坐标
  86.         public float getX() {
  87.             return mEvent.getX(mIndex) + mEvent.getRawX() - mEvent.getX();
  88.         }
  89.         // 返回Y的绝对坐标
  90.         public float getY() {
  91.             return mEvent.getY(mIndex) + mEvent.getRawY() - mEvent.getY();
  92.         }
  93.         // 事件发生的时间
  94.         public long getEventTime() {
  95.             return mEvent.getEventTime();
  96.         }
  97.         // 事件序号
  98.         public int getIndex() {
  99.             return mIndex;
  100.         }
  101.      
  102.         // 事件ID
  103.         public int getId() {
  104.             return mEvent.getPointerId(mIndex);
  105.         }
  106.         
  107.         // 释放事件对象,使系统能够继续使用
  108.         public void recycle() {
  109.             if (mEvent != null) {
  110.                 mEvent.recycle();
  111.                 mEvent = null;
  112.             }
  113.         }
  114.     }
  115.     // 多点手势监听器
  116.     public interface MultiTouchGestureListener {
  117.         // 手指触碰到屏幕,由一个 ACTION_DOWN触发
  118.         boolean onDown(MultiMotionEvent e);
  119.         // 确定一个press事件,强调手指按下的一段时间(TAP_TIMEOUT)内,手指未曾移动或抬起
  120.         void onShowPress(MultiMotionEvent e);
  121.         // 手指点击屏幕后离开,由 ACTION_UP引发,可以简单的理解为单击事件,即手指点击时间不长(未构成长按事件),也不曾移动过
  122.         boolean onSingleTapUp(MultiMotionEvent e);
  123.         // 长按,手指点下后一段时间(DOUBLE_TAP_TIMEOUT)内,不曾抬起或移动
  124.         void onLongPress(MultiMotionEvent e);
  125.         // 拖动,由ACTION_MOVE触发,手指地按下后,在屏幕上移动
  126.         boolean onScroll(MultiMotionEvent e1, MultiMotionEvent e2, float distanceX, float distanceY);
  127.         // 滑动,由ACTION_UP触发,手指按下并移动一段距离后,抬起时触发
  128.         boolean onFling(MultiMotionEvent e1, MultiMotionEvent e2, float velocityX, float velocityY);
  129.     }
  130.     // 多点双击监听器
  131.     public interface MultiTouchDoubleTapListener {
  132.         // 单击事件确认,强调第一个单击事件发生后,一段时间内,未发生第二次单击事件,即确定不会触发双击事件
  133.         boolean onSingleTapConfirmed(MultiMotionEvent e);
  134.         // 双击事件, 由ACTION_DOWN触发,从第一次单击事件的DOWN事件开始的一段时间(DOUBLE_TAP_TIMEOUT)内结束(即手指),
  135.         // 并且在第一次单击事件的UP时间开始后的一段时间内(DOUBLE_TAP_TIMEOUT)发生第二次单击事件,
  136.         // 除此之外两者坐标间距小于定值(DOUBLE_TAP_SLAP)时,则触发双击事件
  137.         boolean onDoubleTap(MultiMotionEvent e);
  138.         // 双击事件,与onDoubleTap事件不同之处在于,构成双击的第二次点击的ACTION_DOWN,ACTION_MOVE和ACTION_UP都会触发该事件
  139.         boolean onDoubleTapEvent(MultiMotionEvent e);
  140.     }
  141.     // 事件信息队列,队列的下标与MotionEvent的pointId对应
  142.     private static List<EventInfo> sEventInfos = new ArrayList<EventInfo>(10);
  143.     // 双击判断队列,这个队列中的元素等待双击超时的判断结果
  144.     private static List<EventInfo> sEventForDoubleTap = new ArrayList<EventInfo>(5);
  145.     // 指定大点击区域的大小(这个比较拗口),这个值主要用于帮助判断双击是否成立
  146.     private int mBiggerTouchSlopSquare = 20 * 20;
  147.     // 判断是否构成onScroll手势,当手指在这个范围内移动时,不触发onScroll手势
  148.     private int mTouchSlopSquare;
  149.     // 判断是否构成双击,只有两次点击的距离小于该值,才能构成双击手势
  150.     private int mDoubleTapSlopSquare;
  151.     // 最小滑动速度
  152.     private int mMinimumFlingVelocity;
  153.     // 最大滑动速度
  154.     private int mMaximumFlingVelocity;

  155.     // 长按阀值,当手指按下后,在该阀值的时间内,未移动超过mTouchSlopSquare的距离并未抬起,则长按手势触发
  156.     private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
  157.     // showPress手势的触发阀值,当手指按下后,在该阀值的时间内,未移动超过mTouchSlopSquare的距离并未抬起,则showPress手势触发
  158.     private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
  159.     // 双击超时阀值,仅在两次双击事件的间隔(第一次单击的UP事件和第二次单击的DOWN事件)小于此阀值,双击事件才能成立
  160.     private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
  161.     // 双击区域阀值,仅在两次双击事件的距离小于此阀值,双击事件才能成立
  162.     private static final int DOUBLE_TAP_SLAP = 64;

  163.     // GestureHandler所处理的Message的what属性可能为以下 常量:
  164.     // showPress手势
  165.     private static final int SHOW_PRESS = 1;
  166.     // 长按手势
  167.     private static final int LONG_PRESS = 2;
  168.     // SingleTapConfirmed手势
  169.     private static final int TAP_SINGLE = 3;
  170.     // 双击手势
  171.     private static final int TAP_DOUBLE = 4;
  172.      
  173.     // 手势处理器
  174.     private final GestureHandler mHandler;
  175.     // 手势监听器
  176.     private final MultiTouchGestureListener mListener;
  177.     // 双击监听器
  178.     private MultiTouchDoubleTapListener mDoubleTapListener;
  179.      
  180.     // 长按允许阀值
  181.     private boolean mIsLongpressEnabled;
  182.     // 速度追踪器
  183.     private VelocityTracker mVelocityTracker;

  184.     private class GestureHandler extends Handler {
  185.         GestureHandler() {
  186.             super();
  187.         }
  188.         GestureHandler(Handler handler) {
  189.             super(handler.getLooper());
  190.         }
  191.         @Override
  192.         public void handleMessage(Message msg) {
  193.             int idx = (Integer) msg.obj;
  194.             switch (msg.what) {
  195.             case SHOW_PRESS: {
  196.                 if (idx >= sEventInfos.size()) {
  197. //                    Log.w(MYTAG, CLASS_NAME + ":handleMessage, msg.what = SHOW_PRESS, idx=" + idx + ", while sEventInfos.size()="
  198. //                            + sEventInfos.size());
  199.                     break;
  200.                 }
  201.                 EventInfo info = sEventInfos.get(idx);
  202.                 if (info == null) {
  203. //                    Log.e(MYTAG, CLASS_NAME + ":handleMessage, msg.what = SHOW_PRESS, idx=" + idx + ", Info = null");
  204.                     break;
  205.                 }
  206.                 // 触发手势监听器的onShowPress事件
  207.                 mListener.onShowPress(info.mCurrentDownEvent);
  208.                 break;
  209.             }
  210.             case LONG_PRESS: {
  211.                 // Log.d(MYTAG, CLASS_NAME + ":trigger LONG_PRESS");


  212.                 if (idx >= sEventInfos.size()) {
  213. //                    Log.w(MYTAG, CLASS_NAME + ":handleMessage, msg.what = LONG_PRESS, idx=" + idx + ", while sEventInfos.size()="
  214. //                            + sEventInfos.size());
  215.                     break;
  216.                 }
  217.                 EventInfo info = sEventInfos.get(idx);
  218.                 if (info == null) {
  219. //                    Log.e(MYTAG, CLASS_NAME + ":handleMessage, msg.what = LONG_PRESS, idx=" + idx + ", Info = null");
  220.                     break;
  221.                 }
  222.                 dispatchLongPress(info, idx);
  223.                 break;
  224.             }
  225.             case TAP_SINGLE: {
  226.                 // Log.d(MYTAG, CLASS_NAME + ":trriger TAP_SINGLE");
  227.                 // If the user's finger is still down, do not count it as a tap
  228.                 if (idx >= sEventInfos.size()) {
  229. //                    Log.e(MYTAG, CLASS_NAME + ":handleMessage, msg.what = TAP_SINGLE, idx=" + idx + ", while sEventInfos.size()="
  230. //                            + sEventInfos.size());
  231.                     break;
  232.                 }
  233.                 EventInfo info = sEventInfos.get(idx);
  234.                 if (info == null) {
  235. //                    Log.e(MYTAG, CLASS_NAME + ":handleMessage, msg.what = TAP_SINGLE, idx=" + idx + ", Info = null");
  236.                     break;
  237.                 }
  238.                 if (mDoubleTapListener != null && !info.mStillDown) { //手指在双击超时的阀值内未离开屏幕进行第二次单击事件,则确定单击事件成立(不再触发双击事件)
  239.                     mDoubleTapListener.onSingleTapConfirmed(info.mCurrentDownEvent);
  240.                 }
  241.                 break;
  242.             }
  243.             case TAP_DOUBLE: {
  244.                 if (idx >= sEventForDoubleTap.size()) {
  245. //                    Log.w(MYTAG, CLASS_NAME + ":handleMessage, msg.what = TAP_DOUBLE, idx=" + idx + ", while sEventForDoubleTap.size()="
  246. //                            + sEventForDoubleTap.size());
  247.                     break;
  248.                 }
  249.                 EventInfo info = sEventForDoubleTap.get(idx);
  250.                 if (info == null) {
  251. //                    Log.w(MYTAG, CLASS_NAME + ":handleMessage, msg.what = TAP_DOUBLE, idx=" + idx + ", Info = null");
  252.                     break;
  253.                 }
  254.                 sEventForDoubleTap.set(idx, null);// 这个没什么好做的,就是把队列中对应的元素清除而已
  255.                 break;
  256.             }
  257.             default:
  258.                 throw new RuntimeException("Unknown message " + msg); // never
  259.             }
  260.         }
  261.     }
  262.     /**
  263.      * 触发长按事件
  264.      * @param info
  265.      * @param idx
  266.      */
  267.     private void dispatchLongPress(EventInfo info, int idx) {
  268.         mHandler.removeMessages(TAP_SINGLE, idx);//移除单击事件确认
  269.         info.mInLongPress = true;
  270.         mListener.onLongPress(info.mCurrentDownEvent);
  271.     }
  272.      
  273.      /**
  274.      * 构造器1
  275.      * @param context
  276.      * @param listener
  277.      */
  278.     public MultiTouchGestureDetector(Context context, MultiTouchGestureListener listener) {
  279.         this(context, listener, null);
  280.     }
  281.     /**
  282.      * 构造器2
  283.      * @param context
  284.      * @param listener
  285.      * @param handler
  286.      */
  287.     public MultiTouchGestureDetector(Context context, MultiTouchGestureListener listener, Handler handler) {
  288.         if (handler != null) {
  289.             mHandler = new GestureHandler(handler);
  290.         } else {
  291.             mHandler = new GestureHandler();
  292.         }
  293.         mListener = listener;
  294.         if (listener instanceof MultiTouchDoubleTapListener) {
  295.             setOnDoubleTapListener((MultiTouchDoubleTapListener) listener);
  296.         }
  297.         init(context);
  298.     }
  299.     /**
  300.      * 初始化识别器
  301.      * @param context
  302.      */
  303.     private void init(Context context) {
  304.         if (mListener == null) {
  305.             throw new NullPointerException("OnGestureListener must not be null");
  306.         }
  307.         mIsLongpressEnabled = true;
  308.         int touchSlop, doubleTapSlop;
  309.         if (context == null) {
  310.             touchSlop = ViewConfiguration.getTouchSlop();
  311.             doubleTapSlop = DOUBLE_TAP_SLAP;
  312.             mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
  313.             mMaximumFlingVelocity = ViewConfiguration.getMaximumFlingVelocity();
  314.         } else {//允许识别器在App中,使用偏好的设定
  315.             final ViewConfiguration configuration = ViewConfiguration.get(context);
  316.             touchSlop = configuration.getScaledTouchSlop();
  317.             doubleTapSlop = configuration.getScaledDoubleTapSlop();
  318.             mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
  319.             mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
  320.         }
  321.         mTouchSlopSquare = touchSlop * touchSlop / 16;
  322.         mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
  323.     }
  324.     /**
  325.      * 设置双击监听器
  326.      * @param onDoubleTapListener
  327.      */
  328.     public void setOnDoubleTapListener(MultiTouchDoubleTapListener onDoubleTapListener) {
  329.         mDoubleTapListener = onDoubleTapListener;
  330.     }
  331.     /**
  332.      * 设置是否允许长按
  333.      * @param isLongpressEnabled
  334.      */
  335.     public void setIsLongpressEnabled(boolean isLongpressEnabled) {
  336.         mIsLongpressEnabled = isLongpressEnabled;
  337.     }
  338.     /**
  339.      * 判断是否允许长按
  340.      * @return
  341.      */
  342.     public boolean isLongpressEnabled() {
  343.         return mIsLongpressEnabled;
  344.     }
  345.     /**
  346.      * 判断当前事件是否为双击事件
  347.      * <br/> 通过遍历sEventForDoubleTap来匹配是否存在能够构成双击事件的单击事件
  348.      * @param e
  349.      * @return
  350.      */
  351.     private EventInfo checkForDoubleTap(MultiMotionEvent e) {
  352.         if (sEventForDoubleTap.isEmpty()) {
  353. //            Log.e(MYTAG, CLASS_NAME + ":checkForDoubleTap(), sEventForDoubleTap is empty !");
  354.             return null;
  355.         }
  356.         for (int i = 0; i < sEventForDoubleTap.size(); i++) {
  357.             EventInfo info = sEventForDoubleTap.get(i);
  358.             if (info != null && isConsideredDoubleTap(info, e)) {
  359.                 sEventForDoubleTap.set(i, null);// 这个单击事件已经被消耗了,所以置为null
  360.                 mHandler.removeMessages(TAP_DOUBLE, i);// 移除Handler内的为处理消息
  361.                 return info;
  362.             }
  363.         }
  364.         return null;
  365.     }
  366.     /**
  367.      * 判断当前按下事件是否能和指定的单击事件构成双击事件
  368.      *
  369.      * @param info
  370.      * @param secondDown
  371.      * @return
  372.      */
  373.     private boolean isConsideredDoubleTap(EventInfo info, MultiMotionEvent secondDown) {
  374.         if (!info.mAlwaysInBiggerTapRegion) { //如多第一次单击事件有过较大距离的移动,则无法构成双击事件
  375.             return false;
  376.         }
  377.         if (secondDown.getEventTime() - info.mPreviousUpEvent.getEventTime() > DOUBLE_TAP_TIMEOUT) {
  378.             //如果第一次单击的UP时间和第二次单击的down时间时间间隔大于DOUBLE_TAP_TIMEOUT,也无法构成双击事件
  379.             return false;
  380.         }
  381.         int deltaX = (int) info.mCurrentDownEvent.getX() - (int) secondDown.getX();
  382.         int deltaY = (int) info.mCurrentDownEvent.getY() - (int) secondDown.getY();
  383.         return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);//最后判断两次单击事件的距离

  384.     }


  385.     /**
  386.      * 将事件信息放入双击判断队列,并返回序号
  387.      *
  388.      * @param info
  389.      * @return
  390.      */
  391.     private int addIntoTheMinIndex(EventInfo info) {
  392.         for (int i = 0; i < sEventForDoubleTap.size(); i++) {
  393.             if (sEventForDoubleTap.get(i) == null) {
  394.                 sEventForDoubleTap.set(i, info);
  395.                 return i;
  396.             }
  397.         }
  398.         sEventForDoubleTap.add(info);
  399.         return sEventForDoubleTap.size() - 1;
  400.     }

  401.     /**
  402.      * 从事件信息队列中移除指定序号的事件
  403.      *
  404.      * @param idx
  405.      */
  406.     private void removeEventFromList(int id) {
  407.         if (id > sEventInfos.size() || id < 0) {
  408. //            Log.e(MYTAG, CLASS_NAME + ".removeEventFromList(), id=" + id + ", while sEventInfos.size() =" + sEventInfos.size());
  409.             return;
  410.         }
  411.         sEventInfos.set(id, null);
  412.     }

  413.     /**
  414.      * 向事件队列中添加新信息
  415.      *
  416.      * @param e
  417.      */
  418.     private void addEventIntoList(EventInfo info) {
  419.         int id = info.mCurrentDownEvent.getId();
  420.         if (id < sEventInfos.size()) {
  421. //            if (sEventInfos.get(id) != null)
  422. //                Log.e(MYTAG, CLASS_NAME + ".addEventIntoList, info(" + id + ") has not set to null !");
  423.             sEventInfos.set(info.mCurrentDownEvent.getId(), info);
  424.         } else if (id == sEventInfos.size()) {
  425.             sEventInfos.add(info);
  426.         } else {
  427. //            Log.e(MYTAG, CLASS_NAME + ".addEventIntoList, invalidata id !");
  428.         }
  429.     }
  430.      
  431.     public boolean onTouchEvent(MotionEvent ev) {
  432.         if (mVelocityTracker == null) {
  433.             mVelocityTracker = VelocityTracker.obtain();
  434.         }
  435.         mVelocityTracker.addMovement(ev);//把所有事件都添加到速度追踪器,为计算速度做准备
  436.         boolean handled = false;
  437.         final int action = ev.getAction(); //获取Action
  438. //        int idx = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;//获取触摸事件的序号
  439.         int idx = ev.getPointerId(ev.getActionIndex());//获取触摸事件的id
  440.         switch (action & MotionEvent.ACTION_MASK) {
  441.         case MotionEvent.ACTION_DOWN:
  442.         case MotionEvent.ACTION_POINTER_DOWN: {
  443.             EventInfo info = new EventInfo(MotionEvent.obtain(ev));
  444.             this.addEventIntoList(info);//将手势信息保存到队列中
  445.             if (mDoubleTapListener != null) {//如果双击监听器不为null
  446.                 if (mHandler.hasMessages(TAP_DOUBLE)) {
  447.                     MultiMotionEvent e = new MultiMotionEvent(ev);
  448.                     EventInfo origInfo = checkForDoubleTap(e);//检查是否构成双击事件
  449.                     if (origInfo != null) {
  450.                         info.mIsDoubleTapping = true;
  451.                         handled |= mDoubleTapListener.onDoubleTap(origInfo.mCurrentDownEvent);
  452.                         handled |= mDoubleTapListener.onDoubleTapEvent(e);
  453.                     }
  454.                 }
  455.                 if (!info.mIsDoubleTapping) {//当前事件不构成双击事件,那么发送延迟消息以判断onSingleTapConfirmed事件
  456.                     mHandler.sendMessageDelayed(mHandler.obtainMessage(TAP_SINGLE, idx), DOUBLE_TAP_TIMEOUT);
  457.                     // Log.d(MYTAG, CLASS_NAME + ": add TAP_SINGLE");
  458.                 }
  459.             }
  460.             // 记录X坐标和Y坐标
  461.             info.mLastMotionX = info.mCurrentDownEvent.getX();
  462.             info.mLastMotionY = info.mCurrentDownEvent.getY();
  463.             
  464.             if (mIsLongpressEnabled) {//允许长按
  465.                 mHandler.removeMessages(LONG_PRESS, idx);
  466.                 mHandler.sendMessageAtTime(mHandler.obtainMessage(LONG_PRESS, idx), info.mCurrentDownEvent.getEventTime() + TAP_TIMEOUT
  467.                         + LONGPRESS_TIMEOUT);//延时消息以触发长按手势
  468.                 // Log.d(MYTAG, CLASS_NAME +
  469.                 // ":add LONG_PRESS to handler  for idx " + idx);
  470.             }
  471.             mHandler.sendMessageAtTime(mHandler.obtainMessage(SHOW_PRESS, idx), info.mCurrentDownEvent.getEventTime() + TAP_TIMEOUT);// 延时消息,触发showPress手势
  472.             handled |= mListener.onDown(info.mCurrentDownEvent);//触发onDown()
  473.             break;
  474.         }
  475.         case MotionEvent.ACTION_UP:
  476.         case MotionEvent.ACTION_POINTER_UP: {
  477.             MultiMotionEvent currentUpEvent = new MultiMotionEvent(ev);
  478.             if (idx >= sEventInfos.size()) {
  479. //                Log.e(MYTAG, CLASS_NAME + ":ACTION_POINTER_UP, idx=" + idx + ", while sEventInfos.size()=" + sEventInfos.size());
  480.                 break;
  481.             }
  482.             EventInfo info = sEventInfos.get(currentUpEvent.getId());
  483.             if (info == null) {
  484. //                Log.e(MYTAG, CLASS_NAME + ":ACTION_POINTER_UP, idx=" + idx + ", Info = null");
  485.                 break;
  486.             }
  487.             info.mStillDown = false;
  488.             if (info.mIsDoubleTapping) { //处于双击状态,则触发onDoubleTapEvent事件
  489.                 handled |= mDoubleTapListener.onDoubleTapEvent(currentUpEvent);
  490.             } else if (info.mInLongPress) {//处于长按状态
  491.                 mHandler.removeMessages(TAP_SINGLE, idx);//可以无视这行代码
  492.                 info.mInLongPress = false;
  493.             } else if (info.mAlwaysInTapRegion) {//尚未移动过
  494.                 if (mHandler.hasMessages(TAP_SINGLE, idx)) {//还在双击的时间阀值内,所以要为双击判断做额外处理
  495.                     mHandler.removeMessages(TAP_SINGLE, idx);
  496.                     info.mPreviousUpEvent = new MultiMotionEvent(MotionEvent.obtain(ev));
  497.                     int index = this.addIntoTheMinIndex(info);// 把当前事件放入队列,等待双击的判断
  498.                     mHandler.sendMessageAtTime(mHandler.obtainMessage(TAP_DOUBLE, index), info.mCurrentDownEvent.getEventTime()
  499.                             + DOUBLE_TAP_TIMEOUT); // 将双击超时判断添加到Handler
  500.                     // Log.d(MYTAG, CLASS_NAME + ": add TAP_DOUBLE");
  501.                 }
  502.                 handled = mListener.onSingleTapUp(currentUpEvent);//触发onSingleTapUp事件
  503.             } else {
  504.                 // A fling must travel the minimum tap distance
  505.                 final VelocityTracker velocityTracker = mVelocityTracker;
  506.                 velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);//计算1秒钟内的滑动速度
  507.                 //获取X和Y方向的速度
  508.                 final float velocityX = velocityTracker.getXVelocity(idx);
  509.                 final float velocityY = velocityTracker.getYVelocity(idx);
  510.                 // Log.i(MYTAG, CLASS_NAME + ":ACTION_POINTER_UP, idx=" + idx +
  511.                 // ", vx=" + velocityX + ", vy=" + velocityY);
  512.                 // 触发滑动事件
  513.                 if ((Math.abs(velocityY) > mMinimumFlingVelocity) || (Math.abs(velocityX) > mMinimumFlingVelocity)) {
  514.                     handled = mListener.onFling(info.mCurrentDownEvent, currentUpEvent, velocityX, velocityY);
  515.                 }
  516.             }
  517.             // Hold the event we obtained above - listeners may have changed the
  518.             // original.
  519.             if (action == MotionEvent.ACTION_UP) {    //释放速度追踪器
  520.                 mVelocityTracker.recycle();
  521.                 mVelocityTracker = null;
  522.                 // Log.w(MYTAG, CLASS_NAME +
  523.                 // ":ACTION_POINTER_UP, mVelocityTracker.recycle()");
  524.             }
  525.          
  526.             info.mIsDoubleTapping = false;
  527.             // Log.d(MYTAG, CLASS_NAME + "remove LONG_PRESS");
  528.             // 移除showPress和长按消息
  529.             mHandler.removeMessages(SHOW_PRESS, idx);
  530.             mHandler.removeMessages(LONG_PRESS, idx);
  531.             removeEventFromList(currentUpEvent.getId());//手指离开,则从队列中删除手势信息
  532.             break;
  533.         }
  534.         case MotionEvent.ACTION_MOVE:
  535.             for (int rIdx = 0; rIdx < ev.getPointerCount(); rIdx++) {//因为无法确定当前发生移动的是哪个手指,所以遍历处理所有手指
  536.                 MultiMotionEvent e = new MultiMotionEvent(ev, rIdx);
  537.                 if (e.getId() >= sEventInfos.size()) {
  538. //                    Log.e(MYTAG, CLASS_NAME + ":ACTION_MOVE, idx=" + rIdx + ", while sEventInfos.size()=" + sEventInfos.size());
  539.                     break;
  540.                 }
  541.                 EventInfo info = sEventInfos.get(e.getId());
  542.                 if (info == null) {
  543. //                    Log.e(MYTAG, CLASS_NAME + ":ACTION_MOVE, idx=" + rIdx + ", Info = null");
  544.                     break;
  545.                 }
  546.                 if (info.mInLongPress) {    //长按,则不处理move事件
  547.                     break;
  548.                 }
  549.                 //当前坐标
  550.                 float x = e.getX();
  551.                 float y = e.getY();
  552.                 //距离上次事件移动的位置
  553.                 final float scrollX = x - info.mLastMotionX;
  554.                 final float scrollY = y - info.mLastMotionY;
  555.                 if (info.mIsDoubleTapping) {//双击事件
  556.                     handled |= mDoubleTapListener.onDoubleTapEvent(e);
  557.                 } else if (info.mAlwaysInTapRegion) {//该手势尚未移动过(移动的距离小于mTouchSlopSquare,视为未移动过)
  558.                     // 计算从落下到当前事件,移动的距离
  559.                     final int deltaX = (int) (x - info.mCurrentDownEvent.getX());
  560.                     final int deltaY = (int) (y - info.mCurrentDownEvent.getY());
  561.                     // Log.d(MYTAG, CLASS_NAME + "deltaX="+deltaX+";deltaY=" +
  562.                     // deltaX +"mTouchSlopSquare=" + mTouchSlopSquare);
  563.                     int distance = (deltaX * deltaX) + (deltaY * deltaY);
  564.                     if (distance > mTouchSlopSquare) {     // 移动距离超过mTouchSlopSquare
  565.                         handled = mListener.onScroll(info.mCurrentDownEvent, e, scrollX, scrollY);
  566.                         info.mLastMotionX = e.getX();
  567.                         info.mLastMotionY = e.getY();
  568.                         info.mAlwaysInTapRegion = false;
  569.                         // Log.d(MYTAG, CLASS_NAME +
  570.                         // ":remove LONG_PRESS for idx" + rIdx +
  571.                         // ",mTouchSlopSquare("+mTouchSlopSquare+"), distance("+distance+")");
  572.                         // 清除onSingleTapConform,showPress,longPress三种消息
  573.                         int id = e.getId();
  574.                         mHandler.removeMessages(TAP_SINGLE, id);
  575.                         mHandler.removeMessages(SHOW_PRESS, id);
  576.                         mHandler.removeMessages(LONG_PRESS, id);
  577.                     }
  578.                     if (distance > mBiggerTouchSlopSquare) {//移动距离大于mBiggerTouchSlopSquare,则无法构成双击事件
  579.                         info.mAlwaysInBiggerTapRegion = false;
  580.                     }
  581.                 } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {//之前已经移动过了
  582.                     handled = mListener.onScroll(info.mCurrentDownEvent, e, scrollX, scrollY);
  583.                     info.mLastMotionX = x;
  584.                     info.mLastMotionY = y;
  585.                 }
  586.             }
  587.             break;
  588.         case MotionEvent.ACTION_CANCEL:
  589.             cancel();//清理
  590.         }
  591.         return handled;
  592.     }
  593.     // 清理所有队列
  594.     private void cancel() {
  595.         mHandler.removeMessages(SHOW_PRESS);
  596.         mHandler.removeMessages(LONG_PRESS);
  597.         mHandler.removeMessages(TAP_SINGLE);
  598.         mVelocityTracker.recycle();
  599.         mVelocityTracker = null;
  600.         sEventInfos.clear();
  601.         sEventForDoubleTap.clear();
  602.     }

  603. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP