美文网首页
Android 事件分发流程分析

Android 事件分发流程分析

作者: 晴天12345 | 来源:发表于2017-11-07 00:08 被阅读16次

    1.1 dispatchTouchEvent onInterceptTouchEvent onTouchEvent 关系

    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if(onInterceptTouchEvent(ev)) { // 当前view拦截
            consume = onTouchEvent(ev); 
        } else { // 子view分发
            child.dispatchTouchEvent(ev);
        }
        return consume;
    }
    

    1.2 PhoneWindow分发到DecorView流程

    PhoneWindow2DecorView

    2.1 ViewGroup::dispatchTouchEvent

    dispatchTouchEvent(MotionEvent ev) {
    
        // 当ACTION_DOWN时,将要重置FLAG_DISALLOW_INTERCEPT标记位;
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // 发生ACTION_DOWN事件, 则取消并清除之前所有的触摸targets
            cancelAndClearTouchTargets(ev);
            resetTouchState(); // 重置触摸状态
        }
    
        ...
        // Check for interception.
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) { // mFirstTouchTarget 处理事件的子元素
            /*
                mFirstTouchTarget != null , 则由子元素处理,
                否则,需要由 当前group处理,
            */
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            // 滑动冲突 内部拦截法:通过改变 FLAG_DISALLOW_INTERCEPT 设置ViewGroup是否拦截事件;
            if (!disallowIntercept) {
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        } else {
            // There are no touch targets and this action is not an initial down
            // so this view group continues to intercept touches.
            intercepted = true;
        }
        ....
    
        // 当前viewgrop不拦截,且Action未取消
        if (!canceled && !intercepted) {
            ...
            // 遍历子元素分发
            for (int i = childrenCount - 1; i >= 0; i--) {
                final int childIndex = customOrder
                        ? getChildDrawingOrder(childrenCount, i) : i;
                final View child = (preorderedList == null)
                        ? children[childIndex] : preorderedList.get(childIndex);
        
                // 如果当前视图无法获取用户焦点,则跳过本次循环
                if (childWithAccessibilityFocus != null) {...}
        
                //如果view不可见,或者触摸的坐标点不在view的范围内,则跳过本次循环
                if (!canViewReceivePointerEvents(child)
                        || !isTransformedTouchPointInView(x, y, child, null)) {
                    ...
                }
        
                // 已经开始接收触摸事件,并退出整个循环
                newTouchTarget = getTouchTarget(child);
                if (newTouchTarget != null) {
                    ...
                }
                //重置取消或抬起标志位
                //如果触摸位置在child的区域内,则把事件分发给子View或ViewGroup
                resetCancelNextUpFlag(child);
                // 递归调用子view的dispatchTouchEvent
                if (dispatchTransformedTouchEvent(ev, false, child/*存在子元素*/, idBitsToAssign)) { //2.2
                    // Child wants to receive touch within its bounds.
                    mLastTouchDownTime = ev.getDownTime();
                    if (preorderedList != null) {
                        // childIndex points into presorted list, find original index
                        for (int j = 0; j < childrenCount; j++) {
                            if (children[childIndex] == mChildren[j]) {
                                mLastTouchDownIndex = j;
                                break;
                            }
                        }
                    } else {
                        mLastTouchDownIndex = childIndex;
                    }
                    mLastTouchDownX = ev.getX();
                    mLastTouchDownY = ev.getY();
                    //如果child处理事件,则添加到mFirstTouchTarget单链表
                    newTouchTarget = addTouchTarget(child, idBitsToAssign); //2.3
                    alreadyDispatchedToNewTouchTarget = true;
                    break;
                }
                ...
            }
            ...
            //
            if (mFirstTouchTarget == null) {
                //子view未消耗事件,则mFirstTouchTarget null, 由当前ViewGroup来处理
                handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); //2.2
            } else {
                ...
                if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
                    handled = true; // 2.2 target.child处理
                }
                ...
            }
            
        }
    
    
        if (mFirstTouchTarget == null) { // 无子元素处理事件时;
            handled = dispatchTransformedTouchEvent(ev, canceled, null/*会调用view.dispatchTouchEvent*/,  TouchTarget.ALL_POINTER_IDS); //2.2
        } else {
          ...//如果View消费ACTION_DOWN事件,那么MOVE,UP等事件相继开始执行
        }
    }
    

    2.2 ViewGroup::dispatchTransformedTouchEvent

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
                View child, int desiredPointerIdBits) {
    ...
        if (child == null) {
            handled = super.dispatchTouchEvent(event); // 返回父组件继续dispatchTouchEvent
            // 最终传递给View处理 参考 View 对事件的处理过程
            // 不存在子视图时,ViewGroup调用View.dispatchTouchEvent分发事件,再调用ViewGroup.onTouchEvent来处理事件
        } else {
            handled = child.dispatchTouchEvent(event); // 子节点 (View //2.4 ViewGroup//2.1) 分发事件 
        }
    ...
    }
    

    2.3 ViewGroup::addTouchTarget

    只有处理ActionDown事件,才会进入addTouchTarget, mFirstTouchTarget被赋值;

    private TouchTarget addTouchTarget(View child, int pointerIdBits) {
        TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target; // mFirstTouchTarget 获取 TouchTarget
        return target;
    }
    

    2.4 View::dispatchTouchEvent

    public boolean dispatchTouchEvent(MotionEvent event) {
    ...
        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) { //onTouch 优先处理
                result = true;
            }
    
            if (!result && onTouchEvent(event)) { //onTouchEvent
                result = true; // onTouchEvent
            }
    ...
    }
    

    2.5 ViewGroup::dispatchTouchEvent 概览

    ViewGroup_dispatchTouchEvent

    3.1 Android 滑动冲突

    3.1.1 外部拦截法

    viewgrop 中重写 onInterceptTouchEvent

    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean result = false;
        detector.onTouchEvent(ev); // 事件传递给 GestureDetector
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
    
                downX = ev.getX();
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 新坐标
                float newdownX = ev.getX();
                float newdownY = ev.getY();
    
                // 计算距离
                int distanceX = (int) Math.abs(newdownX - downX);
                int distanceY = (int) Math.abs(newdownY - downY);
    
                // distanceX 防止抖动
                if(distanceX > distanceY && distanceX > 10) // 根据dx dy判断水平 垂直方向滑动
                    result =  true;
                else
                    scrollToPager(currentIndex);
                break;
            case MotionEvent.ACTION_UP:
                break;
    
        }
        return result;
    }
    

    3.1.2 内部拦截法

    public boolean onTouchEvent(MotionEvent event) {
        getParent().requestDisallowInterceptTouchEvent(true); // 设置父组件(listview)不拦截事件
    }
    

    相关文章

      网友评论

          本文标题:Android 事件分发流程分析

          本文链接:https://www.haomeiwen.com/subject/svarmxtx.html