Detailed explanation of the source code of the Android event distribution mechanism process

The View on Android is a tree structure, and the Views may overlap. When there are multiple Views that can respond to the clicked place, who should this click event be? In order to solve this problem, there is an event distribution mechanism.

1. Concept

1. What is an event?

Every user interaction with the interface in Android, click, long press, move, lift, etc. is an event.
At the same time, every time from touching the screen to leaving the screen is called an event sequence . The previous event is called the predecessor of the next event.

MotionEvent:
In Android, each event is encapsulated as a MotionEvent object, which has different types:
four types commonly used in development are introduced.

ACTION_DOWN: indicates that the finger touches the screen, if and only if the finger touches the screen (pressed, not moved, not lifted)
ACTION_MOVE: the finger moves on the screen
ACTION_UP: the finger leaves the screen

ACTION_CANCEL:
This event is special. The trigger condition is when a view receives a predecessor event, but the next event is intercepted by the parent control. At this time, the view will receive an ACTION_CANCEL event.
For example: place a Button in the scrollview, when pressed, because the Button is clickable by default, the Button will consume the ACTION_DOWN event. When dragging him, the ACTION_MOVE event will be intercepted by his parent control. At this time Button will receive an ACTION_CANCEL event

2. What is event distribution?

Each event is transferred from the screen to each view, and a certain view controls the entire process of handling events (consumption events) or ignoring events (not consuming events). Called the event distribution mechanism.

Pass the hierarchical relationship:
Activity-> Window-> DecorView-> ViewGroup-> View

Activity holds a PhoneWindow object, PhoneWindow is the only subclass of Window, each PhoneWindow object holds a DecorView, DecorView inherits from ViewGroup.

Second, the detailed source code related to the process

1、Activity

flow chart:

Event distribution process in Activity

Source code of Activity main process part:

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();//该方法是一个空的方法,在每一个事件序列一开始会进行调用
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//从这里正式进入事件分发流程
            return true;//表示事件被消费:结束
        }
        return onTouchEvent(ev);//调用Activity的 onTouchEvent 方法并返回结果:结束
    }
    .....
    
     public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {//允许点击空白部分消失,且点击了window之外
            finish();
            return true;
        }

        return false;
    }

Part of the source code of the main process of Window:

//该方法在Window类里面,在Activity  onTouchEvent()方法里被调用
    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        final boolean isOutside =
                event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
                || event.getAction() == MotionEvent.ACTION_OUTSIDE;
        if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
        表示:支持点击空白部分消失、持有DecorView对象、点击的是当前window持有DecorView之外
            return true;
        }
        return false;
    }

getWindow (). superDispatchTouchEvent (ev):
call-> superDispatchTouchEvent (ev) in PhoneWindow, PhoneWindow call-> superDispatchTouchEvent (ev) in DecorView, because DecorView inherits from ViewGroup. Finally, the dispatchTouchEvent (ev) method in the ViewGroup is called.

2、ViewGroup:

flow chart:

ViewGroup event distribution process

Part of the source code of the main process:

