Android event delivery mechanism (two) scenario analysis

In the previous article,  Android event delivery mechanism (1) Process combing  , we combed the process of event delivery. In the previous analysis, we seemed to analyze an event as a whole, but it is not. In the previous article, we also mentioned that when the onTouchEvent() method returns true to indicate that the event is processed, the log process will be printed multiple times. This is because the TouchEvent event contains ACTION_DOWN, ACTION_MOVE, ACTION_UP and other events. When the onTouchEvent() method returns true, ACTION_MOVE, ACTION_UP The subsequent events will be passed to the corresponding controls. Now let's analyze this situation in detail.

As mentioned earlier: the event we are discussing refers to TouchEvent, which can be called a touch event, which includes ACTION_DOWN (press), ACTION_MOVE (move), ACTION_UP (lift), ACTION_CANCEL, etc. Under normal circumstances, the act of touching the mobile phone screen once starts with an ACTION_DOWN event, then generates a series of ACTION_MOVE events, and usually ends with an ACTION_UP event at the end. And we now know that the direction of event delivery is: from top to bottom from the parent control to the child control .

So what we need to understand is: when the ACTION_DOWN event is generated, it will be passed from the parent control to the child control, the ACTION_MOVE event is also passed from the parent control to the child control, and the ACTION_UP event is also passed from the parent control to the child control. They all follow the same logical flow of event delivery.

 

1. The results of the previous incident response will affect the execution of subsequent incidents

1. The impact of the return value of onTouchEvent() on subsequent events:

Taking the previous model as an example, ViewGroupA contains a ViewGroupB, and ViewGroupB contains a child View:

At this time, we modify the return value and log printing in the dispatchTouchEvent(), onInterceptTouchEvent(), and onTouchEvent() methods (only the changed parts are listed, and the rest remain unchanged):

In ViewGroupA:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupA...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupA...onInterceptTouchEvent: false, ACTION = " + ev.getAction());
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "ViewGroupA...onTouchEvent: false, ACTION = " + event.getAction());
        return false;
    }

In ViewGroupB:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupB...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupB...onInterceptTouchEvent: false, ACTION = " + ev.getAction());
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "ViewGroupB...onTouchEvent: false, ACTION = " + event.getAction());
        return false;
    }

In the child view:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "SonView...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "SonView...onTouchEvent: false, ACTION = " + event.getAction());
        return false;
    }

Among them, getAction() can obtain specific events. Specifically, ACTION_DOWN = 0; ACTION_UP = 1; ACTION_MOVE = 2; ACTION_CANCEL = 3. Also touch the SonView area to get the following log results:

The event delivery process is still the same as the previous article, but there is a problem: it does not mean that under normal circumstances, a touch event starts from the ACTION_DOWN event, then generates a series of ACTION_MOVE events, and usually ends with an ACTION_UP event. But why is there only ACTION_DOWN event now, where did the subsequent ACTION_MOVE event and ACTION_UP event go?

If we modify the method in the child View again at this time, let the onTouchEvent() method return true:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "SonView...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "SonView...onTouchEvent: true, ACTION = " + event.getAction());
        return true;
    }

Tap SonView to print the log:

At this point, it can be seen that ACTION_DOWN, ACTION_MOVE, and ACTION_UP are executed successively, and are delivered in turn according to the event delivery process in the above figure. It also verifies our above conclusion: when the ACTION_DOWN event is generated, it will be passed from the parent control to the child control, the ACTION_MOVE event is also passed from the parent control to the child control, and the ACTION_UP event is also passed from the parent control to the child control. They all follow the same logical flow of event delivery.

Next, analyze the result of the second time and compare it with the first time: In the first time, the ACTION_DOWN event is passed to the child View, but the onTouchEvent() method of the child View returns a false for the processing of this ACTION_DOWN event, so that It will cause the onTouchEvent() method of the parent View to be called, and there is another consequence, that is, the subsequent ACTION_MOVE and ACTION_UP events will not be passed on to the child View . Similarly, the onTouchEvent() method of the parent View (ie ViewGroupB) also returns a false value for the processing of this ACTION_DOWN event, so the subsequent ACTION_MOVE and ACTION_UP events will not be passed on to ViewGroupB. In the same way, the onTouchEvent() method of ViewGroupA also returns a false, so the subsequent ACTION_MOVE and ACTION_UP events will not be passed to ViewGroupA, so the subsequent ACTION_MOVE and ACTION_UP events are not included in the first log.

