Do you really understand Android event distribution yet?

Primer

Android event distribution is actually nothing new, but to be honest, I think a lot of people just know about it, ambiguous. The purpose of this paper is once again combing from source-level look, focus on dispatchTouchEvent ViewGroup method, this method is the core of the core distribution in the event! We take this to Seeing, understanding the mechanism of distribution event. ps, this paper focuses on the source code and analysis, not how the drawing (in fact lazy), you can see the picture online, just search a lot.

Briefly talk about the source of the event distribution

A lot of people talk about the event distribution, said that beginning from dispatchTouchEvent Activity start, we can be so simple to understand, but will certainly be some doubt, Activity of this method is to call it from where? I wrote a simple Demo, then dispatchTouchEvent method Activity of Riga a breakpoint get its function call stack, see figure below:

stack.png

Dude, the original Activity before there are so many distribution process, sort out a bit simple: probably from InputEventReceiver, through the ViewRootImpl, after which various InputStage calls, finally gave DecorView, then DecorView passed the Activity. In fact, this is very interesting, had DecorView to get to the event, but it was later assigned to the Activity, Activity and after the event by phoneWindow back to the DecorView, one to one back, just to make about events Activity to deal with it . After Activity pass DecorView, DecorView calls the superDispatchTouchEventmethod:

    public boolean superDispatchTouchEvent(MotionEvent event){
        return super.dispatchTouchEvent(event);
    }

Because DecorView is a FrameLayout, it eventually calls the familiar dispatchTouchEvent ViewGroup of (), which is the protagonist of this article. The so-called event distribution, is essentially a function is called recursively, this recursive function is dispatchTouchEvent, as for onIntercepterTouchEvent, onTouchEvent, OnTouchListener, onClickListener ... balabala are operating in this recursive function inside of it, the core, the backbone of most or dispatchTouchEvent, so we have to analyze it:

ViewGroup event distribution

It should be more or less read its source, although the source is not too long, but at first glance still a big head, I think most people could probably understand its logic, for which a lot of stuff to know why. For example mFirstTouchTarget is doing? Temporary variables alreadyDispatchedToNewTouchTarget is doing? Which seems to have the list, ah, so that's why?

Here a little add that event to distribute it, from the user presses to lift, which is a set of events to ACTION_DOWN for the beginning or the end of the UP CANCEL. Our analysis of this group is behind the incident.

Source longer, I wrote pseudo-code for everyone to see, that is the pseudo-code, in fact, it is quite comprehensive and detailed, omitted part of the function parameters, but the focus of the code is contained, the focus to see the comment. If too long, you can directly look at the back of the conclusions, pseudo code look back.

