directory

role

What does the event distribution process do for us as developers?

  • Customize the rules for sliding touch responses based on our own requirements.
  • Resolving sliding conflicts

The relevant knowledge

MotionEvent

To quote the official introduction: Object used to report movement (mouse, pen, finger, trackball) events. Motion events may hold either absolute or relative movements and other data, depending on the type of device.

An object that reports movement events (mouse, pen, finger, trackball). Motion events can hold absolute or relative motion and other data, depending on the type of device.

In event distribution, there are typically three commonly used events:

  • ACTION_DOWN —— Press down
  • ACTION_MOVE ——- Move operation
  • ACTION_UP ———– Lift operation

Generally, the complete flow of an event is ACTION_DOWN > ACTION_MOVE > ACTION_UP

Commonly used method meaning
getX/Y() Click events relative toTop left corner of current ViewThe x/y wheelbase is off
getRawX/Y() Click events relative toTop left corner of the phone screenThe x/y wheelbase is off

Event distribution related methods.

  • public boolean dispatchTouchEvent(MotionEvent ev)

    For event distribution, the touch event is passed down to the target view or, if it is the target view itself, to handle the event. The return result is affected by its own onTouchEvent and the dispatchTouchEvent method of a subordinate View.

  • public boolean onInterceptTouchEvent(MotionEvent ev)

    Called in dispatchTouchEvent. Whether to intercept events. If false is returned, events are not intercepted. If the return event is true, the event is intercepted, and subsequent events of the event are handled by itself. This method is not called again to ask whether to intercept.

    Only ViewGroup has this method, which returns false by default and does not intercept.

  • public boolean onTouchEvent(MotionEvent ev)

    Called in dispatchTouchEvent. Used to handle events, returning true for consumption events; Return false to indicate that the event is not consumed.

Pseudo-code representing the relationship between the three :(excerpt from chapter 3 of Android art exploration)

// Pseudo-code for dispatchTouchEvent, onInterceptTouchEvent and onTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume;
    if(onInterceptTouchEvent(ev)){
        consume = onTouchEvent(ev);
    }else{
        consume = childView.dispatchTouchEvent(ev);
    }
    return consume ;
} 
Copy the code

Through the above pseudocode, we can roughly understand the event transmission rules: For a root ViewGroup, when the click event is generated, it is first passed to it, and its dispatchTouchEvent is called. If its onInterceptTouchEvent method returns true, it intercepts the event. The event is then handled by the ViewGroup’s onTouchEvent. If onInterceptTouchEvent returns false, meaning that the event is not intercepted, the current event is passed to its children. The child’s dispatchTouchEvent method is then called, and so on until the event is finally processed.

Distribution process

What is the specific call flow of the three methods in a specific project? Let’s do a Demo.

All three methods are simply overridden and the log is printed.

public class Group1 extends FrameLayout {
    public static final String TAG = "-- -- -- -- -- -- -- -- -- --";
    / /... Code omission......

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(TAG, "dispatchTouchEvent-----------------"+ Util.getMotionEvent(ev) + this.getClass().getSimpleName());
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(TAG, "onInterceptTouchEvent-----------------" + Util.getMotionEvent(ev)+ this.getClass().getSimpleName());
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG, "onTouchEvent-----------------" + Util.getMotionEvent(event)+ this.getClass().getSimpleName());
        return super.onTouchEvent(event); }}Copy the code

View code:

public class View1 extends View {
    public static final String TAG = "-- -- -- -- -- -- -- -- -- --";
    / /... Code omission......

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(TAG,"dispatchTouchEvent-----------------" + Util.getMotionEvent(event)+ this.getClass().getSimpleName());
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG,"onTouchEvent-----------------"+ Util.getMotionEvent(event) + this.getClass().getSimpleName());
        return super.onTouchEvent(event); }}Copy the code

Util class code (prints out the name of the event) :

