Android中view的事件分发机制

1、View的事件分发涉及到3个核心的方法:

1.1、第一个是dispatchtouchevent,这个方法是用来进行事件分发,如果事件能够传递给当前 view, 那么此方法一定被调用,它的返回值受当前view的ontouchevent和下级view的 dispatchevent的影响,表示是否消耗当前事件;
1.2、第二个方法是oninterceptouchevent,这个方法在 dispatchtouchevent方法内部调用,用来判断是否拦截某个事件,如果当前view拦截了某个事件 ,那么在同一事件序列中 ,此方法不会 再次被调用,返回的结果表示是否拦截当前事件;
1.3、第三个方法是ontouchevent,这个方法也是在dispatchtouchevent方法中调用,用来处理事件,返回的结果表示是否消耗当前事件,如果不消耗,同一 事件序列中 ,当前view无法再次接 受到此事件序列中的后续事件。

三个方法的联系可用一下伪代码生动描述

public boolean dispatchTouchEvent(MotionEvent event) {
  boolean consume = false;
  if(onInterceptTouchEvent(ev)) {
    consume = onTouchEvent(ev);
  } else {
    consume = child.dispatchTouchEvent(ev);
  }
  return consume;
}

2、具体过程

具体来说,对于一个根 viewgroup来说,事件产生后,首先会传递给它,这时它的dispatchtouchevent方法会被调用,如果这个viewgroup的interceptedtouchevent方法返回为 true,就表示它要拦截当前事件,那么它的 ontouchevent方法会被调用,去处理这个事件 ;
如果这个viewgroup的oninterceptouchevent方法返回为 false,就表示它不拦截当前事件, 这时当前事件就会继续传递给它的子view,接着子view的dispatchtouchevent方法就会被调用,如此往下,直至事件最终被处理。
当一个 view开始处理某个事件,如果它设置了ontouchlistener, 那么它的ontouch方法会被调用,如果ontouch返回为true,那么view的ontouchevent方法不会被调用,如果返回为false,当前view的ontouchevent方法会被调用,如果当前view还设置了onclicklistener,那么onclick也会被随后调用。

3、事件分发的一些结论

(1)、若 ViewGroup 拦截了一个半路的事件(如MOVE),该事件将会被系统变成一个CANCEL事件 & 传递给之前处理该事件的子View;
该事件不会再传递给ViewGroup的onTouchEvent();
只有再到来的事件才会传递到ViewGroup的onTouchEvent()。
(2)、ViewGroup的onInterceptTouchEvent()对事件返回了false,但后续的事件(MOVE、UP)依然会传递给它的onInterceptTouchEvent() ;只有在onInterceptTouchEvent方法返回为true,此方法将不会被调用。
(3)、某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent()返回了false),那么同一事件序列中的其他事件都不会再交给它处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent()会被调用。
(4)、ViewGroup默认不拦截事件。onInterceptTouchEvent()方法默认返回false。
(5)、View没有onInterceptTouchEvent()方法,一旦有事件传递给它,那么它的onTouchEvent()方法就会被调用。
(6)、View的onTouchevent()默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。
(7)、onClick会发生的前提是当前View是可点击的,并且它收到了down和up的事件。
(8)、当面对ACTION_DOWN事件时,ViewGroup总是会调用自己的onInterceptTouchEvent()方法来询问自己是否要拦截该事件。

以下是安卓艺术探索没说清楚的:

(9)、正常情况下,一个事件序列只能被一个View拦截并消耗。(异常情况对应第1条)
(10)某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话,异常情况对应第一条),并且它的onInterceptTouchEvent()不会再被调用。

(2)(3)证明代码
 public boolean dispatchTouchEvent(MotionEvent ev) {
             ..............
            //mFirstTouchTarget是ViewGroup中处理事件(return true)的子View
            //如果没有子View处理则mFirstTouchTarget=null,ViewGroup自己处理
       final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); // restore action in case it was changed
    } else {
        intercepted = false;
    }
} else {
    // There are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
  }
}

当事件是ACTION_DOWN或者有子View去处理这个事件时,父view的onInterceptTouchEvent方法才会去调用,(ViewGroup在两种情况下会去判断是否拦截事件(ACTION_DOWN || mFirstTouchTarget != null)),反过来说就是: 当当前事件是ACTION_MOVE和ACTION_UP事件时,如果子View没有处理down事件(mFirstTouchTarget==null),(第一种情况:是父view的onInterceptTouchEvent返回true,处理了事件;第一种情况:是子view没有消耗down事件。不管是哪种情况)
ViewGroup的onInterceptTouchEvent不会再被调用,后续事件都会默认交给父view处理;子view也无法接收到后续事件。

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