Android touch event distribution mechanism (1)

1 Introduction

This article mainly shares the basic concepts in event distribution.

Describes the main methods responsible for participating in distribution events.

From the core logic of these methods, the rules of event distribution are summarized.

2. Objects to be distributed


What are the objects being distributed? The distributed objects are click events generated when the user touches the screen. The events mainly include: press
, slide, lift and cancel. These events are encapsulated into MotionEvent objects. The main events in this object
are shown in the following table:

The events of pressing, sliding, lifting, and canceling form an event stream. The event flow starts with pressing, may have
several swipes in the middle, and ends with lifting or canceling.
In the process of event distribution in Android, the main task is to distribute the press event and then find
the component that can handle the press event. For subsequent events in the event stream (such as sliding, lifting, etc.), they are directly distributed to components that can handle press events
. Therefore, the content discussed in this article is mainly focused on the press event.

 3. Components that distribute events

Components that distribute events, also known as event dispatchers, include Activity, View, and ViewGroup. The general structure of the three of them
is:

Event Distributor Structure
As can be seen from the above figure, Activity includes ViewGroup, and ViewGroup can contain multiple Views. 

4. Core methods of distribution 

There are three main methods responsible for distributing events, namely:
dispatchTouchEvent (
onTouchEvent ()
onInterceptTouchEvent ().
They do not exist in all components responsible for distribution, and their specific situations are summarized in the following table:

From the table, the dispatchTouchEvent and onTouchEvent methods exist in the three components above. And
onInterceptTouchEvent is unique to ViewGroup. The specific functions of these methods are introduced below.
In the ViewGroup class, there is actually no onTouchEvent method, but since ViewGroup inherits from View, and
View has the onTouchEvent method, the object of ViewGroup can also call the onTouchEvent method. Therefore, the table shows that the onTouchEvent method exists in ViewGroup. 

5. Event distribution process

This section is the core content of this article and will introduce the event distribution process as a whole.
Regarding the event distribution process, the author believes that the views in some online tutorials are wrong.
Some online tutorials believe that events are distributed from within (such as Button), which is wrong.
Some online tutorials often use descriptions such as 'upward' and 'downward' propagation, but do not explain 'what is up' and 'what is down'.
Some tutorials on the Internet refer to the process of Java subclass objects calling parent class methods (upward transformation) as 'upward' propagation, which confuses the propagation of
events between components with the polymorphic features of programming languages, making it difficult for beginners to understand.
It is also wrong for a subclass to call a method of the same name of the parent class in an overridden method, which is called 'upward propagation'.
To this end, before introducing the distribution process, let us first define some concepts:
Downward propagation: Activity includes Layout, and the propagation of events from Activity to Layout is called 'downward propagation'. Layout
contains several Views, and events propagate from Layout to its sub-Views, also known as 'downward propagation'.
Upward propagation: Opposite of 'downward propagation'.
'Upward transformation' cannot be called propagation, that is, when a subclass object calls a parent class method, or calls a parent class method in an overridden method, it
cannot be called propagation. Concepts in object-oriented programming languages ​​should not be confused with propagation up and down the layout hierarchy.
Distribution method dispatchTouchEvent
It can be seen from the name of the method that this method is mainly responsible for distribution and is the core of the Android event distribution process. How events are
delivered mainly depends on this method. If you understand this method, you will also understand the Android event distribution mechanism.
Before understanding the core mechanism of this method, you need to know a conclusion:
If this method of a component returns TRUE, it means that the component has processed the event and does not need to continue calling the
distribution methods of other components, that is, distribution is stopped.
If this method of a component returns FALSE, it means that the component cannot handle the event and needs to continue to
distribute events according to the rules. Without overriding this method, except for some special components, all other components will return False by default
. Examples follow.
Why don't you need to continue distribution when TRUE is returned, but stop distribution when FALSE is returned? In order to solve this question, we need to take a look
at the specific distribution logic of this method. For ease of understanding, the dispatchTouchEvent method is simplified below,
retaining only the core logic.
Activity's dispatchTouchEvent method

// Activity 中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
if (child.dispatchTouchEvent(ev)) {
    return true; //如果子 View 消费了该事件,则返回 TRUE,让调用者知道该事件已
被消费
} else {
    return onTouchEvent(ev); // 如 果 子 View 没 有 消 费 该 事 件 , 则 调 用 自 身 的
    onTouchEvent 尝试处理。
    }
}

