I will n years ago about the Android Touch event distribution of the post from CSDN to nuggets, later here to write a blog, this article was written when the Android version is older, but the overall idea of the later version should remain unchanged, readers can refer to the Android source code to learn.

Androidxref.com/ Androidxref.com/ androidxref.com/ androidxref.com/ androidxref.com/ androidxref.com/


I don’t know if you have the following questions when it comes to Android touch screen events:

1. What is the difference between a View’s onTouchEvent() method returning true and false? The EXPLANATION given by the SDK is simple :” Returning true means the event has been handled, returning false means the opposite “, which does not explain the problem at all. 2.View’s onTouchEvent() method returns true for ACTION_DOWN and false for ACTION_MOVE. What does it mean to return super.onTouchEvent()?

3. What’s the difference between overwriting the onTouchEvent() method and setting up a touch listener via setOnTouchListener()? It looks very similar. 4. The View dispatchTouchEvent(),onTouchEvent(),setOnClickListener(), and ViewGroup onInterceptTouchEvent() methods are missing.

5. Suppose a ViewGroup has two child views, which view will handle the event if the two views overlap.

6. When to call onTouchEvent() and dispatchEvent() for the Activity and how to override it?

7. The most important question is: How do touch events pass from the top ViewGroup all the way down?

If you have a similar question, I believe this blog will give you the answer.

Second, the following points need to be made clear:

1.View is generally for the display of some content and it is also commonly used to handle user touch screen interaction events, and ViewGroup is as a View container exists, although in code it is a subclass of View, But it is usually used only as a container to organize the layout of its child views. By default, even the ViewGroup onDraw method is not called because the WILL_NOT_DRAW flag is set in the ViewGroup constructor.

2. We know that the View hierarchy in Android is a tree structure, and it needs to be clear that only a ViewGroup’s direct child View can be regarded as the son in the tree structure, and then the next layer does not count, similar to the parent-child relationship between processes. For example, FrameLayout has two subviews, LinearLayout and TextView, and the LinearLayout has three subviews, ImageView. Calling FrameLayout’s getChildCount() method will only return 2, not 5. So the term “child View” in the following is used to refer to a direct child View of a ViewGroup, either a View class or a ViewGroup class.

3. The topmost View of the Activity View is a DecorView, which is generated in the PhoneWindow class through the generateDecor() method and inherits from FrameLayout as the root View of the View hierarchy.

4. There are three main events for touch screens: Down, Move, and Up

So how does a touch event get passed up and down the View hierarchy? (This is only considered when the event has already arrived at the DecorView, in fact it is the ViewRootImpl class that receives the event from the underlying InputDispatch.), ViewRootImpl in deliverPointerEvent () method by calling the mView. DispatchPointerEvent (event); The DecorView passes the touch event to the DecorView, which passes it down to the subview via dispatchTouchEvent(), which calls its dispatchTouchEvent() method if the subview is also a ViewGroup, If the subview is a View, the onTouchEvent() method of the subview is called, and if the subview handles the event, event delivery is aborted. The whole process is like a recursive process, and the layer of understanding how a ViewGroup passes dispatchTouchEvent() to its child views understands the whole process.

Instead of analyzing the code for the ViewGroup dispatchTouchEvent() method, I’m going to give you my summary for those interested.

3. To summarize

The following scenario assumes that a ViewGroup has three child views, in index order v1,v2, and v3. V1 is also a ViewGroup,v2 and v3 are ordinary views, and they have a bit of overlap.

1.ViewGroup dispatchTouchEvent() dispatches events to its subviews, which are first sent to v3’s onTouchEvent method. If V3 does not handle the event, it continues to send events to V2. It will continue to distribute to v1, and since v1 is a ViewGroup, its dispatchTouchEvent() will be called to distribute to its child views.

2. V3 does not handle this event: onTouchEvent() returns false on the arrival of a Down event. If it returns true on the receipt of a Down event, the event was handled. So the idea is: as long as you’re willing to handle the Down event, then you have to handle the other events that follow.

3. V3 can only receive touch screen events if its display rectangle is within touch screen range, which is obvious, and v3 must be VISIBLE or animated, otherwise the event will be passed to V2. In fact, all subviews need to make this judgment.

4. If V1,v2, or V3 all decide not to handle touch events, then the event is ultimately handled by the ViewGroup itself, with its onTouchEvent() method called.

5. If an event is passed to v1, whether or not v1 handles the event depends on its subviews. If one of its subviews handles the event, then v1 handled the event. V1 is not handling events if all of its child views are not handling events and v1’s own onTouchEvent() method returns false when handling down events.

6. If a valid listener is set to the view via setOnTouchListener(), onTouchEvent() will only be called if the listener returns false.

