从源码角度分析事件分发机制

Android的事件分发其实就是MotionEvent的事件的分发过程,即当一个MotionEvent产生以后,系统需要把这个事件传递给一个具体的View,这个传递过程就是分发过程。MotionEvent的分发过程由三个重要的方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。

当一个点击事件产生后,它的传递过程遵循如下顺序:Activity -> Window -> View

那么我们就先看下Activity中的dispatchTouchEvent方法吧!

public boolean dispatchTouchEvent(MotionEvent ev) {    
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {        
        onUserInteraction();    
    }    
    if (getWindow().superDispatchTouchEvent(ev)) {        
        return true;    
    }    
    return onTouchEvent(ev);
}

onUserInteraction();该方法是空方法,需要自己实现。
getWindow().superDispatchTouchEvent(ev)从这个方法开始分发,但是你点击进去会发现该方法是抽象方法,所以我们需要查看它的实现类,从Window的代码注释可以看到,该类只存在一个PhoneWindow实现类,那么我们去看看PhoneWindow的superDispatchTouchEvent方法

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {    
    return mDecor.superDispatchTouchEvent(event);
}

该方法调用了DecorView的superDispatchTouchEvent方法,DecorView这个类继承了FrameLayout,由此可见调用的其实就是ViewGroup中的dispatchTouchEvent方法,那么我们就来看看其中的具体实现吧

// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {    
    // Throw away all previous state when starting a new touch gesture.    
    // The framework may have dropped the up or cancel event for the previous gesture    
    // due to an app switch, ANR, or some other state change. 
    cancelAndClearTouchTargets(ev);    
    resetTouchState();
}

方法一进来我们就可以看到每次ACTION_DOWN的时候源码会重置触摸状态和清空触摸目标,注意:变量mFirstTouchTarget就是用来保存触摸目标的

// Check for interception.final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {    
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;    
    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;
}

这段代码我们看到是处理中断事件的,具体逻辑是先判断MotionEvent的行为是否为ACTION_DOWN或者mFirstTouchTarget是否不为空,然后判断mGroupFlags是否被设置,该值的设置由子View调用requestDisallowInterceptTouchEvent方法以请求父View不允许中断事件分发

@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {        
        // We're already in this state, assume our ancestors are too       
         return;    
    }    
    if (disallowIntercept) {        
        mGroupFlags |= FLAG_DISALLOW_INTERCEPT;   
    } else {        
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;   
     }    
    // Pass it up to our parent    
    if (mParent != null) {
        mParent.requestDisallowInterceptTouchEvent(disallowIntercept);   
    }
}

接着查看源码,如果本view不中断事件,那么intercepted就返回false,就执行以下代码

if (!canceled && !intercepted) {
    ......
    final View[] children = mChildren;
    for (int i = childrenCount - 1; i >= 0; i--) {
        ......
        final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
        final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
        ......
        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {    
            // 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();    
            newTouchTarget = addTouchTarget(child, idBitsToAssign);    
            alreadyDispatchedToNewTouchTarget = true;    
            break;
        }
        ......
}

这段代码中首先遍历子View,然后调用dispatchTransformedTouchEvent方法去向子类分发MotionEvent,分发完成并且返回true的话,就调用addTouchTarget方法为mFirstTouchTarget赋值,注意:这里首次为mFirstTouchTarget赋值,之前都是为空的

执行完以上代码接着执行以下代码

// Dispatch to touch targets.
if (mFirstTouchTarget == null) {    
    // No touch targets so treat this as an ordinary view.    
    handled = dispatchTransformedTouchEvent(ev, canceled, null,            TouchTarget.ALL_POINTER_IDS);
} else {
    ......
}

可见如果之前intercepted为true,也就是当前View中断了事件的分发,那么就会调用dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);注意参数null,其实和以上intercepted为false执行的子View分发调用的方法一样,只是传递的参数不同,那么让我们看看dispatchTransformedTouchEvent方法的内部实现

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,        View child, int desiredPointerIdBits) {
    ......
    final int oldAction = event.getAction();
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {        
        event.setAction(MotionEvent.ACTION_CANCEL);    
        if (child == null) {        
            handled = super.dispatchTouchEvent(event);    
        } else {        
            handled = child.dispatchTouchEvent(event);    
        }    
        event.setAction(oldAction);    
        return handled;
    }
    ......
}

由此可见mFirstTouchTarget为空的时候就调用了View的dispatchTouchEvent方法

那么我们接着看View中的dispatchTouchEvent方法的具体实现

public boolean dispatchTouchEvent(MotionEvent event) {

    ......
    ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) {    
        result = true;
    }
    if (!result && onTouchEvent(event)) {    
        result = true;
    }
    ......
    return result;
}

从源码可见,它判断了是否有设置触摸监听器,如果有就调用它的触摸监听器,如果没有设置,就调用onTouchEvent方法,onTouchEvent方法中我们发现当触摸行为为ACTION_UP的时候会执行onClick方法(如果有设置的前提下)

到此事件分发就结束了,我们来总结一下

1,View是没有中断事件的,一旦传递给它那么就回调用onTouchEvent事件(没有设置触摸监听器的前提下)
2,一旦ACTION_DOWN的时候onInterceptTouchEvent返回true,那么后续的MOVE和UP等事件就不会向下分发,直到下一次ACTION_DOWN的到来(也就是下一次点击事件)
3,如果都没有处理事件,也就是所有都返回false,那么最终会调用Activity的onTouchEvent方法

个人愚见,如有错误,欢迎指正

你可能感兴趣的:(从源码角度分析事件分发机制)