A Comprehensive Summary of Android Event System + Practical Analysis

Keywords: Android Windows Java

Before that, I read a lot of related articles. After I have a general understanding, I will start to experience them. Before you start, you need to be clear about what the event distribution mechanism is about: the transfer rules of event sequences between ViewGroups/Views.
Attention should be paid to the following points:

  • It's about sequence of events, not individual events.
  • Consider at least one ViewGroup and one View
  • Delivery or distribution includes event passing from parent View to child View (event downloading process) and event passing from child View to parent View (event consuming process).

Give an example of why we should consider the above points. If an event is passed into a view without a child view, the onTouchEvent() of the view will be called back. We can decide whether to consume the event by rewriting the return value of onTouchEvent(). If the event is down, and onTouchEvent() returns false and does not consume it, then the events behind the event sequence are no longer distributed to the View. If downevent is consumed, subsequent events will continue to be distributed to the View. At this time, if not consuming a move event of the subsequent event, then the motion event will follow. Events will still be distributed to the View.
It can be seen that the consumption of an event in the event sequence will affect the distribution of subsequent events.
On the other hand, if the View does not consume down events, the onTouchEvent() of the parent View calls back layer by layer to pass down events to the parent View, so the consumption of events is also a process of passing events.

The above example is a fact that is verified by writing code. As for the reason, it will be analyzed in detail in this article. Here is just to illustrate the points that need to be considered above.

Although the system of events is very complex, there are rules to follow. The purpose of our practice is to find this rule. According to my understanding, I drew a picture as a summary of Android event mechanism:

From the diagram, we can see that the whole process is divided into four situations. The following four situations will be analyzed in detail through practice, including why such a division is made, how to classify and exclude other more cases, and so on.

Firstly, the following interface is implemented. The dispatch TouchEvent (), onIntercept TouchEvent (), onTouchEvent() of each layer of the interface View are added with the corresponding log and returned to the default super. This code can also be used: Sample code . In the diagram, we can think of Activity as the top-level parent View.

Then four situations in the Android event distribution flow chart are studied:

  1. By default, all returns to super. By default, no non-consumption events are intercepted.
  2. View's onTouchEvent() consumes down events, other defaults.
  3. ViewGroup2's onTouchEvent() consumes down events, other defaults.
  4. ViewGroup2's onInterceptTouchEvent() intercepts events after down load.

Consumer events refer to onTouchEvent() returning true, and interception events refer to onInterceptTouchEvent() returning true. It can be seen from four cases that the events after ACTION_DOWN and ACTION_DOWN of an event sequence are considered separately. After analyzing the source code, you will understand why to do so.

Next, I will start to analyze four cases with log. In order to facilitate viewing and describing, I will divide the log into three parts with blank lines. The first part is the down event, the second part is the move event, and the third part is the up event.

Scenario 1: By default, all returns to super. By default, no non-consumption events are intercepted.

( 1955): MainActivity->dispatchTouchEvent
( 1955): MyViewGroup1-->dispatchTouchEvent
( 1955): MyViewGroup1-->onInterceptTouchEvent
( 1955): MyViewGroup2--->dispatchTouchEvent
( 1955): MyViewGroup2--->onInterceptTouchEvent
( 1955): MyView--------------->dispatchTouchEvent    //down event download process ended
( 1955): MyView--------------->onTouchEvent    // down Event Consumption Process Begins
( 1955): MyViewGroup2--->onTouchEvent
( 1955): MyViewGroup1-->onTouchEvent
( 1955): MainActivity->onTouchEvent

( 1955): MainActivity->dispatchTouchEvent  //Moe1 event
( 1955): MainActivity->onTouchEvent
( 1955): MainActivity->dispatchTouchEvent //Moe2 event
( 1955): MainActivity->onTouchEvent

( 1955): MainActivity->dispatchTouchEvent   //up event
( 1955): MainActivity->onTouchEvent

