点击事件的分发相关方法
-
dispatchTouchEvent
该方法主要就是管理点击事件是否继续向下分发,如果方法返回true,那么事件传递就结束了。如果其包含子view, 子view的dispatchTouchEvent也不会收到事件。ACTION_DOWN,ACTION_MOVE和ACTION_UP都会运行到当前view的dispatchTouchEvent而停止。
-
onInterceptTouchEvent
该方法主要是用于拦截事件,不要让事件到达子view上,保证事件从当前view作为最后一个view来处理。方法返回true后,子view的相关方法都不会被调用,后续的点击事件都会到该控件中执行。 如果子View已经处理了ACTION_DOWN,那么这个时候子View会收到一次ACTION_CANCEL事件。
-
onTouchEvent
方法用于处理点击事件,该方法返回true后,表示点击事件在当前view处理,后续的ACTION_MOVE和ACTION_UP 都会到这里来执行。返回false则会交给父控件处理。
关于各个方法设置为true的不同场景
总结
- dispatchTouchEvent中的ACTION_DOWN事件重要性最高,只要针对他返回了true,ACTION_MOVE和ACTION_UP都不会跑到子view中,且如果dispatchTouchEvent中没有对这两个事件做限制, 那么就会调用该控件的onTouchEvent方法处理事件,同时会跳过其父布局的onTouchEvent,直接调用Activity的onTouchEvent方法
- 对于viewGroup拦截了dispatchTouchEvent,那么接下来如果当前控件不处理onTouchEvent,那么会直接回到Activity中执行.
- 父控件的onInterceptTouchEvent为true后,只会触发一次,后续不再触发该方法。
思考
为什么父控件的 onInterceptTouchEvent为true后,后续就不会再触发呢?
因为当父控件onInterceptTouchEvent为true后,onTouchEvent方法肯定会有地方被执行,所以事件就到不了子控件了,后续这个拦截也就没意义了,所以不会再判断了。
如果我子控件中,想要某次点击事件,让父控件不执行onInterceptTouchEvent进行拦截怎么处理呢?
子控件可以调用方法 requestDisallowInterceptTouchEvent,来让父控件当前这次事件,不执行其onInterceptTouchEvent方法来拦截事件
ACTION_CANCEL什么时候诞生
当子view处理了ACTION_DOWN事件后,后续的ACTION_MOVE或ACTION_UP被父控件拦截,那么这个时候父控件会传给子VIew一次ACTION_CANCEL,以便子控件结束VIEW相关点击事件操作。
onTouchEvent,onTouchListener和onClickListener的执行顺序
//noinspection SimplifiableIfStatement
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;
}
由这段源码可以知道,onTouchListener优先于onTouchEvent执行,且执行完后,onTouchEvent不会再执行,是阻塞的
public boolean onTouchEvent(MotionEvent event) {
......
switch (action) {
case MotionEvent.ACTION\_UP:
......
performClick();
......
break;
......
}
onLicklisterner在onTouchEvent方法中,当ACTION_UP触发时候,被调用触发。
总结下:
- onTouchListener如果被设置了,那么会最先执行,每个ACTION事件都会执行一次,且执行了就不会继续执行onTouchEvent方法以及onClickListener回调了。
- onCLickListener在onTouchEvent中ACTION_UP触发时候执行。