At this point, the return value of onTouchEvent() should be clear. What is the return value of super.onTouchEvent()? If a view is not clickable or longClickable, super.onTouchEvent() returns false, otherwise onClick and onLongClick are processed and return true. Calls to setClickable(true) and setLongClickable(true) change the state of the view, as do calls to setOnClickListener() and setOnLongClickListener().

From the above conclusion, if two views are siblings and they have overlapping parts, clicking on the overlapping part will process the event first for the view with the higher subscript. If this view does not want to process the event, the other view will process the event first.

4. OnInterceptTouchEvent ()

A ViewGroup can call its onInterceptTouchEvent() method to intercept events from its child view. This method returns false by default, meaning no intercepting. If the onInterceptTouchEvent() returns true when it receives a Down event, then subsequent down,move, and Up events will be received by the ViewGroup’s own onTouchEvent() method, and none of its child views will receive the event. The ViewGroup onInterceptTouchEvent() method receives only Down events. Since the parent View handles both move and UP events, it doesn’t make sense for the method to receive only Down events. 1. There are no touch targets and this action is not an initial down,so this view group continues to intercept touches.

If a child view decides to handle all three events, the parent view’s onInterceptTouchEvent() method will be called each time the event arrives, If true is returned on an event, the event and subsequent events are intercepted for processing by the ViewGroup’s own onTouchEvent() method, and the subview receives the ACTION_CANCEL event.

For example, if the subview handles a Down event, but the ViewGroup blocks the move event when the move arrives, the subview will not receive subsequent moves and up events, and will receive ACTION_CANCEL events, while the ViewGroup will receive the move and up events. The onInterceptTouchEvent() method also receives only Down and Move events.

Intercepting methods can be very useful, such as ScrollView, which will first hand the down event to the subview, if it is a click event, to the subview, and if it detects that it is dragging the subview, it will intercept the move event, hand it over, and call overScrollBy() to produce the scroll. Of course, if you override the ViewGroup dispatchTouchEvent() method, you have control over the event distribution process.

5. The Activity onTouchEvent ()

In fact, the Activity’s dispatchTouchEvent is called first by the DecorView’s dispatchTouchEvent:

@Override public boolean dispatchTouchEvent(MotionEvent ev) { // Stylus events with side button pressed are filtered and  other // events are processed normally. if (mEnableGestures && MotionEvent.BUTTON_SECONDARY == ev.getButtonState()) { mStylusFilter.onTouchEvent(ev); return false; } final Callback cb = getCallback(); return cb ! = null && ! isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }Copy the code

Callback CB is actually an Activity reference that is set to the Window class mCallback member variable via mWindow.setCallback(this) when the attach method of the Activity is called. The PhoneWindow subclass getCallback() returns the Activity object associated with the Window. So the Activity’s dispatchTouchEvent() method is called first (ignoring other criteria for now). The Activity’s dispatchTouchEvent() method is simple:

/**
    * Called to process touch screen events.  You can override this to
    * intercept all touch screen events before they are dispatched to the
    * window.  Be sure to call this implementation for touch screen events
    * that should be handled normally.
    * 
    * @param ev The touch screen event.
    * 
    * @return boolean Return true if this event was consumed.
    */
   public boolean dispatchTouchEvent(MotionEvent ev) {
       if (ev.getAction() == MotionEvent.ACTION_DOWN) {
           onUserInteraction();
       }
       if (getWindow().superDispatchTouchEvent(ev)) {
           return true;
       }
       return onTouchEvent(ev);
   }
Copy the code

Its getWindow() actually refers to the PhoneWindow object and calls the DecorView’s superDispatchTouchEvent method, which distributes events at the View level. The Activity’s dispatchTouchEvent method returns true if any of the child views handled the event, otherwise the Activity’s onTouchEvent method is called. The comment states that you can override the Activity’s dispatchTouchEvent method to intercept all events before sending them.

6. MScrollX /mScrollY offset from touch screen coordinates

A view can scroll its contents through scrollBy or scrollTo. This scrolling only affects the display of the view and does not affect the rectangular box properties of the view. The position properties of mLeft and mTop remain unchanged, and the touch screen coordinates of the upper left corner of the view are still (0,0).

A ViewGroup can also scroll its contents through scrollBy or scrollTo. Since scrolling affects the display position of the subviews (but the position attributes of the subviews, such as mLeft and mTop, remain unchanged), touch screen coordinates need to be adjusted. Let the top left touch coordinate of the subview always be (0,0). This is done with the following code:

final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);

handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
Copy the code

This ensures that when the display position of the subview changes, the touch screen coordinates also change accordingly. This will affect the actual touch position of some of the ViewGroup’s computed subviews, for example, if you click on (x,y) of the ViewGroup, and you want to know if that point is in a subview, Check whether the point (x + getScrollX(), y + getScrollY()) is within the rectangle range of the subview. If so, the subview is also clicked and can handle the event.

Concluded, I believe that all the questions at the beginning of the answer, clear on the understanding.