I manually added a few notes to the log above. By default, Activity/ViewGroup/View does not intercept non-consumption events. It is easy to see from the log that down events go through a process of one up, a process of distribution below, and a process of consumption. If no one intercepts the download, the event will be passed down to the child View. If the child View does not consume the event, it will be passed to the parent View to consume, that is, to call back the onTouchEvent of the parent View in turn.
Then there are the move and up events. log is simple, because the down event sub-Views are not consumed, so the subsequent events in the event sequence are no longer issued, and the top-level Activity (DecorView) handles them by itself.
Why are subsequent events no longer sent to the child View? The answer is in the source code. Next, we begin to analyze the source code.

The following is the dispatchTouchEvent() method of ViewGroup, which retains only the key code, where the if statement is complete, and the code can be located in the full source code through the if statement. Number annotations are the downward process of ACTION_DOWN events, and note is the downward process of downward events.

public boolean dispatchTouchEvent(MotionEvent ev) {

    boolean handled = false;
    if (onFilterTouchEventForSecurity(ev)) {
        //1. First, if it is a down event, reset some states, including emptying mFirstTouchTarget
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }

        //2. Whether to intercept events or not, represented by Boolean variable intercepted
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                //Call onInterceptTouchEvent() here to see if events are intercepted
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action);
            } else {
                intercepted = false;
            }
        } else {//If mFirstTouchTarget is empty and the current event is not ACTION_DOWN, intercept the event
                //Note that intercepted is assigned directly here without calling the onInterceptTouchEvent() method
            intercepted = true;
        }

        // 3. After intercepted, if not intercepted, enter this if
        if (!canceled && !intercepted) {
            //4. If it is a down event, enter this if
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                //5. New TouchTarget is defined in this method. By default, it is empty. Enter this if.
                if (newTouchTarget == null && childrenCount != 0) {
                    //6. Loop to find sub-View s that handle events
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        //7. Note that the dispatchTransformedTouchEvent method in this if calls the dispatchTouchEvent method of the child View.
                        //  Calling the dispatchTouchEvent method of the child View passes the event to the child View for processing.
                        //  First, go through the dispatch TouchEvent logic in the View and return a Boolean value to indicate whether the event has been consumed.
                        //  If the dispatch TouchEvent of the child View returns true to indicate the consumption event of the child View, enter this if, otherwise no consumption will not enter this if.
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            //8. If the child View consumes events, assign a value to mFirstTouchTarget
                            //  The operation of assigning values to mFirstTouchTarget is in the addTouchTarget() method
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            break;
                        }
                    }
                }
            }
        }

        //In the if condition of number 7, the event has been sent down to the sub-View and the result returned by the sub-View has been obtained.
        //At this point, the logical end of the event by default

        //The following code for consuming events is omitted
    }

    //Finally, the result is returned, and the method ends
    return handled;
    //At this point, if the code No. 8 is not executed, that is, dispatch TouchEvent of the child View has no consumption event, then the value of mFirstTouchTarget is empty.
    //Considering the condition of No. 3, we can conclude that mFirstTouchTarget is not empty when ViewGroup does not intercept events and its sub-View consumption events, otherwise mFirstTouchTarget is empty.
}

The annotations in the code are very detailed, and ultimately a conclusion can be drawn: mFirstTouchTarget is not empty when ViewGroup does not intercept down events and its sub-View consumes down events, otherwise mFirstTouchTarget is empty.

Here's a new look at the source code with this conclusion, but this analysis is not about down events, but about events after down. Just look at the key parts. Part 2 of the code above is as follows:

/// 2. Whether to intercept events or not, represented by Boolean variable intercepted
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        //Call onInterceptTouchEvent() here to see if events are intercepted
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action);
    } else {
        intercepted = false;
    }
} else {//If mFirstTouchTarget is empty and the current event is not ACTION_DOWN, intercept the event
        //Note that intercepted is assigned directly here without calling the onInterceptTouchEvent() method
    intercepted = true;
}

