欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 事件分发机制

程序员文章站 2022-06-17 22:38:43
1.事件分发机制事件分发简单而言就是点击事件怎么从Activity流转到ViewGroup,然后流转到View,最后这个点击事件有谁处理,怎么处理?时间分发有三个重要的方法,dispatchTouchEvent(分发事件的方法),onInterceptTouchEvent(拦截事件的方法),onTouchEvent(处理事件的方法)搞清楚Activity,ViewGroup和View的这三个方法,事件分发的机制也就了解了,咱们一个类一个类的进行讲解,首先深呼吸放松一下,然后进入代码的世界,大家慢慢的看。...

1.事件分发机制
事件分发简单而言就是点击事件怎么从Activity流转到ViewGroup,然后流转到View,最后这个点击事件有谁处理,怎么处理?时间分发有三个重要的方法,dispatchTouchEvent(分发事件的方法),onInterceptTouchEvent(拦截事件的方法),onTouchEvent(处理事件的方法)搞清楚Activity,ViewGroup和View的这三个方法,事件分发的机制也就了解了,咱们一个类一个类的进行讲解,首先深呼吸放松一下,然后进入代码的世界,大家慢慢的看。

2.事件分发u型图(网络流行的一个图,能详细反应这个事件分发的流程,我这里直接搬过来了)

Android 事件分发机制

3.Activity层的处理

//很简单捕捉点击向下按下的事件,然后执行个空方法,然后判断Window是不是对事件进行了处理有处理Activity就不管了,没有处理得对事件进行处理
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

3.ViewGroup层的处理

①dispatchTouchEvent方法

//处理的结果
boolean handled = false;
//伪代码实现 mFirstTouchTarget 点击事件的处理绑定的View 子布局
//只有是第一次触摸的ACTION_DOWN 按下的瞬间或者 有子的布局处理这个事件的时候 会对事件判断是否拦截
if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    //FLAG_DISALLOW_INTERCEPT 通过这个标志判断是否对他进行拦截
    if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); // restore action in case it was changed
    } else {
        intercepted = false;
    }
} else {
    //只要进入到ViewGroup的 dispatchTouchEvent 这个方法证明上次事件处理是在ViewGroup里面执行的OnTouchEvent返回的true
    //所有默认是拦截的,因为如果dispatchTouchEvent 返回的是false,根本不会到ViewGroup的dispatchTouchEvent方法
    intercepted = true;
}

//当当前不拦截的时候回循环遍历每个子View 判断他们对事件是不是进行了处理,在不在这个区域
if(!intercepted){
    if (action == MotionEvent.ACTION_DOWN) {
        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);
            //判断点击的区域是不是在View 的范围内
            if (!child.canReceivePointerEvents()
                    || !isTransformedTouchPointInView(x, y, child, null)) {
                ev.setTargetAccessibilityFocus(false);
                continue;
            }

            //在点击区域内调用 实际调用的是 子View的dispatchTouchEvent
            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                //判断是不是子View已经处理过这个事件
                alreadyDispatchedToNewTouchTarget = true;
                break;
            }
        }
    }
}

//伪代码
if (mFirstTouchTarget == null) {
    // 真实的代码 最后真实调用的onTouchEvent(ev);
    handled = dispatchTransformedTouchEvent(ev, canceled, null,
          TouchTarget.ALL_POINTER_IDS);
} else {
    //子View处理
    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
        //在之前是不是已经让子View 处理过了
        handled = true;
    } else {
        //子View 处理一下
        handled=mFirstTouchTarget.child.dispatchTouchEvent(ev);
    }
}

总结:当点一次点击event是down事件的时候判断ViewGroup是不是对事件进行拦截,如果进行的话进入ViewGroup的OnTouchEvent();如果不进行拦截进入ViewGroup的dispatch方法里面,当event事件为move的时候首先会判断有没有子View 处理这个时间,有的话,还得判断是不是拦截事件,然后依次执行,如果没有子view拦截的时候直接进入自己的OnTouchEvent(前提是会进入ViewGroup的Dispatch方法,进入前提是ViewGroup或者子View会对事件进行处理,如果都没有处理,在ViewGroup的父布局直接就会返回)

②onInterceptTouchEvent拦截方法

是否对事件进行拦截,拦截的话ViewGroup的onTouchEvent对事件进行处理,不拦截的话会调用子view的dispatchTouchEvent

3.View层的处理

①dispatchTouchEvent方法

boolean result;
//首先用ToucheLister判断 对事件处理与否,如果不处理才会调用自身的onTouchEvent
if(mOnTouchListener !=null && ENABLE
        && mOnTouchListener.onTouch(this, event)){
    result = true;
}
if (!result && onTouchEvent(event)) {
    result = true;
}

return result;

②onTouchEvent方法

//是否可以点击CLICKABLE(是否可以点)  LONG_CLICKABLE(是否可以长按)  
    // CONTEXT_CLICKABLE每个子控件的这几个默认的属性不一样
    //具体的得具体分析
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    //控件的状态是Disable的时候不会执行下面的OnClick事件
    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        return clickable;
    }
    //如果是可以点击的状态 直接返回true 对这个事件进行消耗
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                //真实调用的是onClick事件
                performClickInternal();
                break;

        }
        return true;
}

3.总结

上面我对整个事件分发机制整体进行了概括,有的细节比较简略,如果有什么遗漏的重点内容,大家可以在下面进行评论,谢谢大家!

本文地址:https://blog.csdn.net/u010939317/article/details/107430746