Advanced Android [2] ten minutes to thoroughly understand the View event distribution mechanism

Posted by spider22 on Sat, 06 Nov 2021 14:34:55 +0100

preface

  • Android event distribution mechanism is the foundation that Android developers must understand
  • There are a large number of articles on Android event distribution mechanism on the Internet, but there are some problems: incomplete content, unclear ideas, no source code analysis, complex simple problems and so on
  • Today, I will comprehensively summarize the event distribution mechanism of Android. I can guarantee that it is the most comprehensive, clear and understandable on the market

  1. This paper adheres to the principle of "conclusion first and detailed analysis later", that is, let everyone know sensibly first, and then understand the problem through rational analysis;
  2. Therefore, please remember the conclusion first, and then continue to look at the analysis;

  • The article is long and it takes a long time to read. It is recommended to collect and wait for sufficient time before reading

catalogue

1. Basic cognition

1.1 what is the "event" of event distribution?

A: click event (Touch event). Details are as follows:

Special note: the event column refers to a series of events generated from the process from the finger touching the screen to the finger leaving the screen. Generally, the event column starts with the DOWN event and ends with the UP event, with countless MOVE events in the middle.

1.2 essence of event distribution

A: the whole process of transferring a MotionEvent to a specific view & processing

That is, the process of event delivery = distribution process.

1.3 what objects are events transferred between?

A: Activity, ViewGroup and View. The UI interface of Android consists of Activity, ViewGroup, View and their derived classes

1.4 sequence of event distribution

That is, the sequence of event delivery: activity - > ViewGroup - > view

That is, after a click event occurs, the event is first transferred to Activity, then to ViewGroup, and finally to View

1.5 what methods are used to coordinate the event distribution process?

Answer: dispatchTouchEvent(), onInterceptTouchEvent(), and onTouchEvent()

These three methods are described in detail below

1.6 summary

  • So far, I believe you have a perceptual understanding of Android event distribution
  • Next, I will introduce the Android event distribution mechanism in detail
    • *

2. Overview of event distribution mechanism process

Android event distribution process = activity - > ViewGroup - > View

That is, after a click event occurs, the event is first transferred to Activity, then to ViewGroup, and finally to View

That is, to fully understand the Android distribution mechanism, it is essential to understand:

  1. Activity distribution mechanism for click events
  2. ViewGroup's distribution mechanism for click events
  3. View distribution mechanism for click events

Next, I will comprehensively analyze the event distribution mechanism through the source code

That is, explain in sequence: Activity event distribution mechanism, ViewGroup event distribution mechanism and View event distribution mechanism

3. Detailed analysis of event distribution mechanism process

It mainly includes: Activity event distribution mechanism, ViewGroup event distribution mechanism and View event distribution mechanism

Process 1: event distribution mechanism of Activity

The Android event distribution mechanism will first pass the click event to the Activity, specifically execute dispatchTouchEvent() to distribute the event.

Source code analysis

/**
  * Source code analysis: Activity.dispatchTouchEvent ()
  */ 
  public boolean dispatchTouchEvent(MotionEvent ev) {

    // Post only core code

    // ->>Analysis 1
    if (getWindow().superDispatchTouchEvent(ev)) {

        return true;
        // If getWindow().superDispatchTouchEvent(ev) returns true
        // Then Activity.dispatchTouchEvent() returns true, and the method ends. That is, the click event stops being delivered down & the event delivery process ends
        // Otherwise: continue to call Activity.onTouchEvent

    }
    // ->>Analysis 3
    return onTouchEvent(ev);
  }

/**
  * Analysis 1: getWindow().superDispatchTouchEvent(ev)
  * explain:
  *     a. getWindow() = Gets the object of the Window class
  *     b. Window Class is an abstract class whose only implementation class = PhoneWindow class
  *     c. Window Class's superDispatchTouchEvent() = 1 abstract method, which is implemented by the subclass PhoneWindow class
  */
  @Override
  public boolean superDispatchTouchEvent(MotionEvent event) {

      return mDecor.superDispatchTouchEvent(event);
      // mDecor = instance object of the top-level View (DecorView)
      // ->>Analysis 2
  }

