1. What is event distribution?

Since Android views are layered on top of each other, who should handle the click event when clicked in the position shown below?

This is where the event distribution mechanism is needed to handle it.

In plain English, event distribution is simply a set of rules that determine to whom click events are distributed for processing.

2. Event distribution scenario

Here are a few questions, I don’t know if you have encountered sliding conflict, here are three sliding conflict scenarios:

2.1 a scene

There are two views in the figure. The outer View slides horizontally and the inner View slides vertically. How do I distribute this event when I’m sliding inside the View?

2.2 scenario 2

Now the external View and the internal View slide in the same direction, this time in the internal View slide, this time how to solve the sliding conflict?

2.3 scenario 3

If the first two scenes are mixed at the same time, how do you distribute them?

Okay, so with these questions, we’re going to talk about the event distribution.

3. Basic concepts of event distribution

Several key concepts must be understood in learning event distribution.

3.1 What is an event?

When the finger clicks on the screen, events will be generated. The information of these events is in the MotionEvent class. The types of events are as follows:

Types of events You mean
MotionEvent.ACTION_DOWN Press the View with your finger
MotionEvent.ACTION_MOVE Swipe your finger around the View
MotionEvent.ACTION_UP Finger lift

3.2 What is the Sequence of events?

An event sequence is a sequence of events from the time the finger presses the View until the finger leaves the View.

It starts with a DOWN event, produces countless MOVE events, and ends with an UP event.

3.3 Transmission order of event sequence

Activity -> ViewGroup -> … -> View

3.4 Key methods of event sequence

methods role
dispathchTouchEvent() Distribute click events
onInterceptTouchEvent() Determines whether to block the click event
onTouchEvent() Handling click events

4. Key concepts of event distribution

4.1 Event Distribution Process

Note that there is no onInterceptTouchEvent() method for activities and views.

Now explore what the flow of event distribution looks like.

The process of exploration is printed step by step, and change the relevant return values, and then draw a local flow chart, and finally get the global flow chart.

Here’s the test code:

Util:

public class Util {

    public static String getAction(MotionEvent event) {
        String action = "";

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            action = "ACTION_DOWN";
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            action = "ACTION_MOVE";
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            action = "ACTION_UP";
        }

        returnaction; }}Copy the code

All this class does is let print print out which click event it is.

MainActivity:

public class MainActivity extends AppCompatActivity {

    public String TAG = "chan";

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "=================Activity dispatchTouchEvent Action: "
                + Util.getAction(ev));
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        Log.d(TAG, "=================Activity onTouchEvent Action: "
                + Util.getAction(ev));
        returnsuper.onTouchEvent(ev); }}Copy the code

ViewGroup1:

public class ViewGroup1 extends LinearLayout {

    public String TAG = "chan";

    public ViewGroup1(Context context) {
        super(context);
    }

    public ViewGroup1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(TAG, "=================ViewGroup dispatchTouchEvent Action: "
                + Util.getAction(ev));
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(TAG, "=================ViewGroup onInterceptTouchEvent Action: "
                + Util.getAction(ev));
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d(TAG, "=================ViewGroup onTouchEvent Action: "
                + Util.getAction(event));
        returnsuper.onTouchEvent(event); }}Copy the code

The View1:

public class View1 extends View  {

    public String TAG = "chan";


    public View1(Context context) {
        super(context);
    }

    public View1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(TAG, "=================View dispatchTouchEvent Action: "
                + Util.getAction(event));
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean result = super.onTouchEvent(event);
        Log.d(TAG, "=================View onTouchEvent Action: "
                + Util.getAction(event));
        returnsuper.onTouchEvent(event); }}Copy the code

Activity_main. XML:

<? xml version="1.0" encoding="utf-8"? > <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <com.example.administrator.toucheventdemo.ViewGroup1
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:background="#00ff00">


        <com.example.administrator.toucheventdemo.View1
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:background="#ff0000"/>

    </com.example.administrator.toucheventdemo.ViewGroup1>


</android.support.constraint.ConstraintLayout>
Copy the code

4.1.1 Changing the return value of the Activity’s dispathchTouchEvent() method

There are three possibilities for the return value:

  • super.dispatchTouchEvent(ev)
  • true
  • false