First, the action Masked = MotionEvent. ACTION_DOWN in if is definitely not valid. Look at mFirstTouchTarget!= null. If mFirstTouchTarget is not empty, then the sub-View consumes the down event and executes to intercepted = onTouchEvent (evcept); this line of code. If mFirstTouchTarget is empty, the sub View does not consume down events, intercepted = true in direct else; intercept events. Then look at the default log:

( 1955): MainActivity->dispatchTouchEvent
( 1955): MyViewGroup1-->dispatchTouchEvent
( 1955): MyViewGroup1-->onInterceptTouchEvent
( 1955): MyViewGroup2--->dispatchTouchEvent
( 1955): MyViewGroup2--->onInterceptTouchEvent
( 1955): MyView--------------->dispatchTouchEvent    //down event download process ended
( 1955): MyView--------------->onTouchEvent    // down Event Consumption Process Begins
( 1955): MyViewGroup2--->onTouchEvent
( 1955): MyViewGroup1-->onTouchEvent
( 1955): MainActivity->onTouchEvent

( 1955): MainActivity->dispatchTouchEvent  //Moe1 event
( 1955): MainActivity->onTouchEvent
( 1955): MainActivity->dispatchTouchEvent //Moe2 event
( 1955): MainActivity->onTouchEvent

( 1955): MainActivity->dispatchTouchEvent   //up event
( 1955): MainActivity->onTouchEvent

The top-level ViewGroup here is MainActivity (DecorView). First, the down event is sent to the child View, then the child View does not consume it, and then the child View gives it to the parent View to consume one layer at a time. Finally, no one consumes it back to the MainActivity, and the down event ends. From the source code analysis above, we can see that mFirstTouchTarget is empty at this time. If the move event comes, then the else interception event in part 2 of the source code number will be executed directly, so the log of the subsequent event will not be issued as above (and there is no log of the onInterceptTouchEvent() method because it is not called). If the sub-View consumes down events, mFirstTouchTarget is not empty, and the process of subsequent events is similar to downs. Readers can modify MyView's onTouchEvent() consumes down events to try out the situation of downs.

For subsequent events, it is no more than intercepting or not intercepting, the decision is still in the code of part 2. The result of the decision is whether to enter if of number 3, and if not down event, jump out if of number 3 directly.

Extension: From the above analysis, we can see that the dispatchTouchEvent() method is called by the parent View, and the child View tells the parent View whether to consume events by the return value of this method, which is known to all. But consider an issue, Activity - > ViewGroup1 - > ViewGroup2 - > View. If events are sent in this order, and eventually View consumes down events, how does Activity know if View consumes events? The process must be like this.
View.dispatchTouchEvent() returns true - >.
ViewGroup2.dispatchTouchEvent() returns true - >.
ViewGroup1.dispatchTouchEvent() returns true - >.
Activity gets ViewGroup1.dispatchTouchEvent() and returns to true, which is the source code process analyzed above.