/**
  * Analysis 2: mDecor.superDispatchTouchEvent(event)
  * Definition: belongs to the top-level View (DecorView)
  * explain:
  *     a. DecorView Class is an internal class of the PhoneWindow class
  *     b. DecorView Inheriting from FrameLayout, it is the parent class of all interfaces
  *     c. FrameLayout Is a subclass of ViewGroup, so the indirect parent of DecorView = ViewGroup
  */
  public boolean superDispatchTouchEvent(MotionEvent event) {

      return super.dispatchTouchEvent(event);
      // Calling method of parent class = dispatchTouchEvent() of ViewGroup
      // Pass events to ViewGroup for processing. For details, see the event distribution mechanism of ViewGroup analyzed in subsequent chapters

  }
  // Back to the original analysis 2

/**
  * Analysis 3: Activity.onTouchEvent()
  * Call scenario: this method will be called when a click event is not received / processed by any View under the Activity
  */
  public boolean onTouchEvent(MotionEvent event) {

        // ->>Analysis 5
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
        // That is, true is returned only when the click event is outside the Window boundary. Generally, false is returned. The analysis is completed
    }

/**
  * Analysis 4: mWindow.shouldCloseOnTouch(this, event)
  * Function: it is mainly used to judge whether the click event outside the boundary is a DOWN event, whether the coordinates of the event are within the boundary, etc
  */
  public boolean shouldCloseOnTouch(Context context, MotionEvent event) {

  if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
          && isOutOfBounds(context, event) && peekDecorView() != null) {

        // Return true: indicates that the event is outside the boundary, that is, the consumption event
        return true;
    }

    // Return false: within the boundary, i.e. not consumed (default)
    return false;
  } 

Source code summary

When a click event occurs, it starts from the event distribution of Activity (Activity.dispatchTouchEvent()), and the process is summarized as follows:

Summary of core methods

It mainly includes: dispatchTouchEvent() and onTouchEvent(), which are summarized as follows

When does the dispatchTouchEvent() of ViewGroup return true / false? Please continue to look at the distribution mechanism of ViewGroup events

Process 2: event distribution mechanism of ViewGroup

It can be seen from the event distribution mechanism of Activity above that the event is transferred from Activity - > ViewGroup in Activity.dispatchTouchEvent(), and the event distribution mechanism of ViewGroup starts from dispatchTouchEvent().

Source code analysis

/**
  * Source code analysis: ViewGroup.dispatchTouchEvent ()
  */ 
  public boolean dispatchTouchEvent(MotionEvent ev) { 

  // Post only key codes
  ... 

  if (disallowIntercept || !onInterceptTouchEvent(ev)) {  
  // Analysis 1: each time the ViewGroup distributes an event, it needs to call onInterceptTouchEvent() to ask whether to intercept the event
    // Judgment value 1-disallowIntercept: whether to disable the function of event interception (false by default), which can be modified by calling requestDisallowInterceptTouchEvent()
    // Judgment value 2-!onInterceptTouchEvent(ev): negates the return value of onInterceptTouchEvent()
        // a. If false is returned in onInterceptTouchEvent(), that is, the event is not intercepted, so it enters the internal of condition judgment
        // b. If true is returned in onInterceptTouchEvent(), the event is intercepted, thus jumping out of the conditional judgment
        // c. About onintercepttouchevent() - > > analysis 1

  // Analysis 2
    // 1 \. Through the for loop, traverse all child views under the current View Group
    for (int i = count - 1; i >= 0; i--) {  
        final View child = children[i];  
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  
                || child.getAnimation() != null) {  
            child.getHitRect(frame);  

            // 2 \. Judge whether the currently traversed View is the View being clicked, so as to find the currently clicked View
            if (frame.contains(scrolledXInt, scrolledYInt)) {  
                final float xc = scrolledXFloat - child.mLeft;  
                final float yc = scrolledYFloat - child.mTop;  
                ev.setLocation(xc, yc);  
                child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  

                // 3 \. The internal of condition judgment calls the dispatchTouchEvent() of the View
                // That is, it realizes the transmission of click events from ViewGroup to child views (see the View event distribution mechanism described in the following chapter for details)
                if (child.dispatchTouchEvent(ev))  { 

                // There is a return value after calling the dispatchTouchEvent of the child View
                // If the control can be clicked, the return value of dispatchTouchEvent must be true when clicked, so the condition judgment will be true
                // So the dispatchTouchEvent() given to ViewGroup directly returns true, that is, it jumps out directly
                // That is, the sub View consumes the click events of ViewGroup

                mMotionTarget = child;  
                return true; 
                      }  
                  }  
              }  
          }  
      }  
    }  

  ...

  return super.dispatchTouchEvent(ev);
  // If no View receives the event (for example, click the blank space) / the ViewGroup itself intercepts the event (the onInterceptTouchEvent() is copied and returns true)
  // dispatchTouchEvent() of the parent class of ViewGroup will be called, that is, View.dispatchTouchEvent()
  // Therefore, the ViewGroup's ontouch() - > ontouchevent() - > performclick() - > onclick() will be executed, that is, the event will be handled by itself, and the event will not be passed down
  // Please refer to View.dispatchTouchEvent() in the View event distribution mechanism for details

  ... 

}

