View事件分发机制

前言

· 在Android自定义View/ViewGroup中,我们免不了出现它的触摸事件,所以了解View的事件分发非常重要
· 本人菜鸡水平,写此文章为了加深印象,如果发现错误或不足恳请指正。谢谢

讲在前面

在事件分发过程中,我们首先要知道View的事件分发是要区分View和ViewGroup

来张图

View的事件分发

view事件分发中的三个重要方法:
1.dispatchTouchEvent()
2.onTouch()
3.onTouchEvent()

·我看首先看看事件在View是如何传递和消费的
DispatchTouchEvent()部分代码,也是核心代码

 boolean result = false;
 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;
       }

首先会判断View是否设置了触摸事件,如果设置了点击事件,并且并且当前View是可点击的,那么就会回调li.mOnTouchListener.onTouch(this, event)),如果此方法返回true,那么就不会调用onTouchEvent(event)这个方法了,说明这个触摸事件被消费了。如果返回false,那么就会回调onTouchEvent(event)这个方法,如果返回true,事件被销毁。返回false代表事件继续向下传递。由此可见执行优先级:onTouch() >onTouchEvent()。
然后我们看看onTouchEvent()这个方法
是switch case语句我们不分析,各种事件了。我们主要看看UP中:

  case MotionEvent.ACTION_UP:
        //......
              if (!focusTaken) {
                      if (mPerformClick == null) {
                              mPerformClick = new PerformClick();
                      }
                      if (!post(mPerformClick)) {
                              performClickInternal();
                      }
              }     
       //......
              break;

我们在打开performClickInternal看看,最后就是performClick()这个方法

public boolean performClick() {
        //......
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
        //......
        return result;
    }

通过源码我们可以看到,onClickListener.onClick()方法其实就是onTouchEvent中up事件中被调用,所以执行优先级:onTouchEvent() > onClick()。如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。View的事件分发就说到这。

ViewGroup的事件分发

view事件分发中的三个重要方法:
1.dispatchTouchEvent()
2.onInterceptTouchEvent()
3.onTouchEvent()

首先我们看看ViewGroup的dispatchTouchEvent()这个方法,源码太多,我们把核心的代码提出来

public boolean dispatchTouchEvent(MotionEvent ev){
  //......
      boolean handled = false;
      final boolean intercepted;
      intercepted=onInterceptTouchEvent(event);
      if(intercepted)
            handled =onTouchEvent(event)
      else{
         final View[] children = mChildren;
         for (int i = childrenCount - 1; i >= 0; i--) //找到当前触摸位置的childView
      handled = child.dispatchTouchEvent(event);
      }
  //......
      return handled;
}

这个就是ViewGroup的逻辑,首先先判断onInterceptTouchEvent(event)(默认为false)如果为true,则拦截这个事件。调用viewgroup的onTouchEvent(event)来判断是否消费事件。否则向下传递。遍历找到子View并把当前事件交给子View调用子View的dispatchTouchEvent(event)。

public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }

onInterceptTouchEvent()默认返回false,代表viewGroup不拦截事件
1.当事件传递到View时,如果View的onTouchEvent()返回false,则父类的onTouchEvent()会被调用,依次向上传递
2.若所有的View都不消耗事件时,Activity的onTouchEvent()会被调用

结论

1.对于View一旦决定拦截事件即onTouchEvent()返回true,那后续的整个事件序列都会交给它消耗;
2.执行优先级:onTouch() > onTouchEvent() > onClick()
3.事件的传递是
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
如果没有找到合适的View消费事件:那么事件反向传递如果最后 Activity 也没有处理,本次事件才会被抛弃
Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View


感谢阅读,欢迎点赞和评论

你可能感兴趣的:(View事件分发机制)