class Util {
    static String getMotionEvent(MotionEvent motionEvent) {
        int action = motionEvent.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            return "ACTION_DOWN----";
        } else if (action == MotionEvent.ACTION_UP) {
            return "ACTION_UP------";
        } else if (action == MotionEvent.ACTION_MOVE) {
            return "ACTION_MOVE----";
        } else if (action == MotionEvent.ACTION_CANCEL) {
            return "ACTION_CANCEL--";
        } else {
            returnString.valueOf(motionEvent); }}}Copy the code

MainActivity code:

public class MainActivity extends AppCompatActivity {
    public static final String TAG = "-- -- -- -- -- -- -- -- -- --";
    / /... Code omission......

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(TAG, "dispatchTouchEvent-----------------" + com.sjc.eventdispatch.Util.getMotionEvent(ev) + this.getClass().getSimpleName());
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG, "onTouchEvent-----------------" + com.sjc.eventdispatch.Util.getMotionEvent(event) + this.getClass().getSimpleName());
        return super.onTouchEvent(event); }}Copy the code

XML file in MainActivity:


      
<com.sjc.eventdispatch.Group1 xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.sjc.eventdispatch.View1
        android:id="@+id/view1"
        android:layout_width="200dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:background="@color/colorPrimaryDark" />
</com.sjc.eventdispatch.Group1>
Copy the code

Specific page presentation:

A simulation:

  • Condition: all methods keep returning super.xxx(event);
  • Action: Click View1;
/ / the Log Log
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----MainActivity

----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: onTouchEvent--------ACTION_MOVE----MainActivity

----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: onTouchEvent--------ACTION_UP------MainActivity
Copy the code

From the event transmission process, we can see that when the user touches the screen for operation, the event is first transmitted to the Activity, and then distributed to Group1 with the layout through dispatchTouchEvent. Group1 distributes the event. It calls its own onInterceptTouchEvent to ask if it intercepts (it doesn’t by default), and then passes it to the currently clicked child control View1, which dispatchTouchEvent assigns to itself. Call your own onTouchEvent method. View1 doesn’t consume events by default, so the event is passed to Group1’s onTouchEvent method. Since Group1 doesn’t consume events either, the event is passed back to the Activity, and the Activity’s onTouchEvent receives the event. And subsequent events are received directly by it.

Conclusion:

  1. If a control does not consume a DOWN event passed to it, subsequent events are not passed to it.
  2. If none of the controls in a click area consume events, that event will eventually pass an Activity.

Simulation 2:

  • Condition: View1’s onTouchEvent method returns true;
  • Action: Click View1;
/ / the log log
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1

----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onInterceptTouchEvent--------ACTION_MOVE----Group1
----------: dispatchTouchEvent--------ACTION_MOVE----View1
----------: onTouchEvent--------ACTION_MOVE----View1

----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onInterceptTouchEvent--------ACTION_UP------Group1
----------: dispatchTouchEvent--------ACTION_UP------View1
----------: onTouchEvent--------ACTION_UP------View1
Copy the code

In a typical click-event delivery process, the user clicks a button, the button responds to the action, and the event is consumed by the button. Group1 calls the onInterceptTouchEvent method each time an event is passed in to check for intercepting.

Simulation of three:

  • Condition 1: View1’s onTouchEvent method returns true, Group1’s onInterceptTouchEvent returns true.
  • Action 1: Click View1;
/ / the Log Log
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----MainActivity
        
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: onTouchEvent--------ACTION_MOVE----MainActivity
        
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: onTouchEvent--------ACTION_UP------MainActivity
Copy the code
  • Condition 2: View1’s onTouchEvent method returns true, Group1’s onInterceptTouchEvent returns true, and Group1’s onTouchEvent returns true.
  • Action 2: Click View1.
/ / the Log Log
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----Group1
    
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onTouchEvent--------ACTION_MOVE----Group1
    
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onTouchEvent--------ACTION_UP------Group1
Copy the code

Group1’s onInterceptTouchEvent returns true to intercept the event, and its onTouchEvent is called. OnTouchEvent returns true to indicate that the event is consumed and subsequent events are passed to it for processing; OnTouchEvent returns false to indicate that the event is not consumed and is passed up to the Activity. Subsequent events are not passed to Group1 or View1 for processing.

