Android中事件分发机制是每个Android开发者必须掌握的知识,现在从源码分析,真正掌握Android事件分发机制,内容有点多,请细心仔细看,一定有收获。

一、事件对象(MotionEvent)

  当手触摸手机屏幕开始,Android会产生事件对象即:MotionEvent,MotionEvent类中封装一系列手势行为:ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTION_CANCEL、ACTION_OUTSIDE、ACTION_POINTER_DOWN、ACTION_POINTER_UP、ACTION_HOVER_MOVE、ACTION_SCROLL等等,但平常分析使用大概只有ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTION_CANCEL这几个,当手在手机屏幕上从按下到抬起中间,产生一 些系列手势行为动作构成一次事件列即:

  ACTION_DOWN->N个CTION_MOVE->ACTION_UP。事件对象产生后开始分发。

二、事件向下分发

2.1、事件从Activity专递到ViewGroup

当事件对象(MotionEvent)产生后,Android开始进行分发,从哪里开始分发的呢?大家都知道,Android中每个界面是Activity(Fragment)开始的,所以肯定要Activity分析。那么,是调用Activity那个方法开始分发的呢?这里要提到事件分发的三个重要的方法:dispatchTouchEvent()、onTouchEvent()、onInterceptTouchEvent()。

(1)、dispatchTouchEvent():用来进行事件的分发。如果事件能够传递到当前View,那个此方法一定会被调用,返回返回结果受当前onTouchEvent()方法和下一级的dispatchToucEvent()方法影响,表示是否消费此事件,返回true说明此事件已被消费掉,后续事件会继续传递给该View,返回false说明事件没被消费,后续事件不会被分发。该方法在ViewGroup、View及Activity中都有。

(2)、onTouchEvent():该方法在dispatchTouchEvent()方法内部调用,用来处理事件,返回结果表示是否消费事件,如果ACTION_DOWN不消费,即:返回为false,则同一系列中的其他事件不会传递到方法。事件回传到父一级级别的dispatchTouchEvent()方法,而父一级别的dispatchTouchEvent()内部调用onTouchEvent()方法,所以回传到父一级别的onTouchEvent()方法内,依次类推,到DecorView,如果DecorView也不消费事件,最后交由Activity的onTouchEvent()方法处理,不管此时Activity的onTouchEvent()方法返回何值;该方法在ViewGroup、View及Activity中都有。

(3)、onIntercepterTouchEvent()方法表示是否进行拦截事件,只在ViewGroup中有,默认返回false,表示不进行拦截,事件想下分发,在dispatchTouchEvent()方法内部调用,返回为true,表示拦截,当前ViewGroup想拦截自己处理,如果当前ViewGroup拦截同一事件系列中的某一事件,同一事件系列后续事件不会再进入该方法内,交由当前ViewGroup的onTouchEvent()方法处理。上文提到了Activity,当事件分发时,从Activity的dispatchTouchEvent()方法开始,如图一所示:                                                                                        

图一

从Activity中的dispatchTouchEvent方法内部可知,内部调用了getWindow().superDispatchTouchEvent()方法,getWindow()返回Window接口,Android实现Window接口唯一类是PhoneWindow类,如图二所示:

图二

从图二所示window.superDispatchTouchEvent()内部调用了mDecor.superDispatchTouchEvent()方法,而mDecor是DecorView(不知DecorView请自行百度)的实例对象,而DecorView继承FrameLayout,而DecorView的superDispatchTouchEvent内部调用了super.dispatchTouchEvent()方法,即:ViewGroup的disaptchTouchEvent方法,从这个分析的过程可知:事件从Activity传递到ViewGroup

2.2、事件再从ViewGroup传递到View

从上文可知,事件从Activity传递到了ViewGroup,继续分析ViewGroup的dispatchTouchEvent方法,如图三所示:

图三

图三中2175行中调用了onInterceptTouchEvent(ev)方法判断是否进行拦截事件,onInterceptTouchEvent(ev)默认返回false,不进行 拦截,则图中intercepted变量为false,2200行中if语句为true,ViewGroup开始从前到后开始遍历子View,找到能接受事件的View,如图四中2227行所示:

图四

遍历每个子View,调用dispatchTransformedTouchEvent()方法,如图五所示:

图五

ViewGroup遍历每个子View,把每个子View交由dispatchTransformedTouchEvent()方法处理,再进入dispatchTransformedTouchEvent方法内部分析,如图六所示:

图六

从dispatchTransformedTouchEvent中可知,child不为空执行child.dipatchTouchEvent(),从而事件传递到了View,即:事件从ViewGroup传递到View。