//本源码来自 api 28,不同版本略有不同。
public boolean dispatchTouchEvent(MotionEvent ev) {
    // 第一步:处理拦截
   boolean intercepted;  
     // 注意这个条件,后面会讲
   if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
    // 子view调用了parent.requestDisallowInterceptTouchEvent干预父布局的拦截,不让它爸拦截它
       final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
       if (!disallowIntercept) {
             intercepted = onInterceptTouchEvent(ev);
             ev.setAction(action); 
         } else {
             intercepted = false;
         }
     } else {
        //既不是DOWN事件,mFirstTouchTarget还是null,这种情况挺常见:如果ViewGroup的所有的子View都不消费                //事件,那么当ACTION_MOVE等非DOWN事件到来时,都被拦截了。
         intercepted = true;
     }

    // 第二步,分发ACTION_DOWN
    boolean handled = false;
    boolean alreadyDispatchedToNewTouchTarget = false; //注意这个变量,会用到
   // 不拦截才会分发它,如果拦截了,就不分发ACTION_DOWN了
    if (!intercepted) {
        //处理DOWN事件,捕获第一个被触摸的mFirstTouchTarget,mFirstTouchTarget很重要,
        保存了消费了ACTION_DOWN事件的子view
        if (ev.getAction == MotionEvent.ACTION_DOWN) {
            //遍历所有子view(看源码知子View是按照Z轴排好序的)
            for (int i = childrenCount - 1; i >= 0; i--) {
                //子view如果:1.不包含事件坐标 2. 在动画  则跳过
                if (!isTransformedTouchPointInView() || !canViewReceivePointerEvents()) {
                    continue;
                }
                //将事件传递给子view的坐标空间,并且判断该子view是否消费这个触摸事件(分发Down事件)
                if (dispatchTransformedTouchEvent()) {
                    //将该view加入头节点,并且赋值给mFirstTouchTarget
                    newTouchTarget = addTouchTarget(child, idBitsToAssign);
                    alreadyDispatchedToNewTouchTarget = true;
                }

            }
        }
    }

        //第三步:分发非DOWN事件
        //如果没有子view捕获ACTION_DOWN,则交给本ViewGroup处理这个事件。我们看到,这里并没有判断是否拦截,
        //为什么呢?因为如果拦截的话,上面的代码不会执行,就会导致mFirstTouchTarget== null,于是就走下面第一                         //个条件里的逻辑了
        if (mFirstTouchTarget == null) {
            super.dispatchTouchEvent(ev); //调用View的dispatchTouchEvent,也就是自己处理
        } else {
            //遍历touchTargets链表,依次分发事件
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                if (alreadyDispatchedToNewTouchTarget) {
                  handled = true
                } else {
                    if (dispatchTransformedTouchEvent()) {
                      handled = true;
                    }
                  target = target.next;
                }
            }
        }

        //处理ACTION_UP和CANCEL,手指抬起来以后相关变量重置
        if (ev.getAction == MotionEvent.ACTION_UP) {
            reset();
        }
    }
    return handled;
}

To summarize: ViewGroup event distribution is divided into three steps

  1. The first step: to determine whether or not to intercept: branch to see the conditions here, the outer layer of the judge sentences mean, or certainly interception, interception or may not, may not intercept if required to meet one of the following two conditions:

    1. DOWN event is an event.

    2. Non-DOWN events can be, but need to meet mFirstTouchTarget! = Null. What does this condition mean? DOWN means that before the incident, there is at least one child View capture (consumption) a DOWN event, which is meant for the group distribution event, there are sub-View willing to handle this event.

    In the case of a possible interception, interception we enter judgment process is simple: look at the child view there is no transfer parent.requestDisallowIntercept, if you call, do not intercept, if not come to onIntercepteTouchEvent method, whether interception return value is determined based on.

  2. Step 2: If no interceptions, distribute DOWN events: through all the sub-View, touch area to see if there are qualified consumer sub-view event, judgments based on two reasons: whether child View animation? And a touch point falls within the range of the sub-View. If the first two are met, the event will be distributed to sub-DOWN View, which raises an important step method: dispatchTransformedTouchEventThis method of doing live is the most important thing: distributed sub-view, that is to say, this approach was recursive call, interested students can read the source code of its own. In addition, this distribution method has a return value, if true, was mFirstTouchTarget assignment, otherwise the value is still null. This last step is one method, addTouchTarget, this method involves the construction of the list, the list of what it saved? In fact, for any position coordinates of an event, there may be more View on the screen contains the coordinates distribution event time will keep all of these distribution View all again, these are distributed View is saved to a list of them, easy traversing the back.

  3. The third step: distributing other events: first determine mFirstTouchTarget, if the former is null, explained step DOWN events no child consumed view, this case indicates that the ViewGroup children View not going to handle the event, this will naturally be handed over ViewGroup process itself, the code handed over to super.dispatchTouchEvent, which is called the parent View ViewGroup processing (onTouchEvent). If not null, description has to deal with sub-View event, enter the else statement, the distribution of the incident down. Sharp-eyed readers should see here, the second step would not have been distributed DOWN event yet, why should redistribute here again? Not repeat it, here to speak in front of another variable appearance, alreadyDispatchedToNewTouchTargetat the beginning of this variable in the pseudo-code in the second step mentioned, when the second step, there are sub-View event consumer, the variable will become true, now the third step will be to determine the value that, if true, would return directly handle = true, the event is no longer distributed. This avoids DOWN events are distributed twice. For other events, this variable is definitely false, it will go logic else's, for distribution.