/**
  * Analysis 1: ViewGroup.onInterceptTouchEvent()
  * Function: whether to intercept events
  * explain:
  *     a. Return false: do not intercept (default)
  *     b. Return true: intercept, that is, the event stops being delivered downward (onInterceptTouchEvent() needs to be manually copied, which returns true)
  */
  public boolean onInterceptTouchEvent(MotionEvent ev) {  

    // No interception by default
    return false;

  } 
  // Go back to where you called it

Source code summary

After Android event distribution is delivered to activity, it is always delivered to ViewGroup first and then to View. The process is summarized as follows: (it is assumed that the activity event has been distributed and passed to the ViewGroup)

Summary of core methods

It mainly includes: dispatchTouchEvent(), onTouchEvent(), onInterceptTouchEvent(), which is summarized as follows

Case analysis

1. Layout description

2. Test code

Layout files: activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:focusableInTouchMode="true"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 1" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button 2" />

</LinearLayout>

Core code: MainActivity.java

public class MainActivity extends AppCompatActivity {

  Button button1,button2;
  ViewGroup myLayout;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      button1 = (Button)findViewById(R.id.button1);
      button2 = (Button)findViewById(R.id.button2);
      myLayout = (LinearLayout)findViewById(R.id.my_layout);

      // 1. Set listening events for ViewGroup layout
      myLayout.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              Log.d("TAG", "Click ViewGroup");
          }
      });

      // 2 \. Set listening events for button 1
      button1.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              Log.d("TAG", "Click button1");
          }
      });

      // 3 \. Set listening events for button 2
      button2.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              Log.d("TAG", "Click button2");
          }
      });
   }
}

3. Test results

// Click button 1 and the output is as follows
 Click button1

// Click button 2 and the output is as follows
 Click button2

// Click the blank space and the output is as follows
 Click ViewGroup

4. Result analysis

  • When you click a Button, because the ViewGroup does not intercept by default, the event will be passed to the child View Button, and then execute Button.onClick().
  • At this time, ViewGroup. dispatchTouchEvent() will directly return true, so the ViewGroup itself will not process the event, so the dispatchTouchEvent() of ViewGroupLayout will not be executed, so the registered onTouch() will not be executed, that is, the entire link of ontouchevent() - > performclick() - > onClick() will not be executed, so the onClick() set by ViewGroup will not be executed finally.
  • When clicking a blank area, ViewGroup. dispatchTouchEvent() traverses all child views, hoping that the clicked child View cannot be found, so ViewGroup itself will handle the event, so execute ontouchevent() - > performclick() - > onClick(), and finally execute onClick() set by ViewGroupLayout
    • *

Process 3: event distribution mechanism of View

As you can see from the ViewGroup event distribution mechanism above, the View event distribution mechanism starts from dispatchTouchEvent()

Source code analysis