This process is just a guess. I didn't go into the source code, but I typed a log and the result was that a series of dispatch TouchEvent () did return true. That is to say, ViewGroup1 and ViewGroup2 do not have consumption events, but dispatch TouchEvent () returns true, so a statement on the Internet: dispatch TouchEvent () returns true is consumption events. This statement may not be entirely accurate, the specific need to go to the source code to find the answer, here is not analyzed, but to illustrate a problem: do not easily rewrite dispatch TouchEvent (). Even if overridden, try to ensure that the super method is called and the return value is consistent with the super result. In fact, the source code has provided two interfaces to override it indirectly, namely onInterceptTouchEvent() and onTouchEvent(), which can be known from the source code that they are ultimately called by dispatchTouchEvent(). And it's not a problem to use the return value of onTouchEvent() to describe whether or not the consumption event is consumed (the View without the consumption event can't call onTouchEvent()).

This is the end of the source code analysis of the log by default, and the default situation is understood. The latter three cases are simple.

Case 2: View's onTouchEvent() consumes down events, other defaults

(View is the smallest sub-View in the interface diagram, not ViewGroup1 or ViewGroup2)
First of all, from the default situation above, it is impossible to explain the problem that only a single event is studied for the event mechanism. We need to see how each event in the whole event sequence is handled. Therefore, the study of event consumption needs to consider the following situations:

Explain the figure above. For the first point in the figure, no download event is actually case 1 (default). As can be seen from the log of case 1, when no download event is consumed, subsequent events are no longer distributed to the View, so Branch 1 in the figure does not need to consider subsequent events for the View.
Then look at the 2,3,4,5 branches in the figure. From the previous Android event distribution flow chart, we can see that they can draw the same conclusion, so they can be seen as a situation.
So far, there are only two cases: case 1 and case 2. For 2,3,4,5 branches, draw a conclusion? Take Branch 5 as an example, and modify MyView's onTouchEvent() code as follows:

int x = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        x = 0;
        Log.d(TAG, "MyView--------------->onTouchEvent  x=" + x);
        return true;
    }
    x++;
    Log.d(TAG, "MyView--------------->onTouchEvent  x=" + x);
    if (x == 3) {
        return true;
    }
    if (x == 5) {
        return true;
    }
    return super.onTouchEvent(event);
}

The code is easy to understand. It implements the down event and the third event in the MyView consumption event sequence, the fifth event (counting from 0), and no other event is consumed. At the same time, for easy viewing, the value of x is printed out in the log. The logs are as follows (the logs for each event are separated by blank lines):

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent
( 1888): MyView--------------->dispatchTouchEvent   // End of the issuance process
( 1888): MyView--------------->onTouchEvent  x=0    //  Consumption process begins

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent
( 1888): MyView--------------->dispatchTouchEvent   // End of the issuance process
( 1888): MyView--------------->onTouchEvent  x=1    //  Consumption process begins
( 1888): MainActivity->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent
( 1888): MyView--------------->dispatchTouchEvent   // End of the issuance process
( 1888): MyView--------------->onTouchEvent  x=2    //  Consumption process begins
( 1888): MainActivity->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent
( 1888): MyView--------------->dispatchTouchEvent   // End of the issuance process
( 1888): MyView--------------->onTouchEvent  x=3    //  Consumption process begins

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent
( 1888): MyView--------------->dispatchTouchEvent   // End of the issuance process
( 1888): MyView--------------->onTouchEvent  x=4    //  Consumption process begins
( 1888): MainActivity->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent
( 1888): MyView--------------->dispatchTouchEvent   // End of the issuance process
( 1888): MyView--------------->onTouchEvent  x=5    //  Consumption process begins

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent
( 1888): MyView--------------->dispatchTouchEvent   // End of the issuance process
( 1888): MyView--------------->onTouchEvent  x=6    //  Consumption process begins
( 1888): MainActivity->onTouchEvent

There are seven events in the logs above. Although the logs are very long, they are very simple. They are mainly divided into two categories: MyView consumed and MyView not consumed. Among them, x=0,3,5 is consumed and the others are not consumed. Why onInterceptTouchEvent() is invoked every time is the same for all events in the download process log (as is the default download event), which is also clear through source code analysis. The law of the consumption process is also obvious. Events x=0,3,5 are consumed and gone. Other non-consumed events are passed directly to the top parent View rather than back one layer at a time.

Case 3: onTouchEvent() Consumption down Event of ViewGroup 2

First of all, from the log of case 1 (if you understand the default situation, there is no need to go back to the log at this time, the brain naturally formed). In order to appear the current situation 3, you must first let the onTouchEvent() of View not consume the downevent (if consumption is case 2, it has been analyzed), and at the same time, because of the onTouchEvent of View. HEvent () does not consume down events, so subsequent events are no longer passed to View, that is to say, there is nothing wrong with View, so the interface is equivalent to the following figure:

