View的事件分发机制

来源:互联网 发布:中信超市软件 编辑:程序博客网 时间:2024/06/15 00:05

  View负责android应用的显示,有很重要的地位,而理解View的事件分发机制是实现高质量自定义View和解决滑动冲突问题的基础,既是重点,也是难点。
  本文首先介绍View事件分发机制的流程,然后总结出View事件分发机制的一些结论,最后分析源码知道这些结论是怎么总结来的。


流程

  所谓点击事件的事件分发,其实就是对MotionEvent事件的分发过程。当手指点击屏幕时,就会产生一个MotionEvent,然后系统需要把这个事件传递给一个具体的View,这个传递的过程,就是事件分发。
  事件分发流程:
  首先,系统会将事件分发给Activity的dispatchTouchEvent(ev)方法,在dispatchTouchEvent(ev)方法中,默认实现下,Activity会把事件分发给Window,Window再把事件分发给DectorView,DectorView接着把事件分发给顶层View,顶层View接着把事件分发给各级子View。如果有View消费了事件,dispatchTouchEvent(ev)会返回true并返回,表示已消费了事件。如果没有View消费事件,dispatchTouchEvent(ev)方法会把事件传递给Activity的onTouchEvent(ev)方法。如果重写dispatchTouchEvent(ev)方法并返回true或false,事件就不再传递给Window及各级View,而直接在Activity的dispatchTouchEvent(ev)中消费。
  如果事件传递给了ViewGroup,就会调用它的dispatchTouchEvent(ev)方法。默认实现下,它会询问自身的onInterceptTouchEvent(ev)方法是否拦截事件。如果拦截,首先,它会查看是否设置有touchListener,如果设置有,就调用onTouch(ev)方法,如果onTouch(ev)方法返回true,表示已消费事件,它就返回true,表示事件已在本ViewGroup消费,如果onTouch(ev)方法返回false,它就调用自身的onTouchEvent(ev)方法消费事件。如果不拦截,它就把事件传递给子View的dispatchTouchEvent(ev)方法。如果重写dispatchTouchEvent(ev)并返回true,表示直接在这个方法中消费事件,事件不再传递。如果重写并返回false,表示不在本View及子View中消费事件,事件会返回给上级,一般情况下,上级会在dispatchTouchEvent(ev)方法中把事件传递给onTouchEvent(ev)处理。
  如果事件传递给了View,就会调用它的dispatchTouchEvent(ev)方法。默认情况下,它会查看是否设置有touchListener,如果设置有,就调用onTouch(ev)方法,之后与ViewGroup相同,如果onTouch(ev)返回true,就不调用onTouchEvent,如果返回false,就调用。如果重写dispatchTouchEvent(ev)并返回true,表示直接在这个方法中消费事件,事件不再传递。如果重写并返回false,表示不在本View中消费事件,事件会返回给上级,一般情况下,上级会在dispatchTouchEvent(ev)方法中把事件传递给onTouchEvent(ev)处理。
  当事件传递给View或ViewGroup的onTouchEvent时,如果返回true,表示事件在这里消费,事件停止传递,如果返回false或super,表示事件不在这里消费,事件会被返回给上级处理。super中的默认实现会在ACTION_UP事件发生时会调用performClick,在这个方法中会检查是否设置有点击事件监听器,如果设置有,就会调用onClick事件。
  事件分发流程图如下:
View事件分发流程图


结论

  • 1 Activity的onTouchEvent默认实现返回false。View中onTouchEvent默认实现返回true,ViewGroup中onTouchEvent继承自View。默认返回true表明事件默认会在最子层View上消费。
  • 2 在View和ViewGroup中,优先级onTouchListener > onTouchEvent > onClick。dispatchTouchEvent先检查是否设置有onTouchListener,如果设置有,就调用onTouch。如果onTouch返回false,才调用onTouchEvent。在onTouchEvent中在ACTION_UP时,调用performClick方法检查是否设置有onClickListener,如果有,就调用onClick方法。
  • 3 onClick会发生的前提是,onTouchEvent被调用,且调用了默认实现,且收到了UP事件,且view是可点击的(clickable & ebabled)。
  • 4 View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longclickable同时为false)。View的longClickable属性默认为false,clickable属性要分情况,如Button的clickable属性默认为true,而TextView的clickable属性默认为false。View的enable属性不影响onTouchEvent的默认返回值。哪怕一个View是disable状态的,只要它的clickable或者longClickable有一个为true,那么它的onTouchEvent就返回true。
  • 5 ACTION_DOWN事件的传递如上图所示。ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传,如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递,如果ACTION_DOWN事件是在onTouchEvent消费的,那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。如下图所示。
    MOVE和UP事件传递1

MOVE和UP事件传递2

MOVE和UP事件传递3

  • 6 public boolean dispatchTouchEvent(MotionEvent ev); 如果事件传递到该view,那么此方法一定会被调用。返回结果受当前view的onTouchEvent和下级view的dispatchTouchEvent影响,表示是否消耗当前事件。重写并返回true,表示事件在此方法消费,不再传递。重写并返回false,表示事件不在此view及子view消费,事件会被上级处理。默认实现在Activity、ViewGroup、View中各不相同。
  • 7 ViewGroup的dispatchTouchEvent的默认实现中,首先会判断如果当前在DOWN事件,就重置DISALOW_INTERCEPT_FLAG,保证DOWN事件时,dispatchTouchEvent一定会调用onInterceptTouchEvent。然后判断是否拦截事件,如果是DOWN事件或DOWN事件已在子View中消费,就判断是否允许调用onInterceptTouchEvent,如果不允许,表示不能拦截事件。如果不是DOWN事件,且没有子View消费了事件,就直接拦截事件,且不再调用onInterceptTouchEvent方法。Flag在requestDisallowIntercept方法中重置,该方法在子view中调用。该方法生效的前提是ViewGroup的onInterceptTouchEvent中没有拦截DOWN事件而拦截了MOVE和UP事件,因为如果拦截了DOWN事件,子view就不会再接收到任何事件,而不拦截DOWN事件,在子view中就可以接收到down事件,并在这时调用requestDisallowIntercept,禁止viewGroup

源码

0 0
原创粉丝点击