/**
  * Source code analysis: View.dispatchTouchEvent ()
  */
  public boolean dispatchTouchEvent(MotionEvent event) {  

        if ( (mViewFlags & ENABLED_MASK) == ENABLED && 
              mOnTouchListener != null &&  
              mOnTouchListener.onTouch(this, event)) {  
            return true;  
        } 

        return onTouchEvent(event);  
  }
  // Note: dispatchTouchEvent() returns true only if the following three conditions are true; Otherwise, execute onTouchEvent()
  //   1\. (mViewFlags & ENABLED_MASK) == ENABLED
  //   2\. mOnTouchListener != null
  //   3\. mOnTouchListener.onTouch(this, event)
  // These three conditions are analyzed one by one

/**
  * Condition 1: (mviewflags & enabled_mask) = = enabled
  * explain:
  *    1\. This condition determines whether the currently clicked control is enable d
  *    2\. Since many views are enable d by default, this condition is always true (unless manually set to false)
  */

/**
  * Condition 2: mitouchlistener= null
  * explain:
  *   1\. mOnTouchListener The variable is assigned in View.setOnTouchListener()
  *   2\. That is, as long as the Touch event is registered for the control, the mnontouchlistener must be assigned a value (that is, it is not empty)
  */
  public void setOnTouchListener(OnTouchListener l) { 

    mOnTouchListener = l;  

} 

/**
  * Condition 3: mOnTouchListener.onTouch(this, event)
  * explain:
  *   1\. That is, onTouch() when the callback control registers Touch events;
  *   2\. Manual replication settings are required, as follows (take Button as an example)
  */
  button.setOnTouchListener(new OnTouchListener() {  
      @Override  
      public boolean onTouch(View v, MotionEvent event) {  

        return false;  
        // If you return true in onTouch(), all the above three conditions will be true, so that View.dispatchTouchEvent() directly returns true and the event distribution ends
        // If false is returned in onTouch(), all the above three conditions will not be true, which will cause if to jump out of View.dispatchTouchEvent() and execute onTouchEvent(event)
        // onTouchEvent() source code analysis - > analysis 1
      }  
  });

/**
  * Analysis 1: onTouchEvent()
  */
  public boolean onTouchEvent(MotionEvent event) {  

    ... // Show only key codes

    // If the control can be clicked, it will enter switch judgment
    if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  

        // Judge and handle according to the current event type
        switch (event.getAction()) { 

            // a. Event type = primary analysis
            case MotionEvent.ACTION_UP:  
                    performClick(); 
                    // ->>Analysis 2
                    break;  

            // b. Event type = press View
            case MotionEvent.ACTION_DOWN:  
                postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());  
                break;  

            // c. Event type = end event
            case MotionEvent.ACTION_CANCEL:  
                refreshDrawableState();  
                removeTapCallback();  
                break;

            // d. Event type = sliding View
            case MotionEvent.ACTION_MOVE:  
                final int x = (int) event.getX();  
                final int y = (int) event.getY();  

                int slop = mTouchSlop;  
                if ((x < 0 - slop) || (x >= getWidth() + slop) ||  
                        (y < 0 - slop) || (y >= getHeight() + slop)) {  
                    removeTapCallback();  
                    if ((mPrivateFlags & PRESSED) != 0) {  
                        removeLongPressCallback();  
                        mPrivateFlags &= ~PRESSED;  
                        refreshDrawableState();  
                    }  
                }  
                break;  
        }  

        // If the control can be clicked, it must return true
        return true;  
    }  
  // If the control is not clickable, it must return false
  return false;  
}

/**
  * Analysis 2: performClick()
  */  
  public boolean performClick() {  

      if (mOnClickListener != null) {
          // Just register a click event for the View control through setOnClickListener()
          // The moninclicklistener variable is assigned a value (i.e. it is not empty)
          // Then the callback onclick() & performclick() will return true
          playSoundEffect(SoundEffectConstants.CLICK);  
          mOnClickListener.onClick(this);  
          return true;  
      }  
      return false;  
  }  

Source code summary

It should be noted that onTouch() is executed before onClick()

Summary of core methods

It mainly includes: dispatchTouchEvent(), onTouchEvent()

