/**
     * Analyzes the given motion event and if applicable triggers the appropriate callbacks on the
     * {@link OnGestureListener} supplied.
     *
     * @param ev The current motion event.
     * @return true if the {@link OnGestureListener} consumed the event, else false.
     */
    public boolean onTouchEvent(MotionEvent ev) {
      final int action = ev.getAction();

      if (mVelocityTracker == null) {
        mVelocityTracker = VelocityTracker.obtain();
      }
      mVelocityTracker.addMovement(ev);

      final boolean pointerUp =
          (action & MotionEventCompat.ACTION_MASK) == MotionEventCompat.ACTION_POINTER_UP;
      final int skipIndex = pointerUp ? MotionEventCompat.getActionIndex(ev) : -1;

      // Determine focal point
      float sumX = 0, sumY = 0;
      final int count = MotionEventCompat.getPointerCount(ev);
      for (int i = 0; i < count; i++) {
        if (skipIndex == i) continue;
        sumX += MotionEventCompat.getX(ev, i);
        sumY += MotionEventCompat.getY(ev, i);
      }
      final int div = pointerUp ? count - 1 : count;
      final float focusX = sumX / div;
      final float focusY = sumY / div;

      boolean handled = false;

      switch (action & MotionEventCompat.ACTION_MASK) {
        case MotionEventCompat.ACTION_POINTER_DOWN:
          mDownFocusX = mLastFocusX = focusX;
          mDownFocusY = mLastFocusY = focusY;
          // Cancel long press and taps
          cancelTaps();
          break;

        case MotionEventCompat.ACTION_POINTER_UP:
          mDownFocusX = mLastFocusX = focusX;
          mDownFocusY = mLastFocusY = focusY;

          // Check the dot product of current velocities.
          // If the pointer that left was opposing another velocity vector, clear.
          mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
          final int upIndex = MotionEventCompat.getActionIndex(ev);
          final int id1 = MotionEventCompat.getPointerId(ev, upIndex);
          final float x1 = VelocityTrackerCompat.getXVelocity(mVelocityTracker, id1);
          final float y1 = VelocityTrackerCompat.getYVelocity(mVelocityTracker, id1);
          for (int i = 0; i < count; i++) {
            if (i == upIndex) continue;

            final int id2 = MotionEventCompat.getPointerId(ev, i);
            final float x = x1 * VelocityTrackerCompat.getXVelocity(mVelocityTracker, id2);
            final float y = y1 * VelocityTrackerCompat.getYVelocity(mVelocityTracker, id2);

            final float dot = x + y;
            if (dot < 0) {
              mVelocityTracker.clear();
              break;
            }
          }
          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);
            }
          }

          mDownFocusX = mLastFocusX = focusX;
          mDownFocusY = mLastFocusY = focusY;
          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) {
            break;
          }
          final float scrollX = mLastFocusX - focusX;
          final float scrollY = mLastFocusY - focusY;
          if (mIsDoubleTapping) {
            // Give the move events of the double-tap
            handled |= mDoubleTapListener.onDoubleTapEvent(ev);
          } else if (mAlwaysInTapRegion) {
            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 > mTouchSlopSquare) {
              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
            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;
            final int pointerId = MotionEventCompat.getPointerId(ev, 0);
            velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
            final float velocityY = VelocityTrackerCompat.getYVelocity(velocityTracker, pointerId);
            final float velocityX = VelocityTrackerCompat.getXVelocity(velocityTracker, 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;

        case MotionEvent.ACTION_CANCEL:
          cancel();
          break;
      }

      return handled;
    }
Exemplo n.º 2
0
 @Override
 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
   if ((Build.VERSION.SDK_INT >= 14) || mapView.getSettings().SCROLL_MAP_BY_GESTURES.get())
     return fallback.onFling(e1, e2, velocityX / 3, velocityY / 3);
   return true;
 }