I would suggest that you switch to the SimpleGestureListener and use the onDoubleTap() and onSingleTapConfirmed() methods.
Android distinguish between tap and double tap
-
30-05-2022 - |
문제
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!
해결책
다른 팁
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