Case analysis

In this example, two situations will be analyzed:

  1. Register Touch event listener and return false in onTouch()
  2. Register Touch event listener and return true in onTouch()

Analysis 1: register Touch event listening and return false in onTouch()

Code example
// 1 \. Register the Touch event listener setOnTouchListener and return false in onTouch()
button.setOnTouchListener(new View.OnTouchListener() {

      @Override
      public boolean onTouch(View v, MotionEvent event) {
          System.out.println("Yes onTouch(), Action is:" + event.getAction());
          return false;
      }
});

// 2 \. Register click event OnClickListener()
button.setOnClickListener(new View.OnClickListener() {

      @Override
      public void onClick(View v) {
          System.out.println("Yes onClick()");
      }
});
test result
Yes onTouch(), Action is:0
 Yes onTouch(), Action is:1
 Yes onClick()
Description of test results
  • Clicking the button will generate two types of events - press View and lift View, so it will call back onTouch() twice;
  • Because onTouch() returns false, the event is not consumed and will continue to pass down, that is, call View.onTouchEvent();
  • When calling View.onTouchEvent(), onClick() will be called back because the click event is set when calling performClick().

Analysis 2: register Touch event listening and return true in onTouch()

Code example
// 1 \. Register the Touch event listener setOnTouchListener and return false in onTouch()
button.setOnTouchListener(new View.OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Yes onTouch(), Action is:" + event.getAction());
            return true;
        }
    });

// 2 \. Register click event OnClickListener()
button.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            System.out.println("Yes onClick()");
        }

  });
test result
Yes onTouch(), Action is:0
 Yes onTouch(), Action is:1
Description of test results
  • Clicking the button will generate two types of events - press View and lift View, so it will call back onTouch() twice;
  • Because onTouch() returns true, the event is consumed and will not be passed down. View.dispatchTouchEvent() directly returns true;
  • Therefore, neither View.onTouchEvent() nor onClick() will be called in the end.

So far, the content of Android event distribution mechanism has been explained, that is, the event distribution mechanism of Activity, ViewGroup and View..

That is, the event distribution mechanism of Activity, ViewGroup and View

4. Summary

In this chapter, a large number of charts will be used to summarize the Android event distribution mechanism from various angles. It mainly includes:

  • Workflow summary
  • Business process summary
  • Summary with distribution object as the core
  • Summary with method as the core

4.1 workflow - Summary

Android event distribution process = Activity - > ViewGroup - > View, that is, after a click event occurs, the event is first transmitted to Activity, then to ViewGroup, and finally to View.

4.2 focus on the distribution object - Summary

Distribution objects mainly include: Activity, ViewGroup and View.

4.3 method centered - Summary

Event distribution methods mainly include: dispatchTouchEvent(), onInterceptTouchEvent() and onTouchEvent().

Special attention should be paid to:

  • Note 1: the dotted line on the left represents correlation and layer by layer return;
  • Note 2: the return to true of dispatchTouchEvent() in each layer is consistent (dotted line in the figure)
    The reason is: the return to true of the upper dispatchTouchEvent() depends on whether the lower dispatchTouchEvent() returns true. For example, when Activity.dispatchTouchEvent() returns true = ViewGroup.dispatchTouchEvent() returns true
  • Note 3: the returns of dispatchTouchEvent() and onTouchEvent() on each layer are consistent
    Reason: the return value of dispatchTouchEvent() of the lowest level View depends on the return value of View.onTouchEvent(); Combined with note 1, return up layer by layer to maintain consistency.

Next, the three methods will be shown in the flow chart for the default execution logic, return true and return false.

Method 1: dispatchTouchEvent()

The default execution logic, return true and return false are as follows.

Method 2: onInterceptTouchEvent()

The default execution logic, return true and return false are as follows.

Special attention should be paid here: neither Activity nor View has this method, which is only unique to ViewGroup.

Method 3: onTouchEvent()

The default execution logic, return true and return false are as follows.

Relationship between the three

Next, I use a piece of pseudo code to illustrate the relationship between the above three methods & Event passing rules

