Sometimes you will have to process Touch Events yourself and not rely on an onSomethingListener being available. I’ve had moments where I wanted an article that simply explained how touch events are propagated in the View Hierarchy as a starting point for further study. This blog post is my attempt, and it seems a bit long, but that’s because I’m following the spread of touch events step by step.

Some assumptions


We only consider the four most important touch events: DOWN,MOVE,UP, and CANCEL. A gesture is an event column that starts with a DOWN event (generated when the user touches the screen), followed by zero or more MOVE events (generated when the user moves his finger around), This is followed by a separate UP or CANCEL event (generated when the user’s finger leaves the screen or the system tells you that the gesture is over for some other reason). When we talk about the “rest of the gesture” we mean the MOVE event that follows the gesture and the UP or CANCEL event at the end.

I’m also not going to consider multi-touch gestures here (we’ll just assume one finger) and ignore the fact that multiple MOVE events can be grouped together. Finally, we assume that none of the views in this article is registered with an onTouchListener.

The view hierarchy we will discuss looks like this: The outermost layer is A ViewGroup A, which contains one or more children (children). One of the children is ViewGroupB, and ViewGroupB contains one or more children. One of the child views is View C, which is not a view group. Here we ignore the possibility of overlapping views at the same level.



android-touch.png

Suppose the first point on the screen that the user touches is a point on C that is marked as a touch point, and the DOWN event is generated at that point. The user then moves his finger and finally moves away from the screen. It doesn’t matter if the finger moves away from area C, but rather where the gesture starts.

The default


Assuming that A,B, and C above do not override the default event propagation behavior, the following is the event propagation process:

  • The DOWN event is passed to C’s onTouchEvent method, which returns false, meaning “I don’t care about this gesture.”

  • Therefore, the DOWN event is passed to B’s onTouchEvent method, which also returns false, indicating that B doesn’t care about the gesture either.

  • Also, because B doesn’t care about the gesture, the DOWN event is passed to A’s onTouchEvent method, which also returns false.

Since no view cares about the gesture, they will no longer receive any events from the “rest of the gesture.”

Handle events


Now, let’s assume that C actually cares about the gesture, either because C is made clickable or because you overwrite C’s onTouchEvent method.

  • The DOWN event is passed to C’s onTouchEvent method, which can do whatever it wants and returns true.

  • Because C says it is processing the gesture, the DOWN event will no longer be passed to B and A’s onTouchEvent methods.

  • Because C says it is processing the gesture, the “gesture remainder” event will also be passed to C’s onTouchEvent method, where it does not matter whether the method returns true or false, but it is best to return true for consistency.

Personal Understanding: From this we can see that the processing of the DOWN event by the onTouchEvent method of each View represents the willingness of the View to process the whole gesture starting from DOWN. Returning true means that the View is willing to process the gesture. Returning false indicates an unwillingness to process the gesture.

onInterceptTouchEvent


Now we’ll discuss a new method: onInterceptTouchEvent, which only exists in viewGroups, not in regular views. Before any view’s onTouchEvent is called, its ancestors will first have a chance to intercept the event, in other words, they can steal it. We left out this process in the “handling events” section, but now let’s add it:

  • The DOWN event is passed to A’s onInterceptTouchEvent method, which returns false, indicating that it does not want to intercept.

  • DOWN is passed to B’s onInterceptTouchEvent, which also doesn’t want to intercept, so this method returns false as well.

  • The DOWN event is now passed to C’s onTouchEvent method, which returns true because it wants to handle the gesture headed by the event.

  • Now, MOVE, the next event of the gesture, arrives. This MOVE event is again passed to A’s onInterceptTouchEvent method, which again returns false, as does B’s.

  • The MOVE event is then passed to C’s onTouchEvent, as in the previous section.

  • The other events in the “Gesture Remainder” are handled the same way as above, provided that the onInterceptTouchEvent methods of A and B continue to return false.

Two things to note here:

  • Although the onInterceptTouchEvent methods of ViewGroup A and B return false for DOWN events, subsequent events are still passed to their onInterceptTouchEvent methods, which differs from the behavior of onTouchEvent.

  • If the DOWN event is passed to C’s onTouchEvent method and it returns false, the DOWN event will continue to be passed up to B and A’s onTouchEvent, even though they say in the onInterceptTouchEvent method that they don’t want to intercept the DOWN event, but have no choice. No child View is willing to handle the event.

Personal Understanding: The DOWN event is the onInterceptTouchEvent from A->B, and the onTouchEvent from C->B->A. The DOWN event is the onTouchEvent from C->B->A. Returning true in either step of the method prevents it from propagating further.

Intercept events


Now, let’s go one step further and assume that B does not intercept the DOWN event, but it does intercept the subsequent MOVE event. B is a scrolling view. When the user taps only within its region, the element being tapped should be able to handle the click event. But after the user’s finger has moved a certain distance, the gesture can no longer be seen as a click — clearly, the user means scroll. That’s why B takes over the gesture. Here is the order in which events are processed:

  • DOWN events are passed in turn to A and B’s onInterceptTouchEvent methods, which both return false because they don’t want to intercept yet.

  • The DOWN event is passed to C’s onTouchEvent method, which returns true.

  • On subsequent MOVE events, A’s onInterceptTouchEvent method still returns false.

  • When B’s onInterceptTouchEvent method receives the MOVE event, B notices that the user’s finger movement exceeds a certain threshold (or SLOP). Therefore, B’s onInterceptTouchEvent method decides to return true, thus taking over the subsequent processing of the gesture.

  • The MOVE event will then be turned into a CANCEL event by the system, which will be passed to C’s onTouchEvent method.

  • Now, A MOVE event is passed to A’s onInterceptTouchEvent method. A still doesn’t care about this event, so the onInterceptTouchEvent method continues to return false.

  • The MOVE event is no longer passed to B’s onInterceptTouchEvent method, which returns true once and will never be called again. In fact, both the MOVE and the rest of the gesture are passed to B’s onTouchEvent method (unless A decides to intercept the rest of the gesture).

  • C no longer receives any events generated by this gesture.

Here are a few things that might surprise you:

  • If a ViewGroup intercepts the initial DOWN event, the event is still passed to the ViewGroup’s onTouchEvent method.

  • On the other hand, if a ViewGroup intercepts a halfway event (say, MOVE), the event will be turned into a CANCEL event by the system and passed to the child View that previously handled the gesture. And no longer passes (either an intercepted MOVE or a system-generated CANCEL) to the ViewGroup’s onTouchEvent method. Only incoming events are passed to the onTouchEvent method of the ViewGroup.

From there, you can go one step further. She has just ________-method RequestDisallowInterceptTouchEvent, C can use this method to prevent theft events. If you want to get a little crazy, you can override the dispatchTouchEvent method directly in your own ViewGroup and do whatever you want with the events that are passed in. But you may be breaking some agreements, so be careful.