First of all, it can be seen from the core logic that when an event is passed to Activity, it first distributes the event to the child View for processing.
If the event is consumed (that is, TRUE is returned) after being passed or processed by sub-Views, the Activity's distribution
method will also return TRUE, which also means that the event has been consumed.
If the event is not consumed (that is, FALSE is returned) after being passed or processed layer by layer by the sub-View, the Activity's
distribution method will not return TRUE, but call onTouchEvent() to process it to see its actual processing situation. .
If onTouchEvent consumes the event, it can still return TRUE (indicating that the event has been consumed). This TRUE is used as the
return value of dispatchTouchEvent to let the object calling it know that the Activity has consumed the event.
If onTouchEvent does not consume the event, it returns FALSE (indicating that the event has not been consumed). This FALSE is used as the
return value of dispatchTouchEvent to let the object calling it know that the Activity has not consumed the event and needs to continue processing.
ViewGroup's dispatchTouchEvent method

// ViewGroup 中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (!onInterceptTouchEvent(ev)) {
        return child.dispatchTouchEvent(ev); //不拦截,则传给子 View 进行分发处理
    } else {
        return onTouchEvent(ev); //拦截事件,交由自身对象的 onTouchEvent 方法处理
    }
}

This method of ViewGroup is similar to that of Activity, except that a new onInterceptTouchEvent method is added. When an event
comes in, onInterceptTouchEvent is first called.
If the method returns FALSE (indicating no interception), it is handed over to the sub-View to call the dispatchTouchEvent() method.
If
the method returns TRUE (indicating interception), it is directly handed over to the onTouchEvent(ev)
method of the ViewGroup object for processing. Whether it can be handled specifically depends on the actual situation of onTouchEvent().
In fact, when onInterceptTouchEvent returns TRUE to indicate interception, the super.dispatchTouchEvent method is actually called
, that is, the method of View, which then calls onTouchEvent.
View's dispatchTouchEvent method.

// View 中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
//如果该对象的监听成员变量不为空,则会调用其 onTouch 方法,
    if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
        return true; // 若 onTouch 方 法 返 回 TRUE , 则 表 示 消 费 了 该 事 件 , 则
        dispachtouTouchEvent 返回 TRUE,让其调用者知道该事件已被消费。
    }
    return onTouchEvent(ev); //若监听成员为空或 onTouch 没有消费该事件,则调用对象自身的        onTouchEvent 方法处理。
}

As can be seen from the core logic of this method, after the event is passed in, mOnTouchListener will first be empty. If the
Listener was previously set, its onTouch method will be called.
If the onTouch method returns TRUE, dispatchTouchEvent will also return TRUE, indicating that the event is consumed.
If the onTouch method returns FALSE, or mOnTouchListener is originally empty, call its own onTouchEvent()
for processing. Whether to consume the event can be judged by its return value.
In fact, in the onTouchEvent method of View, if the onClickListener listening object is set,
its onClick method will be called.
When onTouchListener and onClickListener objects are set at the same time, it is precisely because the
dispatchTouchEvent method of View will first call the onTouch of mOnTouchListener before calling the onTouchEvent method,
so the onTouch method of the onTouchListener object is called prior to the onClick method of the onClickListener object
. Here we only briefly describe the conclusion. For specific source code, please see the corresponding advanced content of this article.
Section: dispatchTouchEvent method
Looking back at the dispatchTouchEvent methods in Activity, ViewGroup and View above, they can be roughly divided into
There are two parts. The former part is handled by the dispatchTouchEvent method or onTouch method of the sub-View, and the latter
part is handled by its own onTouchEvent method. If you understand it this way, it will be very easy to remember.
In order to facilitate memory and understanding, the dispatchTouchEvent method of each component can be divided into two parts: the
dispatchTouchEvent or onTouch method of the sub-View and
its own onTouchEvent method
. This structure is somewhat similar to the recursive process, that is, the dispatchTouchEvent of the component will use the method of the same name of the sub-component.
The sub-component will also call the method of the same name of the sub-sub component until the recursion reaches the end, and then returns to the upper level from the bottom of the recursion until it
returns to the top level, and the whole process ends. Or during this process, the event is passed to a sub-View.
If the sub-View decides to handle the event, the event will be handled by its own onTouchEvent method. If the onTouchEvent method
cannot handle it, it will be handled by the method of the same name of the parent component until Pass up to the top to finish.
As a result, there are many U-shaped diagrams in tutorials.