In the concentrated look, add a little vernacular:

  public boolean dispatchTouchEvent(MotionEvent event) {

        boolean intercepted = false;
        if (DOWN 或者 DOWN的时候没有孩子想处理) {
            if (孩子不让拦截?) {
                intercepted = false;
            } else {
                intercepted = onIntercept();
            }
        } else {
          intercepted = true;
        }

        if (DOWN && !intercepted) {
            for (遍历孩子View) {
                if(如果该孩子能消费就给分发给它,如果它真消费了DOWN事件){
                    给mFirstTouchTarget赋值 ;
                    Down事件已经分发了;
                }
            }
        }

        if (mFirstTouchTarget == null) {
            孩子都不想消费,交给我自己处理吧;
        } else {
            while(遍历所有孩子,将事件分发下去) {
                if (DOWN事件已经分发了) {
                    return true;
                }else {
                    分发给子View,如果有人消费,返回true;
                }
            }
        }

    }

Here, we put ViewGroup event distribution finished, then analyze View of dispatchTouchEvent

View event distribution

View is very simple

public boolean dispatchTouchEvent(MotionEvent event) {
    boolean result = false;
    if (onTouchListener.onTouch()) {
        result = true;
    }
    if (!result && onTouchEvent()) {
        result = true;
    }
    return result;
}

Visible, first determine the listener, returns true if a listener, onTouchEvent not enter, otherwise, go onTouchEvent method.

Point of View is a complex place in the default implementation of the method in onTouchEvent, which dealt with a lot onClick, logic onLongclick events, interested students may read their own source code, just say here that, once set onClickListener or onLongclickListener, it will onTouchEvent returns true, that is the consumer, the consumer does not default in other cases, source code written in such a

        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

clickable is true, returns true, otherwise false.

For example to practice

The problem is simple, a middle FrameLayout put a button, Framelayout and buttons have been added click event, then click the button to ask the regional event and click the button other than the distributing process like?

Look beyond the button: FrameLayout is a ViewGroup, and does not override dispatchTouchEvent method. based on the above analysis:

  • The first step, down to the future, into the interception logic, framelayout not intercepted, intercepted == false
  • The second step, processing down event, found no sub-point touch view, so no one will deal with this incident, mFirstTouchTarget == null
  • The third step is to deal with themselves, their calls onTouchEvent, where the provision of the clickListener, returns true, consumer event.
  • And the subsequent move up, due mFirstTouchTarget == null, the first step will be to intercept, so directly to their own treatment, with the third step above, at the same time, up time will respond to click events.

Button within:

  • The first step, ibid.
  • The second step, the touch point has found the child view, mFirstTouchTarget! = Null, and the event will be distributed to the sub-DOWN View.
  • The third step, mFirstTouchTarget non-null, but alreadyDispatchedToNewTouchTargetthis variable is true, a true return directly.
  • And the subsequent move up, the first step will not block, because it is not down event so skip the second step, the third step will be distributed to the sub-View event, child View responded to the click event, returns true, and this process, ViewGroup do not consume any event, it is natural that does not respond to onClick event.

In this way, the response is not to interpret results when the two are added click View event a ~

to sum up

Used, the event distribution in two steps, interception and distribution, the distribution of which there are two cases, events and non-Down
Down event, down event is the beginning of the chain of events, the decision whether or not to consume the event, it will affect all non-subsequent distribution down the event, if the event is not down consumption, will make mFirstTouchTarget is null, all events will no longer be distributed to the rear of the sub-view, directly from this view group process.
Feel more and more the importance of reading the source code, Let's read the fucking sourceCode!

Guess you like

Origin www.cnblogs.com/jymblog/p/12178527.html