// After the click event is generated
// Step 1: call dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {

    boolean consume = false; //Whether the representative will consume the event

    // Step 2: judge whether to intercept the event
    if (onInterceptTouchEvent(ev)) {
      // a. If intercepted, the event will be handed over to the current View for processing
      // That is, onTouchEvent() is called to handle the click event
      consume = onTouchEvent (ev) ;

    } else {

      // b. If not intercepted, the event will be passed to the lower layer
      // That is, the dispatchTouchEvent() of the lower element will be called, and the above process will be repeated
      // Until the click event is finally processed
      consume = child.dispatchTouchEvent (ev) ;
    }

    // Step 3: finally return to notify whether the event is consumed (received & processed)
    return consume;

}

5. Common event distribution scenarios

Next, I will illustrate the common event delivery situation & process through an ex amp le

5.1 background description

  • The layout of the discussion is as follows:

  • scene

    1. The user first touches a point on View C on the screen (yellow area in the figure)

    Action_ The down event is generated here

    1. User moves finger
    2. Finally leave the screen

5.2 general event transmission

General event delivery scenarios include:

  • Default
  • Handling events
  • Intercept DOWN event
  • Intercept subsequent events (MOVE, UP)

Scenario 1: default

  • That is, the methods in the control (dispatchTouchEvent(), onTouchEvent(), onInterceptTouchEvent()) are not overridden or the return value is not changed
  • Then the default implementation of the three methods is called: calling the lower layer method & return layer by layer
  • Event transmission: (U-shaped)

    1. Call dispatchTouchEvent() from top to bottom

    Activity A ->> ViewGroup B ->> View C

    1. Call onTouchEvent() from the bottom up

    View C ->> ViewGroup B ->> Activity A

Note: Although the onInterceptTouchEvent() of ViewGroup B returns false for the DOWN event, subsequent events (MOVE, UP) will still be passed to its onInterceptTouchEvent()
This is different from the behavior of onTouchEvent(): other events of this event column are no longer passed & received

Scenario 2: handling events

Set View C to handle the click event, that is, set View C to Clickable or copy its onTouchEvent() to return true

The most common: set the Button button to respond to click events

Event transmission: (as shown below)

  • The DOWN event is passed to the onTouchEvent method of C, which returns true, indicating that the event is handled
  • Because View C is processing this event, the DOWN event will no longer be passed up to the onTouchEvent() of ViewGroup B and Activity A;
  • Other events (Move, Up) of this event column will also be passed to onTouchEvent() of View C

It will return to dispatchTouchEvent() layer by layer, and finally the event distribution ends

Scenario 3: intercept the DOWN event

Suppose ViewGroup B wants to handle the click event, that is, ViewGroup B replicates that onintercepttuchevent() returns true and ontuchevent() returns true
Event transmission: (as shown below)

  • The DOWN event is passed to onInterceptTouchEvent() of ViewGroup B. the method returns true, which means that the event is intercepted, that is, the event is handled by itself (the event is no longer passed DOWN)
  • Call its own onTouchEvent() to handle the event (the DOWN event will no longer be passed up to the onTouchEvent() of Activity A)
  • Other events (Move, Up) of this event column will be passed directly to onTouchEvent() of ViewGroup B

Note:

  1. Other events (Move, Up) in the event column will no longer be passed to onInterceptTouchEvent() of ViewGroup B; Cause: once this method returns true once, it will never be called again
  2. Return to dispatchTouchEvent() layer by layer, and the final event distribution ends

Scenario 4: intercept subsequent events of DOWN

conclusion

  • If the ViewGroup intercepts a half-way event (such as MOVE), the event will be turned into a CANCEL event by the system & passed to the child View that handled the event before;
  • This event is no longer passed to the onTouchEvent() of the ViewGroup
  • Only the next event will be passed to the onTouchEvent() of the ViewGroup

Scene description
ViewGroup B does not intercept the DOWN event (or View C handles the DOWN event), but it intercepts the next MOVE event

That is, the DOWN event passed to onTouchEvent() of View C returns true