Look at the picture and the title and find out what happened in case 2. It's easy. Look at the log directly. Here's the onTouchEvent() consumption down event of ViewGroup 2. The log that is not consumed by subsequent events (equivalent to branch 2 of case 2, which can verify the conclusion of branch 5 above by the way):

( 2008): MainActivity->dispatchTouchEvent
( 2008): MyViewGroup1-->dispatchTouchEvent
( 2008): MyViewGroup1-->onInterceptTouchEvent
( 2008): MyViewGroup2--->dispatchTouchEvent
( 2008): MyViewGroup2--->onInterceptTouchEvent
( 2008): MyView--------------->dispatchTouchEvent
( 2008): MyView--------------->onTouchEvent
( 2008): MyViewGroup2--->onTouchEvent

( 2008): MainActivity->dispatchTouchEvent
( 2008): MyViewGroup1-->dispatchTouchEvent
( 2008): MyViewGroup1-->onInterceptTouchEvent
( 2008): MyViewGroup2--->dispatchTouchEvent
( 2008): MyViewGroup2--->onTouchEvent
( 2008): MainActivity->onTouchEvent

( 2008): MainActivity->dispatchTouchEvent
( 2008): MyViewGroup1-->dispatchTouchEvent
( 2008): MyViewGroup1-->onInterceptTouchEvent
( 2008): MyViewGroup2--->dispatchTouchEvent
( 2008): MyViewGroup2--->onTouchEvent
( 2008): MainActivity->onTouchEvent

( 2008): MainActivity->dispatchTouchEvent
( 2008): MyViewGroup1-->dispatchTouchEvent
( 2008): MyViewGroup1-->onInterceptTouchEvent
( 2008): MyViewGroup2--->dispatchTouchEvent
( 2008): MyViewGroup2--->onTouchEvent
( 2008): MainActivity->onTouchEvent

Note in the log that the down event experienced an onTouchEvent() method of MyView, and that the event after down no longer executes the onInterceptTouchEvent() method of ViewGroup2. The reason has been given in the source code analysis, and the conclusion has been drawn in the first two cases. There is nothing to explain.

From the above analysis, we can see that this situation can be counted as case 2, but we feel that it is more clear and reasonable to separate it out separately. When we analyze case 4, we will explain some reasons. At the same time, this is also an application of the first two situations. If we understand case 1 and case 2, many other situations that are not mentioned in this paper. They all make sense.

Case 4: onInterceptTouchEvent() of ViewGroup2 intercepts events after down

Previous studies are all about onTouchEvent() consumption-related situations. Here we study onInterceptTouchEvent() download interception.

First of all, look at the title carefully. Why not consider intercepting downs instead of just events after intercepting downs? Because after intercepting the down event, the event is handed over directly to its onTouchEvent(), and no longer through the sub-View, it becomes the case 3 interface (note that this is the case 3 interface instead of the case 3 interface, the internal process is different from the case 3, explained later). When it becomes the interface of case 3, it calls back to its onTouchEvent() and divides it into no consumption down and consumption down: no consumption down is case 1; consumption down is case 2.

Here's why "it's the interface that becomes case 3, not case 3", where the onInterceptTouchEvent() of ViewGroup 2 intercepts the down down and hands it directly to the onTouchEvent() of ViewGroup 2 for consumption. In case 3, without interception, the down down event first slipped through the sub-View, found that the sub-View did not consume it, and then passed it to the father ViewGroup 2 to consume.
It shows that there are two ways to consume ViewGroup2 to down events. This is also one reason for separating case 3 from case 2.

The above analysis shows that intercepting down will inevitably lead to one of the three preceding situations, so we will not consider intercepting down here. Now let's see what happens after intercepting down.
Modify the onInterceptTouchEvent() code of ViewGroup2 as follows:

int x = 0;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.d(TAG, "MyViewGroup2--->onInterceptTouchEvent x="+x);
    if (x==3) {
        x++;
        return true;
    }
    x++;
    return super.onInterceptTouchEvent(ev);
}

The code is very simple. When x==3, intercept the event, which is the fourth event in the sequence of intercepted events. The log is as follows:

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent x=0
( 1888): MyView--------------->dispatchTouchEvent
( 1888): MyView--------------->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent x=1
( 1888): MyView--------------->dispatchTouchEvent
( 1888): MyView--------------->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent x=2
( 1888): MyView--------------->dispatchTouchEvent
( 1888): MyView--------------->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onInterceptTouchEvent x=3
( 1888): MyView--------------->dispatchTouchEvent
( 1888): MyView--------------->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onTouchEvent
( 1888): MainActivity->onTouchEvent

( 1888): MainActivity->dispatchTouchEvent
( 1888): MyViewGroup1-->dispatchTouchEvent
( 1888): MyViewGroup1-->onInterceptTouchEvent
( 1888): MyViewGroup2--->dispatchTouchEvent
( 1888): MyViewGroup2--->onTouchEvent
( 1888): MainActivity->onTouchEvent

log needs to be analyzed in three places:

1. The event before x = 3, well understood, is case 2.

2. Events at x=3, and ViewGroup 2 at x=3 have intercepted events. Why did MyView receive an event? If you print out the action value of the event MyView receives with log, it is found to be ACTION_CANCEL, which indicates that the event ended for non-human reasons. For the ACTION_CANCEL event, let me give you an example. Generally, when we deal with the event, we record something in the down event, and then clear the record in the up event. If there is a Button in the ListView, when we hold down the Button, the button receives the down event, then the finger starts to slide, and the event will be intercepted and processed by the ListView. At this time, the button will no longer receive the up event, but when the ListView intercepts the event, the button will receive the cancel event to indicate the non-human reason conclusion. So we can let cancel do what we need to do in the up event.

3. For events after x = 3, only onInterceptTouchEvent() is changed in the code, and onTouchEvent() is returned by default. That is to say, ViewGroup2 intercepts events without consumption. As can be seen from the log, each event returns Activity. However, why does ViewGroup2 not consume events? Why do subsequent events continue to be distributed to ViewGroup2 and executed to its onTouchEvent() method? (It seems to violate the rule of default). This is a problem and one that needs to be noticed.
First, why are events distributed to ViewGroup 2? In fact, source code analysis has explained that when a child View consumes the down event, mFirstTouchTarget is no longer empty, and subsequent events are sent to the child View (recursion), unless the parent View's onInterceptTouchEvent() actively intercepts the event, whereas the parent ViewGroup2's ViewViewViewViewView and Acceptance in the example do not. Interception of events.
Then why is it always called to the onTouchEvent() method of ViewGroup2 because ViewGroup2 does not handle the child View of the event, so it calls super.dispatchTouchEvent(), the parent class View.dispatchTouchEvent() method, so if onTouchListener is not set, onTouchEvent will inevitably be called. ChEvent ().

Concluding remarks

This is the end of the article. Then look at the Android Event Distribution Flow Chart, which is drawn according to the above four situations. If you understand the four situations (at least to give a way to intercept consumption, you can think of what the log s of each event are like), then it should be easy to understand this chart.

As for the conclusion, there is no pure text description (even if the description is very complex, a bunch of if). 11 conclusions are given in Android Exploration of the Art of Development. Interested ones can take a look. However, there are some literal descriptions in the figure above, which can be regarded as conclusions, mainly the two parts corresponding to the green arrows, describing the final direction of each event. The arrows of each branch in the figure are the conditions under various circumstances.

