Android事件分发机制(源码分析二)

ViewGroup进行事件分发的过程中,多次把事件传递给了子View,开始View的事件分发。那么,View的事件分发如何进行?

关于View事件分发的几个问题:

1. View进行事件分发的目的?

确定事件能否被消费,以及响应事件具体的类型。

2. View什么情况下会消费事件?

(1)View设置的OnTouchListener,返回true。

(2)重写View的onTouchEvent()方法时,返回true。

(3)View为可点击状态,默认消费事件。例如Button,CheckBox,ImageButton等。

3. View进行事件响应的顺序?

OnTouchListener –> onTouchEvent –> OnLongClickListener –> OnClickListener。

View的事件分发

View的事件分发主要涉及两个方法:

(1)public boolean dispatchTouchEvent(MotionEvent event)

(2)public boolean onTouchEvent(MotionEvent event)

从dispatchTouchEvent方法开始分析:

1. 检测是否设置OnTouchListener

ListenerInfo li = mListenerInfo;

if(li != null && li.mOnTouchListener != null

        && (mViewFlags & ENABLED_MASK) == ENABLED

        && li.mOnTouchListener.onTouch(this, event)) {

    result = true;

}

先检测View是否有设置OnTouchListener,如果有设置,调用监听器的回调方法。回调方法返回true,消费事件,即dispatchTouchEvent()方法的返回值为true,相当于告诉ViewGroup,当前事件被消费了。

2. 调用View自身的onTouchEvent()方法

if(!result && onTouchEvent(event)) {

    result = true;

}

View未设置OnTouchListener,或OnTouchListener中onTouch()回调方法返回值为false,则继续调用View自身的onTouchEvent()方法。如果onTouchEvent()方法返回true,即消费了事件,返回false,未消费事件。

事件传递给onTouchEvent()方法后,需要在其中确认事件类型的响应顺序,下面进入这个方法:

(1)判断View使能

if((viewFlags & ENABLED_MASK) == DISABLED) {

if(action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) !=0) {

        setPressed(false);

    }

// A disabled view thatisclickable still consumes the touch

// events, it just doesn't respond to them.

    return (((viewFlags & CLICKABLE) == CLICKABLE

            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)

            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);

}

View为DISABLED,被禁用,且不可点击时,直接返回false,告诉ViewGroup,自己没有消费事件。但如果被禁用,却是可点击的(点击,长点击等),依然会返回true。这种情况下View什么也没做,依然把事件消费了。

(2)View未被禁用,继续处理事

if(clickable || (viewFlags & TOOLTIP) == TOOLTIP) {

//省略

return true;

}

只要View是clickable,或者悬停、长按时可显示工具提示,就会返回true,消费事件。

(3)关于点击和长按事件的处理

if(!mHasPerformedLongPress && !mIgnoreNextUpEvent) {

// Thisisa tap, so remove the longpress check

    removeLongPressCallback();

// Only perform take click actionsifwe wereinthe pressed state

if(!focusTaken) {

// Use a Runnableandpost this rather than calling

        // performClick directly. This lets other visual state

        // of the view update before click actions start.

if(mPerformClick == null) {

            mPerformClick = new PerformClick();

        }

if(!post(mPerformClick)) {

            performClick();

        }

    }

}

在手指抬起,View收到ACTION_UP时,先检测是否是长按。如果不是长按,则执行removeLongPressCallback(),移除长按的CheckForLongPress对象,系统不会对长按进行响应。否则,执行performClick(),开始调用点击事件监听器中的回调方法,进行事件响应。

总结

ViewGroup拿到事件后,先调用了自己的onInterceptTouchEvent()判断是否要进行拦截。如果拦截,则会将ViewGroup当成View进行事件分发。不拦截,则遍历其中的子View,挨个询问里面的子View有没有要消费事件的。消费,则事件传递结束,不消费,传递给下一个View,都不消费,则又传回到ViewGroup,把自己当成View进行事件分发。在ViewGroup当成View进行事件分发的时候,若没有消进行消费,这次事件被丢弃。事件传递的整个过程看起来像一个回路,是一个经典的责任链式设计模式。

你可能感兴趣的:(Android事件分发机制(源码分析二))