Conclusion:

  1. Once the ViewGroup intercepts an event, subsequent events are handed over to it, and the onInterceptTouchEvent method is no longer called to ask if the event is intercepted.
  2. It’s been verified againIf a control does not consume a DOWN event passed to it, subsequent events are not passed to it.

conclusion

Other facts:

An understanding of the CANCEL event

The unintentional premature termination of a sequence of events.

Simulation conditions:

  • View1’s onTouchEvent method returns true;
  • Group1’s onInterceptTouchEvent returns true on MOVE events and false on other events.
  • Other methods keep returning super.xxx().

Simulate action: Click View1;

public class Group1 extends FrameLayout {
    public static final String TAG = "-- -- -- -- -- -- -- -- -- --";
    / /... Code omission......
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(TAG, "onInterceptTouchEvent--------" + Util.getMotionEvent(ev) + this.getClass().getSimpleName());
        boolean intercept;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                intercept = false;
                break;
            case MotionEvent.ACTION_MOVE:
                intercept = true;
                break;
            case MotionEvent.ACTION_UP:
                intercept = false;
                break;
            default:
                intercept = false;
                break;
        }
        returnintercept; }}Copy the code
/ / the log log
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1
        
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onInterceptTouchEvent--------ACTION_MOVE----Group1
----------: dispatchTouchEvent--------ACTION_CANCEL--View1
----------: onTouchEvent--------ACTION_CANCEL--View1
        
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onTouchEvent--------ACTION_MOVE----Group1
----------: onTouchEvent--------ACTION_MOVE----MainActivity
        
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onTouchEvent--------ACTION_UP------Group1
----------: onTouchEvent--------ACTION_UP------MainActivity
Copy the code

In this event stream, View1 consumes the DOWN event first. When the user moves, a MOVE event is generated. Group1’s onInterceptTouchEvent method returns true in the MOVE event, indicating that the MOVE event and subsequent UP events are handled by Group1. I’m not passing it to View1 anymore. At this point View1 consumes only DOWN events and is in the middle of a stream of events. In order for View1 to have a complete stream of events, it passes View1 a CANCEL event to tell View1 that the stream of events is over for it.

requestDisallowInterceptTouchEvent

Request the parent control not to intercept events. A method that belongs to ViewParent, which is an interface, and a ViewGroup implements the ViewParent interface.

Simulation conditions:

  • The View1 onTouchEvent method returns true, call requestDisallowInterceptTouchEvent method in dispatchTouchEvent method;
  • Group1’s onInterceptTouchEvent returns true on MOVE events and false on other events.
  • Other methods keep returning super.xxx().

Simulate action: Click View1;

public class View1 extends View {
    public static final String TAG = "-- -- -- -- -- -- -- -- -- --";
    / /... Code omission......
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(TAG, "dispatchTouchEvent--------" + Util.getMotionEvent(event) + this.getClass().getSimpleName());
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(event); }}Copy the code
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1
        
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: dispatchTouchEvent--------ACTION_MOVE----View1
----------: onTouchEvent--------ACTION_MOVE----View1
        
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onInterceptTouchEvent--------ACTION_UP------Group1
----------: dispatchTouchEvent--------ACTION_UP------View1
----------: onTouchEvent--------ACTION_UP------View1
Copy the code

We can see the View1 call requestDisallowInterceptTouchEvent, can receive the MOVE and other subsequent events, the interception of Group1 didn’t work. So we can use requestDisallowInterceptTouchEvent to solve the problem of some developers such as the conflicts of sliding.

Note: the View call requestDisallowInterceptTouchEvent request parent control effect not intercept meaningful premise is: the View would receive the DOWN event, and a DOWN event consumer. If a control does not consume DOWN events, subsequent events are not passed to it.

Source code analysis

View event distribution (two) source analysis


This article is used to record the understanding and notes in the learning process. If there are any mistakes, please correct them. Thank you very much!

Event distribution series:

View event distribution (two) source analysis

View event distribution (three) source code analysis

Reference documentation

Chapter 3 of Exploring the Art of Android Development

Android event distribution mechanism, big cousin take you slowly in-depth