【Android 自定义 View】事件分发机制

事件分发流程

事件手机后最先传递给 Activity,然后依次向下传递:

Activity - > PhoneWindow - > DecorView - > ViewGroup - > ... - > View

如果没有任何View消费掉事件,那么这个事件会按照反方向回传,最终传回给Activity,如果最后 Activity 也没有处理,本次事件才会被抛弃:

Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View

ViewGroup#dispatchTouchEvent

ViewGroup 的事件分发流程可以用以下伪代码来概括

public boolean dispatchTouchEvent(MotionEvent event){
    //默认事件没有被消费
    boolean result  = false;
    //如果 ViewGroup 自身没有拦截事件,事件传递给子 View
    if(!onInterceptTouchEvent){
        result = child.dispatchTouchEvent(event);
    }
    //如果 ViewGroup 拦截了事件,或者没有拦截但是子 View 并没有消费事件,调用 ViewGroup 的 onTouchEvent 方法
    if(result){
        result = onTouchEvent(event);
    }
    //将事件的消费结果返回给父 View,父 View 根据返回值来调用自身的 onTouchEvent 方法,返回 true 表示事件被消费,返回 false 表示事件没有被消费
    return result;
}

ViewGroup#onInterceptTouchEvent

ViewGroup#onInterceptTouchEvent() 方法是默认返回 false,即 ViewGroup 默认不拦截任何事件,如果想要让 ViewGroup 拦截事件,那么应该在自定义的 ViewGroup 中重写这个方法。

ViewGroup#onTouchEvent

ViewGroup 没有重写 View 的 onTouchEvent 方法。默认情况下,View 是可点击的,返回 true,不可点击返回 false。

  • onInterceptTouchEvent 方法返回 true 表示拦截事件,返回 false 表示不拦截事件,onTouchEvent 返回 true 表示消费事件,返回 false 表示不消费事件,与是否使用了触摸事件没有关系;
  • dispatchTouchEvent 方法内部调用 onInterceptTouchEvent 方法和 onTouchEvent 方法,以及子 View 的 dispatchTouchEvent 方法,返回 true 通知父 View 事件已被消费,返回 false 通知父 View 事件没有被消费,交给父 View 处理。

View#dispatchTouchEvent

可以用如下伪代码来表示 View 的事件分发过程。

public boolean diapatchTouchEvent(MotionEvent event){
    if(mOnTouchListener.onTouch(this,event)){
        return true;
    }else if(onTouchEvent(event)){
        return true;
    }
    return false;
}

在 View 的 diaptchTouchEvent 方法中如果注册了 onTouchListener ,最先调用的是 onTouchListener 的 onTouch 方法, 如果 onTouchListenr 的 onTouch 方法返回 true,则 diaptchTouchEvent 方法返回 true,不再执行 onTouchEvent 方法;如果返回 false,则调用 View 的 onTouchEvent 方法,onTouchEvent 返回 true 则dispatchTouchEvent 方法返回 true,否则返回 false。

View#onTouchEvent

在 onTouchEvent 方法里面判断 View 是否是 clickable 或 long_clickable,如果是则回调 onLongClickListener 和 onClickListener 的 onClick 方法,onTouchEvent 返回 true,无论有没有注册 onLongClickListener 和 onClickListener,只要 View 是可点击的,不对 onTouchEvent 方法进行重写,默认就会返回 true,消费事件。

  • 不论有没有注册事件,只要 View 是可点击的就会对事件进行消费,只要给 View 注册了 onClickListener、onLongClickListener、onConotextClickListener 中的任何一个监听器,View 的状态一定会变为 clickable,onTouchEvent 方法返回 true。
  • 给 View 注册 onTouchListener 监听并不会影响 View 的可点击状态,只要 onTouchListener 不返回 true,就不会消费事件。
  • 当 ACTION_DOWN 事件发生时,经过上述事件分发流程之后某一 View 消费了该事件,则后续的 ACTION_MOVE、ACTION_UP 事件都由该 View 接收。

所以我们在自定义 View 时可以重写 ViewGroup 的 onInterceptTouchEvent 或者 View 的 onTouchEvent 方法处理触摸事件。或者给 View 注册 onTouchListener 监听,如果 onTouchListener 返回 false 不会影响原来的分发流程,返回了 true 则不会再执行 onTouchEvent
,因为 ViewGroup 重写了 dispatchTouchEvent 方法,onTouchListener 不会执行。很明显如果重写了 onTouchEvent 方法,onClickListener 等监听也将不会再执行,这个时候是否消费事件也不取决于是否是可点击的了,onTouchEvent 的返回结果由自己来定义。

参考:
(Android View 事件分发机制源码详解(View篇))[http://www.jianshu.com/p/1378b334ee85]
(安卓自定义View进阶-事件分发机制原理)[http://www.gcssloop.com/customview/dispatch-touchevent-theory]

你可能感兴趣的:(【Android 自定义 View】事件分发机制)