从源码角度理解Android View的事件分发机制(二)

开个头

在上一篇我们已经把一个事件从Activity–>PhoneWindow–>DecorView–>ViewGroup这个过程,通过源码详细分析了一波。
这一篇,我们主要是通过源码,来分析一个事件通过ViewGroup的dispatchTouchEvent(ev)方法,传到View的dispatchTouchEvent(ev)方法之后的过程。(我们就先假设ViewGroup里面都是View,没有ViewGroup。因为如果一个ViewGroup里面还有ViewGroup那么他之后的过程,和他父ViewGroup传给他的过程是一样的)

接着上一篇,事件的传递规则

现在那个ACTION_DOWN事件通过下面的代码传到了View。也就是传递给了View.dispatchTouchEvent(ev)方法。

 //通过这个方法把ViewGroup吧事件传了 child的dispatchTouchEvent(ev)
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled; 
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispa tchTouchEvent(event);
            } else {
                //因为是通过ViewGroup遍历过来的,所以child肯定不为null,就执行了这个方法,把事件传到了child里面
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }
        ...
    }   

好,接下里,话不多说,直接View.dispatchTouchEvent(ev)的源码,我们还是只看关键代码。

android.view.View
//看着很简单嘛,代码很少。
public boolean dispatchTouchEvent(MotionEvent event) {

        boolean result = false;
        //这个我们就先默认他是true,直接进去看里面的代码。
        if (onFilterTouchEventForSecurity(event)) {
            ListenerInfo li = mListenerInfo;
            //这个if语句应该看的很清楚了。
            //主要判断了,1.当前的View是否设置了OnTouchListener,2.并且onTouch()方法是否返回true
            //如果以上两个条件都成立了,那么result=true;
            //如果以上两个条件有一个不成立,那么result=false;
            //很简单接着往下看。
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            //如果上面的两个条件都成立,那么result=true,这个if不会走,也就说,不会执行View的onTouchEvent(ev)
            //这个结论在上一篇我们有提到,在这里我们就给出了解释。
            //result=true了, 那么就到最后的return result;了,也就是ViewGroup的dispatchTouchEvent()
            //方法也返回true了。到此ACTION_DOWN就结束了,这个事件由ouTouch方法处理,由当前的View消耗了。

            //如果上面条件不成立,就是要么没有设置OnTouchListener要么,设置了但是onTouch()返回false了。
            //那么result=false,这个时候才会走onTouchEvent(event)方法。
            //然后就看onTouchEvent(event)的返回值了。
            //如果返回true,那么result也为true。表示ACTION_DOWN事件被View的onTouchEvent(event)方法处理了。
            //如果返回false,那么result也为false。此时就是告诉ViewGroup的dispatchTouchEvent()方法,
            //我这个View不处理事件,你另请高明吧,看看其他View有人处理嘛。

            //注意这里的处理不处理都只是一个标志而已,并不代表你是否真正处理了事件。比如说我onTouchEvent(event)
            //就是返回了false(不处理),但是我里面还是根据ACTION_DOWN做了其他事情。或者我就是返回true,但是我的
            //onTouchEvent(event)里面是空代码,什么也没有,直接return true。
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        ...
        return result;
    }

通过上面的分析我们验证了,上一篇说到的,OnTouchListener的优先级是比onTouchEvent(ev)方法高的。也看到了,如果没有设置OnTouchListener,所有的判断都集中在onTouchEvent(ev)方法里。
所以直接上onTouchEvent(ev)的源码。

    public boolean onTouchEvent(MotionEvent event) {
        //首先判断这个View是否可点击 CLICKABLE ,是否可长按点击 LONG_CLICKABLE
        //如果有一个成立 clickable都为true。否则为false。
        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
        //接下来判断了这个View是否是 DISABLED状态。
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            //这里我们可以看到。即使View当前处于DISABLED状态,只要View可点击和可长按点击
            //这里都return了true。也就是说,DISABLED状态也消耗了事件,只是没有对事件做任何处理。
            return clickable;
        }
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    //先简答的理解下.
                    //ACTION_DWON传进来后,就是告诉这个View 那个地方被按压了。并且return true
                    setPressed(true, x, y); 
                    break;
                case MotionEvent.ACTION_UP:
                    //还记得我们上一篇说是用点击事件举例嘛。一个ACITON_DOWN和一个ACTION_UP
                    //当ACTION_UP事件也通过上面的流程传进来后。执行了这个方法
                    //这个方法其实是告诉OnClickListener,有个点击事件。 然后return true
                    //在下面可以看下performClick()的源码
                     performClick();
                    break;
                case MotionEvent.ACTION_CANCEL:

                    break;
                case MotionEvent.ACTION_MOVE:

                    break;
            }
            return true;
        }
        return false;
    }

这个时候有人可能会说,那我每次想监听点击事件的时候不都得要先设置clickable和long_clickable嘛。这个时候,我们还是用源码来说话。

    //可以看到,不管是设置单击事件还是长按事件,android源码都会为我们自动setClickable(true)和
    //setLongClickable(true),所以我们大可不必过分在乎之前监听点击事件的时候,是否设置过这两个属性。
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }
        public void setOnLongClickListener(@Nullable OnLongClickListener l) {
        if (!isLongClickable()) {
            setLongClickable(true);
        }
        getListenerInfo().mOnLongClickListener = l;
    }
    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        //判断当前View是否设置了OnClickListener。
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            //如果设置了OnClickListener那么直接回调onClick()方法。
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        notifyEnterOrExitForAutoFillIfNeeded(true);
        return result;
    }

OK,至此,一个点击事件的分发和处理,我们就通过源码的方式分析完毕。

不过,事件分发不仅如此,还有更深入的问题等着我们探讨。

如有问题,欢迎指正。

猜你喜欢

转载自blog.csdn.net/xy4_android/article/details/80460761