Android: Difference between onInterceptTouchEvent and dispatchTouchEvent?
What is the difference between onInterceptTouchEvent
and dispatchTouchEvent
in Android?
According to the android developer guide, both methods can be used to intercept a touch event (MotionEvent
), but what is the difference?
How do onInterceptTouchEvent
, dispatchTouchEvent
and onTouchEvent
interact together within a hierarchy of Views (ViewGroup
)?
The best place to demystify this is the source code. The docs are woefully inadequate about explaining this.
dispatchTouchEvent is actually defined on Activity, View and ViewGroup. Think of it as a controller which decides how to route the touch events.
For example, the simplest case is that of View.dispatchTouchEvent which will route the touch event to either OnTouchListener.onTouch if it's defined or to the extension method onTouchEvent.
For ViewGroup.dispatchTouchEvent things are way more complicated. It needs to figure out which one of its child views should get the event (by calling child.dispatchTouchEvent). This is basically a hit testing algorithm where you figure out which child view's bounding rectangle contains the touch point coordinates.
But before it can dispatch the event to the appropriate child view, the parent can spy and/or intercept the event all together. This is what onInterceptTouchEvent is there for. So it calls this method first before doing the hit testing and if the event was hijacked (by returning true from onInterceptTouchEvent) it sends a ACTION_CANCEL to the child views so they can abandon their touch event processing (from previous touch events) and from then onwards all touch events at the parent level are dispatched to onTouchListener.onTouch (if defined) or onTouchEvent(). Also in that case, onInterceptTouchEvent is never called again.
Would you even want to override [Activity|ViewGroup|View].dispatchTouchEvent? Unless you are doing some custom routing you probably should not.
The main extension methods are ViewGroup.onInterceptTouchEvent if you want to spy and/or intercept touch event at the parent level and View.onTouchListener/View.onTouchEvent for main event handling.
All in all its overly complicated design imo but android apis lean more towards flexibility than simplicity.
Because this is the first result on Google. I want to share with you a great Talk by Dave Smith on Youtube: Mastering the Android Touch System and the slides are available here. It gave me a good deep understanding about the Android Touch System:
How the Activity handles touch:
Activity.dispatchTouchEvent()
- Always first to be called
- Sends event to root view attached to Window
onTouchEvent()
- Called if no views consume the event
- Always last to be called
How the View handles touch:
View.dispatchTouchEvent()
- Sends event to listener first, if exists
View.OnTouchListener.onTouch()
- If not consumed, processes the touch itself
View.onTouchEvent()
How a ViewGroup handles touch:
ViewGroup.dispatchTouchEvent()
onInterceptTouchEvent()
- Check if it should supersede children
- Passes
ACTION_CANCEL
to active child- If it returns true once, the
ViewGroup
consumes all subsequent events- For each child view (in reverse order they were added)
- If touch is relevant (inside view),
child.dispatchTouchEvent()
- If it is not handled by a previous, dispatch to next view
- If no children handles the event, the listener gets a chance
OnTouchListener.onTouch()
- If there is no listener, or its not handled
onTouchEvent()
- Intercepted events jump over the child step
He also provides example code of custom touch on github.com/devunwired/.
Answer:
Basically the dispatchTouchEvent()
is called on every View
layer to determine if a View
is interested in an ongoing gesture. In a ViewGroup
the ViewGroup
has the ability to steal the touch events in his dispatchTouchEvent()
-method, before it would call dispatchTouchEvent()
on the children. The ViewGroup
would only stop the dispatching if the ViewGroup
onInterceptTouchEvent()
-method returns true. The difference is that dispatchTouchEvent()
is dispatching MotionEvents
and onInterceptTouchEvent
tells if it should intercept (not dispatching the MotionEvent
to children) or not (dispatching to children).
You could imagine the code of a ViewGroup doing more-or-less this (very simplified):
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!onInterceptTouchEvent()){
for(View child : children){
if(child.dispatchTouchEvent(ev))
return true;
}
}
return super.dispatchTouchEvent(ev);
}
Supplemental Answer
Here are some visual supplements to the other answers. My full answer is here.
The dispatchTouchEvent()
method of a ViewGroup
uses onInterceptTouchEvent()
to choose whether it should immediately handle the touch event (with onTouchEvent()
) or continue notifying the dispatchTouchEvent()
methods of its children.