Finally, there are some situations or inappropriate places in the article that are not taken into account. Welcome to leave a message for discussion. In addition, for the source code, only the analysis of the download process is given. Consumption process will no longer give a detailed analysis, to provide a train of thought, the source code analysis in this article has a line of comments // below the consumer event-related code, omitted, readers can start looking at the consumer-related code here, note that this is the dispatch TouchEvent () in the ViewGroup class, this line of comments will have the relevant logic below. Call the dispatchTransformed TouchEvent () method (the download process is also called to this method), which will have code like super.dispatchTouchEvent(event) calling dispatchTouchEvent() in the parent View of the ViewGroup class, and then look at dispatchTouchEvent() in the View, and you will find onTouchEvent. () Called, and you will find other things, such as onTouchListener() and onTouchEvent() priorities.
To explain dispatch Transformed TouchEvent (), there are two kinds of code: super. dispatch TouchEvent (event) and child. dispatch TouchEvent (event). Simply understand, the former is to call the onTouchEvent() of ViewGroup itself to consume, and the latter is to distribute events to the sub-View (on the interface) to process. If the child View is a ViewGroup, then the process of processing events for the child View is similar to the parent View, starting with the dispatch TouchEvent () of the ViewGroup; if the child View is not a ViewGroup, then the dispatch TouchEvent () logic of the View is executed directly. The dispatch TouchEvent () of View does not pass events to the parent View to consume. In fact, the consumption process event is not passed, but based on the return value of dispatch TouchEvent () of the child View and some records of the ViewGroup itself to decide whether to call super. dispatch TouchEvent (event) to call its own onTouchEvent(). Well, here's the idea.

Explain a question:

Question: In case 2, the last paragraph in the analysis reads, "The law of consumption process is also obvious. Events x=0,3,5 will be consumed, and other non-consumed events will be passed directly to the top parent View rather than back one layer at a time." Why is this?

First look at the dispatch TouchEvent () source code in Activity

public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

It is not difficult to see whether the onTouchEvent() of activity can be called depends on the if condition getWindow (). superDispatch TouchEvent (ev).
From the previous source code analysis, you can see that the dispatchTouchEvent() method is called by the parent View, and the child View tells the parent View whether the consumption event is consumed by the return value of the method (see the source code analysis number 7, and the extension at the end of case 1).
So in this problem, for non-consuming events, the dispatchTouchEvent() return value for each layer of View/ViewGroup is as follows:
MyView.dispatchTouchEvent() returns false - >.
ViewGroup2.dispatchTouchEvent() returns false - >.
ViewGroup1.dispatchTouchEvent() returns false - >.
......
Finally, the root View, DecorView.dispatchTouchEvent(), returns false

Because each View does not consume events, their onTouchEvent() is not called, and the specific code is not analyzed.

Then look at the dispatch TouchEvent () source code of Activity, getWindow (). superDispatch TouchEvent (ev), which is the method of abstract class of Windows. Everyone knows that the implementation class of Windows is PhoneWindow, so look directly at the super Dispatch TouchEvent () method of PhoneWindow:

public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

ctrl + left-click jump to DecorView.java's superDispatch TouchEvent () method

public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

ctrl + left-click jump is the dispatchTouchEvent() method of ViewGroup. As mentioned above, for non-consuming events, this series of methods all return false, so in the dispatchTouchEvent() method of Event Activity, if(getWindow().superDispatchTouchEvent(ev)) condition is false, and return on TouchEvent (ev) is executed.

Last

If you think the article is well written, give it a compliment? If you think it's worth improving, please leave me a message. We will inquire carefully and correct the shortcomings. Thank you.

I hope you can forward, share and pay attention to me, and update the technology dry goods in the future. Thank you for your support! ___________

Forwarding + Praise + Concern, First Time to Acquire the Latest Knowledge Points

Android architects have a long way to go. Let's work together.

The following wall cracks recommend reading!!!

Finally, I wish you all a happy life.~

Posted by sineadyd on Thu, 08 Aug 2019 03:20:56 -0700