从上文可知:事件从Activity分发到ViewGroup,再由ViewGroup遍历子View找到可消费事件的View,分发到View。

查看View的dispatchTouchEvent()方法,如图七所示:

图七

在图七中的10017行中的if语句中判断mOnTouchListener是否可空且view是否ENABLED且mOnTouchListener.onTouch()是否为true,如果mOnTouchListener不为空且View是ENABLED,执行onTouch()方法,从而可知onTouch的优先级比onTouchEvent高,如果onTouch返回true,则表明当前View消费事件,事件不会再往下传递,如果过onTouch返回false,则执行View的onTouchEvent方法,再看分析View.onTouchEvent()方法,如图八所示:


图八

当前View是可点击或是可长按执行switch语句,否则onTouchEvent会返回false,没有任何View消费事件,事件向上级回传。switch语句中进行了事件处理,其中switch中MotionEvent.ACTION_DOWN时checkForLongClick()检查是否进行设置longClick事件,在MotionEvent.ACTION_UP时检查了是否设置了Click事件,所以从View.onTouchEvent方法内部可知,View的onTouchEvent()方法的优先级大于LongClick事件,LongClick优先级大于Click事件,即:onTouch>onTouchEvent > LongClick > Click。

三、事件向上级回传

上文中从源码中分析了事件由Activity向ViewGroup分发,ViewGroup向View分发,直至找到消费事件的View;如果向下分发至最底层View,都没找到任何View消费事件,该如何处理呢?按照常规,从源码分析,即:ViewGroup的dispatchTouchEvent(),如图九所示:

图九

如图九所示:ViewGroup的dispatchTouchEvent方法内部中2342行中,没有任何View消费事件,即触发目标为空,则dispatchTransformedTouchEvent(ev, canceled, null,   TouchTarget.ALL_POINTER_IDS);null就是子View为空,如图十dispatchTransformedTouchEvent中源码所示:


图十

图十中2681中,child=null,执行super.dispatchTouchEvent()方法,即:View的dispatchTouchEvent()方法,又执行View的dispatchTouchEvent方法流程,View的dispatchTouchEvent方法内部调用了onTouchEvent方法,onTouchEvent方法决定了dispatchTouchEvent()的返回值。如果当前ViewGroup不消费事件,以此类推回传到上级,直至DecorVIew的superDispatchTouchEvent()方法,DecorView的superDispatchTouchEvent方法返回false,即最顶层View也不消费事件,最后PhoneWindow.superDispatchTouchEvent()方法返回false,如图十一所示:

图十一

phoneWindow.superDispatchTouchEvent()方法是在Acitivity中disaptchTouchEvent()方法中调用,如图十二所示:

图十二

图十二中getWindow().superDispatchTouchEvent()方法返回false,会执行Activity的onTouchEvent()方法,不知你还记否,刚开始事件分发是从Activity中的dispatchTouchEvent()方法开始,如果分发到最顶层还没有任何View消费事件,最后还是要回到Activity的dispatchTouchEvent()方法,最后在调用了onTouchEvent方法,或许这就中国人所说的善始善终,首尾呼应。

注:(1)当同一系列事件中某一事件不被消费,后续的其他事件直接回传到到Activity中dispatchTouchEvent()方法中。

     (2)同一系列事件中某一事件被拦截即:onInterceptTouchEvent()返回false,则后续的其他事件不在调用onInterceptTouchEvent()方法,只执行一次。

有错请纠正,不喜勿喷!!!不喜勿喷!!!不喜勿喷!!!

欢迎关注我:順之自然Han

更多相关文章

  1. android中设置全屏的方法
  2. Android笔记 - Android启动之Launcher启动
  3. Android之Handler用法总结
  4. [原创] Android(安卓)Activity onNewIntent() 详解
  5. 源码解析Android中AsyncTask的工作原理
  6. Android(安卓)UI线程和非UI线程
  7. 转:Android(安卓)AsyncTask
  8. Android解析XML-详尽
  9. Android(安卓)LineaerLayout中layout_margin失效不起作用的解决

随机推荐

  1. 实现底部导航栏中间凸起
  2. Android最新框架
  3. 关于android:layout_x 与 android:layout
  4. Android中style的使用
  5. Android启动流程分析(二) init进程的启动
  6. android EditText 全面阐述
  7. Android HTTP实例 发送请求和接收响应
  8. 如何删除Android自带的应用程序?
  9. Android开发从入门到精通
  10. Android Studio 错误:Invalid Android NDK