Example explanation

  • In the subsequent MOVE event, the onInterceptTouchEvent() of ViewGroup B returns true to intercept the MOVE event, but the event is not passed to ViewGroup B; The MOVE event will be turned into a CANCEL event by the system and passed to onTouchEvent() of View C
  • Then there is another MOVE event, which will be directly passed to onTouchEvent() of ViewGroup B

Subsequent events will be directly passed to onTouchEvent() of ViewGroup B for processing, and will not be passed to onInterceptTouchEvent() of ViewGroup B, because once this method returns true once, it will never be called again.

  • View C will no longer receive subsequent events generated by this event column

So far, the common event delivery & process of Android has been explained.

6. Special instructions

6.1 subsequent events (MOVE, UP) level transmission of touch events

  • If a Touch event is registered for the control, each click will trigger a series of action events (ACTION_DOWN, ACTION_MOVE, ACTION_UP, etc.)
  • When the dispatchTouchEvent() event is distributed, the latter event (ACTION_MOVE and ACTION_UP) will be received only if the previous event (such as ACTION_DOWN) returns true

That is, if you are executing action_ false is returned when down, followed by a series of actions_ MOVE,ACTION_ No up event is executed

From the above analysis of event distribution mechanism:

  • dispatchTouchEvent(), onTouchEvent() consume events, and terminate event delivery (return true)
  • The onInterceptTouchEvent cannot consume events. It is equivalent to a bifurcation port, which plays the role of diversion and diversion for subsequent actions_ Move and ACTION_UP event reception plays a very important role

Remember: received action_ The function of down event may not receive subsequent events (ACTION_MOVE, ACTION_UP)

Action is given here_ Move and action_ Transmission conclusion of up event:

  • Conclusion 1
    If the dispatchTouchEvent() of an object (Activity, ViewGroup, View) consumes an event after distributing the event (returns true), then an action is received_ The down function can also receive ACTION_MOVE and ACTION_UP

Black line: ACTION_DOWN event delivery direction
Red line: ACTION_MOVE , ACTION_UP event transfer direction

  • Conclusion 2
    If onTouchEvent() of an object (Activity, ViewGroup, View) processes an event (returns true), ACTION_MOVE,ACTION_ After the up event is transferred to the View from top to bottom, it will not be transferred down, but directly to its own onTouchEvent() & end the event transfer process.

Black line: ACTION_DOWN event delivery direction
Red line: ACTION_MOVE,ACTION_UP event transfer direction

6.2 difference between ontouch() and onTouchEvent()

  • The 2 methods are invoked in View.dispatchTouchEvent ().
  • However, onTouch () takes precedence over onTouchEvent; If manual replication returns true in onTouch() (i.e. the event is consumed), onTouchEvent() will not be executed again

Note: if a control is not clickable (i.e. not enable), the onTouch event registered for it will never be executed. See the following code for the specific reasons

// &&It is short circuit and, that is, if the previous condition is false, it will not be executed downward
//  Therefore, two preconditions are required for the implementation of onTouch():
     // 1 \. The value of montouchlistener cannot be empty
     // 2 \. The currently clicked control must be enable d
mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)

// For this kind of control, if you want to listen to its touch event, you must override onTouchEvent () in this control

Article transferred from https://www.jianshu.com/p/380... If there is infringement, please contact to delete.

Related videos:

Android advanced UI Performance Optimization -- Application of Measure principle of View and explanation of xml parsing process principle_ Beep beep beep_ bilibili
Android advanced system learning -- Gradle introduction and project practice_ Beep beep beep_ bilibili
Android network architecture construction and principle analysis (I) -- witness the growth of network modules step by step through a network request_ Beep beep beep_ bilibili
[advanced Android course] - explanation of colin Compose's drawing principle (I)_ Beep beep beep_ bilibili
[advanced Android tutorial] - Handler source code analysis for Framework interview_ Beep beep beep_ bilibili
[advanced Android tutorial] - Analysis of hot repair Principle_ Beep beep beep_ bilibili
[advanced Android tutorial] - how to solve OOM problems and analysis of LeakCanary principle_ Beep beep beep_ bilibili

Topics: Android Programmer view