Android事件分发机制流程源码详解

Android上面的View是树形结构的,View可能会重叠在一起,当点击的地方有多个View可以响应的时候,这个点击事件应该给谁呢?为了解决这个问题,就有了事件分发机制。

一、概念

1、什么是事件?

Android中每一次用户与界面的交互,点击、长按、移动、抬起等都是一个事件
同时,把每一从触摸屏幕到离开屏幕称为一个事件序列。把前一个事件称为后一个事件的前驱事件。

MotionEvent:
在Android中,将每一次事件封装为一个MotionEvent对象,它具有不同的类型:
介绍开发中常用的四种类型。

ACTION_DOWN:表示手指接触到屏幕,当且仅当手指接触到屏幕(按下,未移动,未抬起)
ACTION_MOVE:手指在屏幕上进行移动
ACTION_UP:手指离开屏幕

ACTION_CANCEL:
该事件比较特殊,触发条件为当一个view接收到了前驱事件,但接下来的事件被父控件所拦截,此时,该view就会接收到一个ACTION_CANCEL事件。
例如:在scrollview中放置一个Button,当按下时,因为Button是默认可以点击的,所以Button会消耗掉ACTION_DOWN事件。当拖动他时,ACTION_MOVE事件会被他的父控件所拦截。此时Button就会接到一个ACTION_CANCEL事件

2、什么是事件分发?

每一个事件从屏幕上传递到各个view上,并由某个view来处理事件(消费事件)或忽略事件(不消费事件)的这一整个过程的控制。称为事件分发机制。

传递层级关系:
Activity --> Window --> DecorView --> ViewGroup --> View

Activity持有一个PhoneWindow对象,PhoneWindow是Window的唯一子类,每个PhoneWindow对象持有一个DecorView,DecorView继承自ViewGroup。

二、流程相关源码详解

1、Activity

流程图:

Activity中事件分发流程

Activity主流程部分源码:

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;
    }

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):
调用–>PhoneWindow中superDispatchTouchEvent(ev),PhoneWindow调用–>DecorView中superDispatchTouchEvent(ev),由于DecorView继承自ViewGroup。最终调用ViewGroup中的dispatchTouchEvent(ev)方法。

2、ViewGroup:

流程图:

ViewGroup事件分发流程

主流程部分源码:

ViewGroup:dispatchTouchEvent() 主要做三件事
1、去判断是否需要拦截事件
2、在当前viewGroup中找到用户真正点击的view
3、分发事件到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;
    }

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

流程图:

View中事件分发流程

主流程部分源码分析:

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;
    }

在view中做的事情:
1、判断是否有焦点
2、判断是否是鼠标事件
3、判断是否有OnTouchListener监听并调用监听接口onTouch方法,调用onTouchEvent方法,判断是否有OnClickListener监听并调用onClick。

注:只有在ViewGroup中才有onInterceptTouchEvent事件,Activity是事件开始分发不需要拦截,view是最后接到事件的也不需要拦截。

注:

getParent().requestDisallowInterceptTouchEvent(true)
//该方法可以控制父控件是否进行拦截,true为不拦截,false为拦截。其原理是控制是否调用onInterceptTouchEvent方法
发布了60 篇原创文章 · 获赞 25 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41466437/article/details/104778018
今日推荐