android Manage touch events in a ViewGroup
https://developer.android.google.cn/training/gestures/viewgroup?hl=en
Intercept touch events in a ViewGroup
The onInterceptTouchEvent()
method is called whenever a touch event is detected on the surface of a ViewGroup
, including on the surface of its children. If onInterceptTouchEvent()
returns true
, the MotionEvent
is intercepted, meaning it is not passed on to the child, but rather to the onTouchEvent()
method of the parent.
The onInterceptTouchEvent()
method gives a parent the chance to see any touch event before its children do. If you return true
from onInterceptTouchEvent()
, 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. onInterceptTouchEvent()
can also return false
and simply spy on events as they travel down the view hierarchy to their usual targets, which will handle the events with their own onTouchEvent()
.
In the following snippet, the class MyViewGroup
extends ViewGroup
. MyViewGroup
contains multiple child views. If you drag your finger across a child view horizontally, the child view should no longer get touch events, and MyViewGroup
should handle touch events by scrolling its contents. However, if you press buttons in the child view, or scroll the child view vertically, the parent shouldn't intercept those touch events, because the child is the intended target. In those cases, onInterceptTouchEvent()
should return false
, and MyViewGroup
's onTouchEvent()
won't be called.
public class MyViewGroup extends ViewGroup {
private int mTouchSlop;
...
ViewConfiguration vc = ViewConfiguration.get(view.getContext());
mTouchSlop = vc.getScaledTouchSlop();
...
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onTouchEvent will be called and we do the actual
* scrolling there.
*/
final int action = MotionEventCompat.getActionMasked(ev);
// Always handle the case of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the scroll.
mIsScrolling = false;
return false; // Do not intercept touch event, let the child handle it
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
if (mIsScrolling) {
// We're currently scrolling, so yes, intercept the
// touch event!
return true;
}
// If the user has dragged her finger horizontally more than
// the touch slop, start the scroll
// left as an exercise for the reader
final int xDiff = calculateDistanceX(ev);
// Touch slop should be calculated using ViewConfiguration
// constants.
if (xDiff > mTouchSlop) {
// Start scrolling!
mIsScrolling = true;
return true;
}
break;
}
...
}
// In general, we don't want to intercept touch events. They should be
// handled by the child view.
return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Here we actually handle the touch event (e.g. if the action is ACTION_MOVE,
// scroll this container).
// This method will only be called if the touch event was intercepted in
// onInterceptTouchEvent
...
}
}
Note that ViewGroup
also provides a requestDisallowInterceptTouchEvent()
method. The ViewGroup
calls this method when a child does not want the parent and its ancestors to intercept touch events with onInterceptTouchEvent()
.