自定义View 点击事件处理

570 阅读3分钟

点击事件的分发相关方法

  1. dispatchTouchEvent

        该方法主要就是管理点击事件是否继续向下分发,如果方法返回true,那么事件传递就结束了。如果其包含子view,     子view的dispatchTouchEvent也不会收到事件。ACTION_DOWN,ACTION_MOVE和ACTION_UP都会运行到当前view的dispatchTouchEvent而停止。

  2. onInterceptTouchEvent

        该方法主要是用于拦截事件,不要让事件到达子view上,保证事件从当前view作为最后一个view来处理。方法返回true后,子view的相关方法都不会被调用,后续的点击事件都会到该控件中执行。     如果子View已经处理了ACTION_DOWN,那么这个时候子View会收到一次ACTION_CANCEL事件。

  3. onTouchEvent

        方法用于处理点击事件,该方法返回true后,表示点击事件在当前view处理,后续的ACTION_MOVE和ACTION_UP 都会到这里来执行。返回false则会交给父控件处理。

关于各个方法设置为true的不同场景

View点击事件分发1.png

View点击事件分发2.png

View点击事件分发3.png

View点击事件分发4.png

View点击事件分发5.png

总结

  1. dispatchTouchEvent中的ACTION_DOWN事件重要性最高,只要针对他返回了true,ACTION_MOVE和ACTION_UP都不会跑到子view中,且如果dispatchTouchEvent中没有对这两个事件做限制, 那么就会调用该控件的onTouchEvent方法处理事件,同时会跳过其父布局的onTouchEvent,直接调用Activity的onTouchEvent方法
  2. 对于viewGroup拦截了dispatchTouchEvent,那么接下来如果当前控件不处理onTouchEvent,那么会直接回到Activity中执行.
  3. 父控件的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触发时候,被调用触发。

总结下:

  1. onTouchListener如果被设置了,那么会最先执行,每个ACTION事件都会执行一次,且执行了就不会继续执行onTouchEvent方法以及onClickListener回调了。
  2. onCLickListener在onTouchEvent中ACTION_UP触发时候执行。