4.1.1.1 return super. DispatchTouchEvent (ev)

Print result:

08-09 09:49:03. 167, 26886-26886 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_DOWN 08-09 09:49:03. 168, 26886-26886 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup dispatchTouchEvent Action: ACTION_DOWN =================ViewGroup onInterceptTouchEvent Action: ACTION_DOWN =================View dispatchTouchEvent Action: ACTION_DOWN 08-09 09:49:03. 169, 26886-26886 / com. Example. Administrator. Toucheventdemo D/chan: =================View onTouchEvent Action: ACTION_DOWN =================ViewGroup onTouchEvent Action: ACTION_DOWN =================Activity onTouchEvent Action: ACTION_DOWN 08-09 09:49:03. 190, 26886-26886 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE =================Activity onTouchEvent Action: ACTION_MOVE 08-09 09:49:03. 196, 26886-26886 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE =================Activity onTouchEvent Action: ACTION_MOVE 08-09 09:49:03. 197, 26886-26886 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_UP 08-09 09:49:03. 197, 26886-26886 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity onTouchEvent Action: ACTION_UPCopy the code

The flow chart drawn according to the printed results:

The above flow chart only depicts the distribution process of DOWN events. From the printed result, we can see that the Activity does not distribute the following MOVE and UP events, but processes them by itself. One conclusion can be drawn here:

If a View does not consume DOWN events, the rest of the same sequence of events will not be handled by it, but by its parent element.

4.1.1.2 returns true

Print result:

08-09 10:11:19. 533, 28137-28137 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_DOWN 08-09 10:11:19. 546, 28137-28137 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE 08-09 10:11:19. 569, 28137-28137 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE 08-09 10:11:19. 572, 28137-28137 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_UPCopy the code

If you return true, no events are distributed and can be handled in the Activity.

4.1.1.3 returns false

Print result:

08-09 10:13:54. 394, 28415-28415 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_DOWN 08-09 10:13:54. 442, 28415-28415 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE 08-09 10:13:54. 444, 28415-28415 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_UPCopy the code

If you return false directly, no events are distributed and events can be handled in the Activity.

As you can see from this, Acitity must return the default value of dispatchTouchEvent() if it wants to distribute events.

4.1.2 Changing the return value of the ViewGroup dispathchTouchEvent() method

Having explored the case where ViewGroup returns a default value, let’s just look at the case where ViewGroup returns true and false.

4.1.2.1 returns true

Print result:

08-09 10:20:51. 658, 29295-29295 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_DOWN 08-09 10:20:51. 659, 29295-29295 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup dispatchTouchEvent Action: ACTION_DOWN 08-09 10:20:51. 690, 29295-29295 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE 08-09 10:20:51. 691, 29295-29295 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup dispatchTouchEvent Action: ACTION_MOVE 08-09 10:20:51. 699, 29295-29295 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE =================ViewGroup dispatchTouchEvent Action: ACTION_MOVE 08-09 10:20:51. 702, 29295-29295 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_UP =================ViewGroup dispatchTouchEvent Action: ACTION_UPCopy the code

The flow chart drawn according to the printed results:

4.1.2.1 returns false

Print result:

08-09 10:25:27. 046, 29672-29672 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_DOWN 08-09 10:25:27. 047, 29672-29672 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup dispatchTouchEvent Action: ACTION_DOWN 08-09 10:25:27. 048, 29672-29672 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity onTouchEvent Action: ACTION_DOWN 08-09 10:25:27. 062, 29672-29672 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE =================Activity onTouchEvent Action: ACTION_MOVE 08-09 10:25:27. 078, 29672-29672 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE =================Activity onTouchEvent Action: ACTION_MOVE 08-09 10:25:27. 088, 29672-29672 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE =================Activity onTouchEvent Action: ACTION_MOVE 08-09 10:25:27. 091, 29672-29672 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_UP =================Activity onTouchEvent Action: ACTION_UPCopy the code

As you can see from the results, if the ViewGroup does not consume DOWN events, the subsequent events in the event sequence are not handed to it.

4.1.3 Changing the return value of the onInterceptTouchEvent() method in ViewGroup

