preface

In the previous article we talked about how Android passes click events from an Activity to a ViewGroup and its two important methods dispatchTouchEvent and the timing and logic of onTouchEvent calls. That chapter goes on to explain how click events are passed in a ViewGroup.

The body of the

From the idea of the previous Activity, the transmission idea of ViewGroup is also the chain of responsibility mode, layer by layer to pass down, different from the Activity is completed by three methods.

In the process

I believe Android developers are familiar with this part of the content, so I directly draw a flow diagram to illustrate:

In this case, when the ViewGroup handles the event itself, it is the event distribution process of the View, because it is very simple, the parent class of the ViewGroup is the View, so we can call the super.DispatchTouchEvent method directly. We’ll talk about this later.

DispatchTouchEvent internal logic

There is a lot of code for this method, so let’s take the main one:

// Intercept the flag bit
final boolean intercepted;
 // Only in these two cases should we consider whether to intercept the event
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget ! =null) {
        // More on that laterfinal boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) ! =0;
    if(! disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action);// restore action in case it was changed
    } else {
        intercepted = false; }}else {
    intercepted = true;
}
Copy the code

The crucial point to note here is that the main logic to intercept is determined only if the event type is ACTION_DOWN or mFirstTouchTarget is not null.

So when an ACTION_DOWN event is received, it is determined whether to intercept it regardless of the case.

So what’s the other judgment condition?

mFirstTouchTarget ! = null

Here is a more convoluted understanding, mainly to achieve a function.

When the event is successfully processed by a child element of the ViewGroup, mFirstTouchTarget is assigned and points to the child element. The flip side is that mFirstTouchTarget is null when ViewGroup intercepts the event, so why do this?

Let’s say I have A swipe operation, and I swipe on GroupView A’s View B, as follows:

If View B is designed to be unclickable and unable to slide, it requires that the click event be blocked when it is passed to GroupView A. Here A series of events can be summarized as follows:

DOWN -> MOVE …. MOVE -> UP this event stream,

So look at the flow chart below:

When an onInterceptTouchEvent intercepts an onInterceptTouchEvent, it processes all MOVE and UP events. Since mFirstTarget is null, the onInterceptTouchEvent will not be called again.

When a ViewGroup intercepts a DOWN event, the entire event stream will be handled by the ViewGroup. It will not be distributed further, and the onInterceptTouchEvent of the ViewGroup will not be called. This is crucial, and more on this later.

If the DOWN event is not intercepted by the ViewGroup onInterceptTouchEvent, it returns false. If the MOVE and UP events are not intercepted by the ViewGroup onInterceptTouchEvent, it returns false. It is possible to call the onInterceptTouchEvent event of the ViewGroup again, but how and when will be discussed.

DisallowIntercept flags

The above code actually follows the general logic, but there are some problems that the above code can’t handle, such as the following example:

Here GroupView A is A vertical scrolling View, and View B is A vertical scrolling View, so View B can be used as RecyclerView. At this moment, I slide UP B, then the content is shifted:

When it reaches the bottom (item 12 is the last one) and then slides up, the entire View B should slide up as follows:

The MOVE event is then intercepted by ViewGroup A.

Sliding conflict

There’s a sliding conflict involved here, but what I want to say is that when you look at a problem you have to look at the nature of it.

The problem here, for example, we can solve if we follow the previous flow chart, which is what we call external interception.

External interception

  • First, when the DOWN event is transmitted to A, it cannot be intercepted at this time. As mentioned above, if the DOWN event is intercepted by A, all subsequent event streams will be processed by A.
  • Then, when the MOVE event is passed to A, according to the previous code flow, mFirstTouchTarget is not empty at this time, it will enter the judgment, then determine whether B slides to the top or bottom, if it slides to the top or top, THEN A will intercept the MOVE event, A will process, otherwise it will not intercept. The MOVE event is passed to B, which continues processing.

The external interception method here is easier to understand, but the point is that you cannot intercept DOWN and obtain the state of B, so there is A limitation here, that is, the event distribution of A must be handled, and the event distribution of B must be handled, which is A bit troublesome.

Internal interception method

Is there A more convenient way? For example, A is A vertical ScrollView, B is my custom View, I just need to modify B to achieve this effect, of course, this is internal interception.

The key to internal interception is the disallowIntercept flag, so let’s take a look at what this flag bit is.

/ / the flag ViewGroup
protected int mGroupFlags;
Copy the code

It is usually assigned by its child View:

// Usually called by subclasses
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    //true -> FLAG_DISALLOW_INTERCEPT
    if (disallowIntercept) {
        mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
    } else {
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    }
    // recursive call
    if(mParent ! =null) { mParent.requestDisallowInterceptTouchEvent(disallowIntercept); }}Copy the code

Then there’s where it’s used, in the dispatchTouchEvent method we talked about earlier:

//dispatchTouchEvent key logic code
final boolean intercepted;
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget ! =null) {
    // This method is falsefinal boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) ! =0;
    if(! disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action);// restore action in case it was changed
    } else {
        / / when the call requestDisallowInterceptTouchEvent is false
        intercepted = false; }}else {
    // There are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
}
Copy the code

There are many different names for this flag, but my favorite is whether to disable event blocking. The default is false, meaning disabled.

  • When set to false requestDisallowInterceptTouchEvent method, also is unable to restrain to intercept with events function, then DOWN, MOVE event will take place in the ViewGroup whether to intercept, For example, ViewGroup A will block vertical MOVE events by default, or if event blocking is disabled, it will block MOVE events.

  • When the requestDisallowInterceptTouchEvent method is set to true, the disable event interception function, then whatever the event is not to intercept, passed to his son the View.

Summary of internal interception

So it’s important that the flag bit disallowIntercept disables event interception. Here’s a quick summary:

  1. When you customize a View and cannot modify the event blocking code that wraps the View, use internal blocking.

  2. For a DOWN event, its wrapped View cannot be intercepted. As mentioned above, if the DOWN event is intercepted, the back View will never get the rest of the event stream.

  3. For MOVE events, disable event blocking for wrapped Views when you need to handle them yourself. You can’t help wrapping the View’s event blocking functionality when you don’t need to handle it yourself.

Out to deal with

Of course, just like the previous Activity, when the child View of the ViewGroup fails to consume the event, or when it clicks on a blank area of the ViewGroup, there is a backpocket policy that calls the super.dispatchTouchEvent method, That’s the event distribution mechanism of the View, and the onTouchEvent method that we haven’t talked about so far, we’ll put in the View distribution event.

conclusion

Most of this article is familiar from Android development, but I’ll summarize a few points:

  • According to the mFirstTouchTarget logic, when a ViewGroup intercepts a DOWN event, it processes the subsequent stream of events.

  • The onInterceptTouchEvent method of the ViewGroup does not intercept DOWN events by default. It intercepts MOVE events as required.

  • When you cannot modify the event blocking function of the wrapped View, you can enable the internal interception method by disabling the event blocking function of the ViewGroup, so that the ViewGroup can distribute the event according to the child View.

  • When a child View of a ViewGroup does not consume an event or click on a blank space, the parent View handles it itself.