Simply put, if you return false when you execute the ACTION_DOWN event in the onTouchEvent() method, then a series of other ACTIONs after the control will no longer be executed. In fact, when the dispatchTouchEvent() method is performing event distribution, only the previous ACTION will return true, then the latter ACTION will be passed to the control. So if a View needs to handle the sliding event, that is, the ACTION_MOVE event, then it must not return false in the ACTION_DOWN event in the onTouchEvent() method.

       2. The effect of the return value of onInterceptTouchEvent() on subsequent events:

Similarly, we modify the return value and log printing in the dispatchTouchEvent(), onInterceptTouchEvent(), and onTouchEvent() methods:

In ViewGroupA:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupA...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupA...onInterceptTouchEvent: false, ACTION = " + ev.getAction());
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "ViewGroupA...onTouchEvent: false, ACTION = " + event.getAction());
        return false;
    }

In ViewGroupB:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupB...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupB...onInterceptTouchEvent: false, ACTION = " + ev.getAction());
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "ViewGroupB...onTouchEvent: true, ACTION = " + event.getAction());
        return true;
    }

In the child view:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "SonView...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "SonView...onTouchEvent: false, ACTION = " + event.getAction());
        return false;
    }

It can be seen that we just returned true in the onTouchEvent() method of ViewGroupB, that is, let ViewGroupB consume this event. Tap the SonView area, you will get the following log results:

The above log can also verify our above conclusion: the ACTION_DOWN event on line 5 is passed to the child View, but the onTouchEvent() method of the child View on line 6 returns a false for the processing of this ACTION_DOWN event, and the parent View on line 7 (Ie ViewGroupB) onTouchEvent() method returns true for the processing of this ACTION_DOWN event, and subsequent ACTION_MOVE and ACTION_UP events will not be passed on to the child View, but directly handed over to the onTouchEvent() method of ViewGroupB for processing, and not Then call onInterceptTouchEvent() of ViewGroupB to determine whether it is intercepted.

But this is not our current focus. The focus is on the return value of the onInterceptTouchEvent() method. We modify the onInterceptTouchEvent() method in ViewGroupB to return true as follows:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupB...dispatchTouchEvent: ");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(Constant.TAG, "ViewGroupB...onInterceptTouchEvent: true, ACTION = " + ev.getAction());
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(Constant.TAG, "ViewGroupB...onTouchEvent: true, ACTION = " + event.getAction());
        return true;
    }

Tap the SonView area after execution, and you will get the following log results:

As shown in the log, when the ACTION_DOWN event arrives on ViewGroupB in line 3, ViewGroupB in line 4 needs to call its own onInterceptTouchEvent() method to determine whether to intercept this ACTION_DOWN event. At this time, it returns true to indicate interception, then this ACTION_DOWN event will arrive. Respond in the onTouchEvent() method of ViewGroupB. The onTouchEvent() method of ViewGroupB in line 5 also returns true, which means that ViewGroupB responded to the ACTION_DOWN event. But there is one difference here is that the event is passed to the onTouchEvent() method of ViewGroupB because its own onInterceptTouchEvent() method judges the interception, instead of being returned by the child View. In this case, when When the ACTION_MOVE event and ACTION_UP event are passed to ViewGroupB, it is of course not passed to the child View, and its own onInterceptTouchEvent() method is no longer called, but dispatchTouchEvent()->onTouchEvent() is passed directly to ViewGroupB. event. However, for ViewGroupA, it is still the process of dispatchTouchEvent()->onInterceptTouchEvent().

Simply put, if you return true when you execute ACTION_DOWN in the onInterceptTouchEvent() method, then a series of other ACTIONs behind the control will not call the onInterceptTouchEvent() method to determine whether it is intercepted, but directly call onTouchEvent( ) Method consumption event.

Guess you like

Origin blog.csdn.net/beita08/article/details/89084033