U-shaped diagram of Android distribution events
From the U-shaped diagram, we can find that the main idea of ​​Android event distribution is actually very simple, that is, the parent component continuously
distributes to the child components, and if the child component can handle it, it returns immediately. If none of the subcomponents are processed, then it is passed to the underlying subcomponent and then returned
. This process is similar to the recursive process mentioned above.
Here is an explanation of this U-shaped diagram. First look at the upper left corner of the picture. The event is passed to Activity. First, its
dispatchTouchEvent method is called. It will be passed to the sub-View for processing. The sub-View (ViewGroup in the diagram) will
call its dispatchTouchEvent method, if this method is overridden and returns TRUE directly, it will return to Activity immediately, indicating that
the event has been consumed. If the method has not been overridden or the method of the same name of super is called,
the onInterceptTouchEvent method will be called. If the method returns TRUE to intercept the event, it will be handed over to its own onTouchEvent
for processing. If the method returns FALSE and does not intercept, it will continue to be passed to the child. The dispatchTouchEvent method of View (View in the picture)
is processed. At this point, look at the U-shaped diagram again. The recursive call has ended. If the
onTouchListener method in this method does not handle it, its own onTouchEvent will be called. If it still cannot be handled,
return upward from the bottom of the recursion and call the ViewGroup's and Activity's onTouchEvent methods in sequence.
In fact, using this U-shaped diagram to describe Android's event distribution mechanism is not necessarily accurate, because the
dispatchTouchEvent method of the same object actually contains several other methods (Activity and View only contain onTouchEvent),
but in this diagram, However, several methods are drawn in different boxes.
So it is inaccurate to understand the event distribution strategy through this U-shaped diagram . But it may be helpful to some readers. To accurately understand the event calling mechanism, you should
go back to the above and look at the core logic of the three core methods to understand accurately.
It is emphasized that the 'upward' and 'downward' propagation of Android event distribution should not be confused with concepts such as the relationship between base classes and subclasses in object-oriented programming languages, or subclasses
calling parent class methods upwards. Regarding the 'upward' and 'downward' propagation of Android event distribution, the upper and lower here
refer to the upper and lower in the 'recursive' calling process (also reflected in the upper and lower in the U-shaped diagram). This concept, reflected in the layout, is outside and inside. That is to say, the 'downward' propagation of events mentioned here is equivalent to the propagation from the outside to the inside in the layout,
and the 'upward' propagation is equivalent to the propagation in the layout from the inside to the outside.
In object-oriented programming languages, for subclasses to override parent class methods, or for subclasses to call parent class methods, these 'upper' and 'lower'
relationships cross the layout level at the layout level and should not be related to event propagation. The concept of direction is confused.
Interception method onInterceptTouchEvent
This method is unique to ViewGroup class objects and is used to intercept events in advance. Under normal circumstances, this method
returns FALSE by default, that is, it does not intercept.
If the custom ViewGroup wants to intercept the event and does not want the event to continue to propagate to the sub-View, you can override this method and
return TRUE to prevent the downward propagation process.
In fact, as can be seen from the pseudocode of the core logic above, after the ViewGroup calls dispatchTouchEvent,
this method will definitely be called, and how to handle it will be determined based on the return value of the method. If this method returns True, the event will be
intercepted and processed by its own onTouchEvent. If False is returned, continue passing it to the child to execute the distribution process.
Processing method onTouchEvent
This method mainly processes events. If it returns True, it means that the event has been processed. If it returns False, it means that the
event has not been processed and the event needs to continue to be delivered. Normally, the default is FALSE. In the View's onTouchEvent
method, if the onClickListener listening object is set, its onClick method will be called.

6. Summary

This article introduces the basic concepts of event distribution and introduces the core methods responsible for participating in event distribution, including
dispatchTouchEvent(), onInterceptTouchEvent and onTouchEvent methods. The core logic of these methods is introduced in the form of pseudo code
, focusing on the analysis of the dispatchTouchEvent method in Activity, ViewGroup and View
. The method structure among the three of them is similar. They first call the method of the same name or the listener method of the sub-View, and then
call its own onTouchEvent method.
These methods embody a similar 'recursive' calling process in the calling relationship,
passing the event down through dispatchTouchEvent, and passing the event up through onTouchEvent. This intermediate process can be done by letting
the onInterceptTouchEvent method (for ViewGroup) or another method responsible for distribution return TRUE, which
can terminate this 'recursive' calling process in advance, so that the event processing meets our expectations.

 

 

Guess you like

Origin blog.csdn.net/loveseal518/article/details/131968638