The Android event distribution mechanism is something that should be familiar to the beginning programmer. Once you understand the distribution mechanism, you will be better able to solve common problems such as sliding conflicts and how custom control events are distributed in real production. I’m going to take a quick look at this mechanic and hopefully help developers who aren’t familiar with the concept or aren’t impressed with it.

Responsibility chain model and its application

1. Why talk about the chain of responsibility model?

Why talk about the chain of responsibility model? Of course, Android’s event distribution mechanism is a shadow of the chain of responsibility model. Let’s first look at the definition of the chain of responsibility model:

Chain of responsibility mode is an object behavior mode. In the chain of responsibility pattern, many objects are connected in a chain by each object’s reference to its next parent. Requests pass along the chain until one of the objects on the chain decides to process the request.

Of course, the definition comes from Baidu, but as I understand it, the chain of responsibility model is a bit like a top-down management organization. When a position meets the work belonging to their own functions to complete, do not belong to their own work or can not handle the work on the downward (up) throw, to be able to deal with people to deal with. Of course, the specific definition is not fixed, but the general idea is this.

2. Simple application of responsibility chain mode

The following is the whole specific work, we are Boss,Manager,Staff from the top down mode, each position has its own scope of amount processing, encountered does not belong to their scope of work will be thrown, the following is a simple UML diagram:

To briefly explain the UML diagram, create an abstract handler, create a chain of objects through internal methods, define an abstract handler, and let the next subclass define the concrete implementation, so the final Code:

public abstract class Handler {

    // Create a chain of objects
    protected Handler mHandler;
    public Handler getHandler(a){
        return mHandler;
    }
    public void setHandler(Handler handler){
        this.mHandler = handler;
    }
    public abstract void doSomething(int money);

    // Static inner class, lazy
    public static class Boss extends Handler{

        @Override
        public void doSomething(int money) {
            if (money<1000){
                System.out.println("The amount is less than 1000, Boss ignored, downward distribution.");
                getHandler().doSomething(money);
            }else {
                System.out.println("Boss approval."); }}}public static class Manager extends Handler{

        @Override
        public void doSomething(int money) {
            if (money<100){
                System.out.println("The amount was less than 100, the manager ignored it and sent it down.");
                getHandler().doSomething(money);
            }else {
                System.out.println("Approved by the manager."); }}}public static class Staff extends Handler{

        @Override
        public void doSomething(int money) {
            if (money<0){
                System.out.println("Less than zero, logic has to be rewritten.");
            }else {
                System.out.println("Employee approval."); }}}}Copy the code
public class Main {
    public static void main(String[] args) {
        Handler.Boss boss = new Handler.Boss();
        Handler.Manager manager = new Handler.Manager();
        Handler.Staff staff = new Handler.Staff();
        boss.setHandler(manager);
        manager.setHandler(staff);

        int money = 50;
        System.out.println("Application Amount"+ money); boss.doSomething(money); }}Copy the code
To apply for the amount50Amount is less than1000, Boss ignore, send down. Amount is less than100The manager ignored it and sent it down. Employee approval.Copy the code

Finally, we can see that everyone can handle the work belonging to their own functions, and the use of responsibility chain mode can not only make each category perform its own duties, but more importantly, it makes the whole architecture dynamic. As you can see from the code above, when you want to add an intermediate position, you can easily create the corresponding class by inheriting the abstract class and defining the method concretely. Android’s event distribution mechanism uses a similar architecture, so the entire event flow and our custom View can handle event distribution, and then we focus on Android event distribution.

Android event distribution

1. What is the event of distribution?

As you can see from the previous example, the event distributed is actually the amount of money. Each job class “sends” the event to the corresponding class for processing according to the amount. But what is the event distributed by Android?

MotionEvent

What is a MotionEvent? MotionEvent is a collection of encapsulated events created by the system architecture for our input events, including single touch, multi-touch, mouse events, and so on. To make it easier to understand, when we use Android devices, input events such as touch and slide are carried out. The system architecture translates these real actions into specific motionevents. For example, when a finger just touches the screen, Hardware awareness turns into a MotionEvent called ACTION_DOWN through a series of transformations so that we can receive this event in our code and implement the corresponding logic. Therefore, events distributed by Android are essentially user-specific actions.

2. Levels of distribution

After knowing what the distribution event is, since the shadow of the Android event distribution responsibility chain model has been mentioned before, what is the general responsibility chain? The following is the hierarchy of online theft (due to the general idea is relatively simple, infringement obediently deleted) :

The hierarchy is Activity-> Window-> DecorView-> ViewGroup-> View. Regardless of the implementation details, the Android event distribution is similar to the previous example, the top down event distribution. Specific controls according to logic to achieve the corresponding functions, and finally achieve a complete event distribution mechanism. At this point, we understand what the event is, what the abstract chain of responsibility looks like, and finally we just need to focus on the concrete layer and look at the details.

3. Details of distribution

According to the figure above, the top of the responsibility chain seems to be the Activity. But how does an event happen? How is the chain of responsibility assembled? How do events get passed to Activiity? These three questions are a bit beyond the outline for the author. Here is a big boss of Amway, but he smells qing Mei. I believe that the big bosses have more or less been impressed.

Juejin. Cn/post / 684490…

Then how do all levels process and distribute events when all the prerequisites are available? Next, we’ll look at the ViewGroup hierarchy in more detail. If you understand the details of a ViewGroup, you’ll see other layers as well. Before we begin, let’s list three important functions:

public boolean dispatchTouchEvent(MotionEvent ev) If the event can be passed to the current level, it will be called. The result returned depends on whether the current View and the lower View consume the event
Copy the code
public boolean onInterceptTouchEvent(MotionEvent ev) If the current View intercepts the event, it will not be called again
Copy the code
public boolean onTouchEvent(MotionEvent ev) // is called in the dispatchTouchEvent method, and the return value indicates whether the current event is consumed. If not, subsequent sequences of similar events are not received
Copy the code

It would be unnatural to post the source code at the beginning, so let’s take our time to sort it out:

  1. The event gets passed to the ViewGroup, and as WE said before, whenever we get to the current level, we always call the dispatchTouchEvent method.
  2. Now that the event is coming in, we can choose to intercept it or not intercept it, if we don’t intercept it, it’s definitely false, and the event will be passed down to the next level; If we choose to intercept the event, onInterceptTouchEvent returns true, and the onTouchEvent method is then fired.
  3. At this point we can choose whether or not we consume the event, if we don’t consume it, it must be false, so our overall result is that we don’t consume the event, and the whole dispatchTouchEvent method returns false, which goes up one level; If we choose to consume the event, onTouchEvent returns true, and the overall result is true, still sending back the processing result.

The whole process can be simply understood as a top-down responsibility chain, which determines whether events are consumed layer by layer, and finally returns the event processing results layer by layer at the bottom. The classical recursion, pseudo-code is as follows:

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean result = false;
    if(onInterceptTouchEvent()){
        result = onTouchEvent();
    }else {
        result = child.dispatchTouchEvent();
    }
    return result;
}
Copy the code

Pseudocode will describe the above process is very clear, as long as you understand the idea of event distribution, the specific implementation is a headache gnaws source link (source code specification and optimization ideas are necessary to learn, necessary for advanced) :

public boolean dispatchTouchEvent(MotionEvent ev) {
	boolean handled = false; .final boolean intercepted;
    // Omit N steps, use the onInterceptTouchEvent method to determine whether to intercept, ViewGroup usually returns falseintercepted = onInterceptTouchEvent(ev); .// mFirstTouchTarget is used to record the underlying View, super.dispatchTouchEvent() if it is null;
   if (mFirstTouchTarget == null) {
       handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);
   }else{...// mFirstTouchTarget is not null and the event is distributedhandled = child.dispatchTouchEvent(event); }...return handled;
}
Copy the code

Above is only part of the source thumbnail description, you can see with the pseudo code before the thoughts were similar, the concrete implementation approach and realize the optimization, the reader can experience carefully read the source code itself, other levels of concrete implementation with ViewGroup although there are differences, but the whole idea is consistent, if want to know friends can refer to other analysis, At this point, the Android event distribution of the analysis has come to an end.

Origin and solution of sliding conflict

1. What is sliding conflict?

In fact, sliding conflict is reflected in sliding two words, can slide the control produced a conflict, so brought with the expected results. We usually slide horizontally or vertically. When controls are nested, we expect them to slide correctly, but the controls themselves do not know whether they should respond to the slide, as in the following cases:

Reverse conflicts are nested slides of two controls that slide in different ways. A “chaos” scenario may occur when sliding. For example, both controls are sliding. The control itself does not know when it should slide, which requires us to solve through the requirements logic, while the N-direction conflict is a mixture of the two situations, which can be broken down, there is no explanation here.

2. Resolve the fault based on the event distribution mechanism

From the above, slide conflicts seem to occur because different sliders do not know what to do when they are nested. Combined with the event distribution mechanism mentioned earlier, we have two solutions:

  1. The corresponding sliding event is intercepted at the corresponding level of control and consumed. (External interception method)
  2. The top level control does not intercept the event, the event is handed over to the sub-level, which then throws an unprocessed event to the upper level to resolve the sliding conflict. (Internal interception method)

Here’s an example: Scrollview nested RecyclerVIew implements the combination of top navigation bar and list

If Google did not officially adaptive sliding conflict in the control, the sliding experience is certainly not good, for example, there will be sliding, the navigation bar is sliding at the same time the list is sliding, or from the extent of it is to slide down, the navigation bar is sliding around.

Analysis problem: Since this example is relatively simple, according to our requirements, the navigation bar can only be swiped left and right, so the response of swiping up and down should be given

The Scrollview, in other words, the top layer of the Scrollview intercepts and handles all sliding events as follows:

private float mLastX = 0f;
private float mLastY = 0f;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    int action = ev.getAction();
    float x = ev.getX();
    float y = ev.getY();
    boolean intercept = false;
    switch (action){
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_UP:
            intercept = false;
            break;
        case MotionEvent.ACTION_MOVE:
            // Determine whether it is longitudinal or lateral sliding by displacement increment
            float mDeltaX = x - mLastX;
            float mDeltaY = y - mLastY;
            intercept = Math.abs(mDeltaX) < Math.abs(mDeltaY);
            break;
    }
    mLastX = x;
    mLastY = y;
    return  intercept;
    //return super.onInterceptTouchEvent(ev);
}
Copy the code

This is the way to resolve conflicts by intercepting events in the top-level control. As mentioned earlier, the top-level control can also be used not to intercept events, and give the sub-layer control to consume events, and throw unnecessary events to the top-level control. But internal intercept method need to use the sub-layer controls whether requestDisallowInterceptTouchEvent method is used to set the top-level control interception, the train of thought to solve the problem are similar, interested friends can go to look at, there is not much.

Finally, co-direction and mixed conflict are not explained in one way. As long as you understand the nature of event distribution and how to solve it through external interception and internal interception, these types of problems are copied and I believe everyone is already familiar with them.

Scrollview + RecyclerVIew is just a small example, to achieve this function must not use! At present there are NestedScrollView and other solutions, this piece of the author is not very understanding, cautious into the pit!