ViewGroup: dispatchTouchEvent () mainly does three things
1. To judge whether the event needs to be intercepted
2. Find the view that the user actually clicked in the current viewGroup
3. Distribute the event to the view

    public boolean dispatchTouchEvent(MotionEvent ev) {
                   ...
                   
        //从这里开始
        boolean handled = false;//定义为整个方法返回值
        if (onFilterTouchEventForSecurity(ev)) {是否符合安全策略,该方法在view中
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;
            
            if (actionMasked == MotionEvent.ACTION_DOWN) {//down事件,全部事件的开始
            
            //当开始一个新的触摸手势时,扔掉所有以前的状态
                cancelAndClearTouchTargets(ev);//清除所有触摸事件
                resetTouchState();//重置状态
            }

            final boolean intercepted;//是否拦截的判断
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {//该值当事件被子view消费后被赋值
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);//一般情况下直接返回false,源码在下面
                    ev.setAction(action); // 恢复操作
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }
                      
                      ...
                      
            // 检查是否是取消事件
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
                    
                     ...
                     
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {//不是取消事件且不拦截
            //是按下事件
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                        
                    final int actionIndex = ev.getActionIndex(); // 事件索引
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;

                    //清除早期触摸事件
                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;//子view数量
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        // 获取所有能接收事件的子view.
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        //是否自定义view绘制顺序
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        //从前到后遍历子view
                        for (int i = childrenCount - 1; i >= 0; i--) {
                        //获取view的真实索引,解释在下方
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                                    //根据索引获取目标view
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                                         ...
                                         
                            //当前view是否能接受事件,当前view是否在该事件点击范围之内
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // 表示当前子view获取到了触摸事件
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                            
                            //在dispatchTransformedTouchEvent方法中进行下层view的分发
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                   
                                   .....
                                   
                                //一旦返回为true,则代表事件被子view消费
                                //addTouchTarget 在方法中为mFirstTouchTarget赋值
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                            ev.setTargetAccessibilityFocus(false);
                        }
                        //循环结束
                        if (preorderedList != null) preorderedList.clear();
                    }

                    .....
                }
            }
            
            

            if (mFirstTouchTarget == null) {
                // 依然没有子view去处理事件
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {//遍历
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;//直接返回
                    } else {//重新分发
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if (cancelChild) {
                            if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            } 
            
            .......
            
        }

        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;//最后返回
    }
    
    
    
    
    
    //是否拦截事件
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    //是鼠标事件且点击了左键,正常手机操作不会出现这样情况,故返回false
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }
    
    
    
    
    
    //获取子view的真实索引
    private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder) {
        final int childIndex;
        if (customOrder) {//是否自定义
        //该方法需要在自定义绘制顺序时去实现,否则默认返回i
            final int childIndex1 = getChildDrawingOrder(childrenCount, i);
            if (childIndex1 >= childrenCount) {
                throw new IndexOutOfBoundsException("getChildDrawingOrder() "
                        + "returned invalid index " + childIndex1
                        + " (child count is " + childrenCount + ")");
            }
            childIndex = childIndex1;
        } else {
            childIndex = i;
        }
        return childIndex;
    }
    
    
    
    
    
    //根据索引获取目标view
    private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
            int childIndex) {
        final View child;
        if (preorderedList != null) {
            child = preorderedList.get(childIndex);
            if (child == null) {
                throw new RuntimeException("Invalid preorderedList contained null child at index "
                        + childIndex);
            }
        } else {
            child = children[childIndex];
        }
        return child;
    }
    
    
    
    
    //进行下层view的分发,主流程相关部分代码
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        //是否是取消事件
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
            //调用的是View的dispatchTouchEvent
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }
        .........
        
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);
        }

        // 重置
        transformedEvent.recycle();
        return handled;
    }

Related methods in View:

//是否符合安全策略
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
        //表示接收此事件部分的窗口被完全遮挡,不处于顶部
        if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
            return false;
        }
        return true;
    }

3、View

flow chart:

View event distribution process

Source code analysis of the main process:

public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.isTargetAccessibilityFocus()) {
            // 没有焦点,不能获取到事件
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // 我们有焦点,得到事件,然后使用正常的事件调度。
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        ...

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // 停止滚动
            stopNestedScroll();
        }

        //以下是在view中事件分发的核心逻辑
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                //鼠标拖动
                result = true;
            }
            //
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                //注册过OnTouchListener监听,且调用的onTouch返回true
                //注:OnTouchListener监听在此时被调用
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                //没有注册过OnTouchListener监听或者onTouch返回false、调用onTouchEvent返回true
                //注:onTouchEvent在此时被调用
                result = true;
            }
        }

        ...

        return result;
    }
    
    


    //该方法中调用mOnClickListener
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

//是否可点击
        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            return clickable;//如果设置过禁止点击,直接返回可点击状态
        }

        //是否有点击事件代理
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {//内部为默认处理逻辑
                case MotionEvent.ACTION_UP:

        ...

                    if (!focusTaken) {

                        if (!post(mPerformClick)) {
                            performClickInternal();//在此方法中调用mOnClickListener监听
                        }
                    }


        ...
                    break;

        ...

            }

            return true;
        }

        return false;
    }

Things to do in the view:
1. Determine whether there is focus
2. Determine whether it is a mouse event
3. Determine whether there is OnTouchListener monitoring and call the onTouch method of the monitoring interface, call the onTouchEvent method, determine whether there is OnClickListener monitoring and call onClick.

Note: The onInterceptTouchEvent event is only available in the ViewGroup. Activity is the event that starts to be distributed and does not need to be intercepted. The view is the last one that receives the event and does not need to be intercepted.

Note:

getParent().requestDisallowInterceptTouchEvent(true)
//该方法可以控制父控件是否进行拦截,true为不拦截,false为拦截。其原理是控制是否调用onInterceptTouchEvent方法
Published 60 original articles · 25 praises · 10,000+ views

Guess you like

Origin blog.csdn.net/qq_41466437/article/details/104778018