Pergunta

I am using the onTouch method to catch a touch with ACTION_UP and GestureDetector to capture the double tap, my issue is a double tap results in a tap then a double tap, then a tap. Is there a way to have a double tap block a tap or something like that? I know logically what its doing is correct, so if you advise I find another way just comment, please dont down vote. Thanks!

Foi útil?

Solução

I would suggest that you switch to the SimpleGestureListener and use the onDoubleTap() and onSingleTapConfirmed() methods.

Outras dicas

To be more precise than what is said by britzl, the GestureDetector does the actual work of determining when something is a single tap, double tap, long press, etc. The SimpleGestureListener is just a "listener" the GestureDetector uses to indicate what it recognized. It implements OnGestureListener and OnDoubleTapListener just to always return false. Check out a snippet from the onTouchEvent(MotionEvent) in GestureDetector:

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;
        mDeferConfirmSingleTap = 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;

Then the desired result can be obtained by creating a GestureDetector with the appropriate listener:

final View.OnTouchListener touch_listener = new View.OnTouchListener() {

            @Override public boolean onTouch(View view, MotionEvent event) {
                return _gesture_detector.onTouchEvent(event);
            }

            private final GestureDetector _gesture_detector = new GestureDetector(getContext()
                    , new GestureDetector.SimpleOnGestureListener() {

                @Override public boolean onSingleTapConfirmed(MotionEvent event) {
                    // TODO: implement single tap behavior
                    // NOTE: returning true indicates that the gesture was handled
                    return true;
                }

                @Override public boolean onDoubleTap(MotionEvent event) {
                    // TODO: implement double tap behavior
                    // NOTE: returning true indicates that the gesture was handled
                    return true;
                }
            });
        };

And from there, this OnTouchListener can be set to the View that wants the behavior.

It works by using the default GestureHandler (which is a Handler):

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) {
                if (!mStillDown) {
                    mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
                } else {
                    mDeferConfirmSingleTap = true;
                }
            }
            break;

        default:
            throw new RuntimeException("Unknown message " + msg); //never
        }
    }
}

Recall the line mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); from the GestureDetector. It delays notification of the tap by the timeout period for a valid double tap gesture. And the line if (hadTapMessage) mHandler.removeMessages(TAP); from the GestureDetector removes that notification upon a valid double tap. The GestureHandler receives the tap notification after the delay and uses the callback to notify the GestureListener with mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);. GestureHandler will delay that callback to the MotionEvent.ACTION_UP (handled by the GestureDetector) in the event that the user's finger is down when the tap notification is received.

Besides what britzl suggests you may want to check your logic there for a second.

I don't think a double tap results in multiple, it simply results in 4 events, like you somewhat mention. While the Gesture libraries are (i think) the best choice, you should consider that:

  • Store the Motionevent's timestamp (it's one of its methods) on ACTION_UP , then compare it with the next action up. Provided with a timeout, you will know when it is a tap or a double tap.

That is what the gesture listeners do

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top