OnInterceptTouchEvent () returns false by default, so we’ll just explore returning true and the default.

4.1.3.1 returns true

Print result:

08-09 10:31:52. 499, 30168-30168 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_DOWN 08-09 10:31:52. 501, 30168-30168 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup dispatchTouchEvent Action: ACTION_DOWN =================ViewGroup onInterceptTouchEvent Action: ACTION_DOWN =================ViewGroup onTouchEvent Action: ACTION_DOWN =================Activity onTouchEvent Action: ACTION_DOWN 08-09 10:31:52. 515, 30168-30168 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_UP =================Activity onTouchEvent Action: ACTION_UPCopy the code

Flow chart:

The print above is for the ViewGroup’s onTouchEvent to return the default value, but as the flow chart above shows, if onTouchEvent returns the default value or false, meaning that it does not consume the DOWN event, then it will not be given any other events in the event sequence.

4.1.3.2 Return false or the default value

If this is the case, the event will be directly distributed to the next View, which I won’t go over here.

View dispathchTouchEvent() returns true to consume the event, false to not process the event, Return the default to pass the event to onTouchEvent().

The overall flow chart can be obtained from the above results:

4.2 Some conclusions of the event distribution

In fact, some conclusions can be drawn from the above research:

  1. If a View does not consume DOWN events, the rest of the same sequence of events will not be handled by it, but by its parent element
  2. If a ViewGroup decides to intercept an event (onInterceptTouchEvent returns true), onInterceptTouchEvent() will not be called again

One more thing to verify is that if the View does not consume events other than the DOWN event, those events are handed directly to the Activity’s onTouchEvent() and not to its parent’s onTouchEvent().

Here’s the code for View1:

public class View1 extends View  {

    public String TAG = "chan";


    public View1(Context context) {
        super(context);
    }

    public View1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(TAG, "=================View dispatchTouchEvent Action: "
                + Util.getAction(event));
        return super.dispatchTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean result = super.onTouchEvent(event);
        Log.d(TAG, "=================View onTouchEvent Action: "
                + Util.getAction(event));
        if(event.getAction() ! = MotionEvent.ACTION_DOWN) {return false;
        }
        return true; }}Copy the code

You can see the code and I just changed the onTouchEvent() code to consume only DOWN events and nothing else.

Print result:

08-09 11:18:52. 846, 3098-3098 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_DOWN 08-09 11:18:52. 847, 3098-3098 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup dispatchTouchEvent Action: ACTION_DOWN 08-09 11:18:52. 848, 3098-3098 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup onInterceptTouchEvent Action: ACTION_DOWN =================View dispatchTouchEvent Action: ACTION_DOWN =================View onTouchEvent Action: ACTION_DOWN 08-09 11:18:52. 863, 3098-3098 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_MOVE 08-09 11:18:52. 864, 3098-3098 / com. Example. Administrator. Toucheventdemo D/chan: =================ViewGroup dispatchTouchEvent Action: ACTION_MOVE =================ViewGroup onInterceptTouchEvent Action: ACTION_MOVE =================View dispatchTouchEvent Action: ACTION_MOVE =================View onTouchEvent Action: ACTION_MOVE =================Activity onTouchEvent Action: ACTION_MOVE 08-09 11:18:52. 877, 3098-3098 / com. Example. Administrator. Toucheventdemo D/chan: =================Activity dispatchTouchEvent Action: ACTION_UP =================ViewGroup dispatchTouchEvent Action: ACTION_UP =================ViewGroup onInterceptTouchEvent Action: ACTION_UP =================View dispatchTouchEvent Action: ACTION_UP 08-09 11:18:52. 878, 3098-3098 / com. Example. Administrator. Toucheventdemo D/chan: =================View onTouchEvent Action: ACTION_UP =================Activity onTouchEvent Action: ACTION_UPCopy the code

As you can see from the print, all events except the DOWN event are handled directly by the Activity without calling the ViewGroup onTouchEvent().

Reference article:

  • Exploring the Art of Android Development

  • Android event distribution mechanism in detail walkthrough, you deserve it

  • Android custom View advanced – event distribution mechanism principle

  • Android custom View advanced – event distribution mechanism details