问题
I have a ListView inside of a ViewFlipper which I am flipping when the user swipes across the screen. Clicking on a ListView will open the browser. Sometimes when I am swiping, it gets detected as a touch on the ListView and will open the browser. This can be annoying. How can I prevent this from happening?
class MyGestureDetector extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
return false;
// right to left swipe
if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
viewFlipper.setInAnimation(slideLeftIn);
viewFlipper.setOutAnimation(slideLeftOut);
viewFlipper.showNext();
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
viewFlipper.setInAnimation(slideRightIn);
viewFlipper.setOutAnimation(slideRightOut);
viewFlipper.showPrevious();
}
if (viewFlipper.getDisplayedChild() == 0) {
// TODO: light up left
flipperPosition = 0;
} else if (viewFlipper.getDisplayedChild() == 1) {
// TODO: light up middle
flipperPosition = 1;
} else if (viewFlipper.getDisplayedChild() == 2) {
// TODO: light up right
flipperPosition = 2;
}
} catch (Exception e) {
System.out.println(e);
}
return false;
}
}
protected MotionEvent downStart = null;
public boolean onInterceptTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
// keep track of the starting down-event
downStart = MotionEvent.obtain(event);
break;
case MotionEvent.ACTION_MOVE:
// if moved horizontally more than slop*2, capture the event for ourselves
float deltaX = event.getX() - downStart.getX();
if(Math.abs(deltaX) > ViewConfiguration.getTouchSlop() * 2)
return true;
break;
}
// otherwise let the event slip through to children
return false;
}
回答1:
The way this is normally done is through the parent view's onInterceptTouchEvent method. onInterceptTouchEvent
has a chance to see any touch event before a view's children do. If onInterceptTouchEvent
returns true
the child view that was previously handling touch events receives an ACTION_CANCEL
and the events from that point forward are sent to the parent's onTouchEvent
method for the usual handling. It can also return false
and simply spy on events as they travel down the view hierarchy to their usual targets.
You want to do essentially this in onInterceptTouchEvent
on the parent view where you're detecting the flings:
- On
ACTION_DOWN
, record the location of the touch. Returnfalse
. - On
ACTION_MOVE
, check the delta between initial touch down position and current position. If it's past a threshold value, (the framework uses ViewConfiguration#getScaledTouchSlop() or other appropriate values fromViewConfiguration
for things like this,) returntrue
. - Detect and handle the fling as usual based on
onTouchEvent
.
Once you intercept, the ListView
will cancel its touch handling and you won't get unwanted tap events on your list items. ListView
is also set up to disallow its parent from intercepting events once the user has started vertically scrolling the list, which means you won't get mistaken horizontal flings if the user sloppily flings the list vertically.
This is how things like the stock Android Launcher or News and Weather do side to side paging of scrolling/tappable content.
回答2:
Have you tried using SimpleOnGestureListener.onSingleTapConfirmed(MotionEvent) for the on touch event ("click")? This will only be called after the detector is confident that the user's first tap is really a tap and not a double tap (or hopefully a fling).
class MyGestureDetector extends SimpleOnGestureListener {
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
// Code...
}
}
来源:https://stackoverflow.com/questions/3833857/how-to-differentiate-between-a-fling-and-a-touch