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()方法开始,如图一所示:
![](https://img.haomeiwen.com/i9195714/c2f2bc803527024a.png)
从Activity中的dispatchTouchEvent方法内部可知,内部调用了getWindow().superDispatchTouchEvent()方法,getWindow()返回Window接口,Android实现Window接口唯一类是PhoneWindow类,如图二所示:
![](https://img.haomeiwen.com/i9195714/f2f04aa5457c6a05.png)
从图二所示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方法,如图三所示:
![](https://img.haomeiwen.com/i9195714/571773b627de7f7b.png)
图三中2175行中调用了onInterceptTouchEvent(ev)方法判断是否进行拦截事件,onInterceptTouchEvent(ev)默认返回false,不进行 拦截,则图中intercepted变量为false,2200行中if语句为true,ViewGroup开始从前到后开始遍历子View,找到能接受事件的View,如图四中2227行所示:
![](https://img.haomeiwen.com/i9195714/9171672fecc62d8b.png)
遍历每个子View,调用dispatchTransformedTouchEvent()方法,如图五所示:
![](https://img.haomeiwen.com/i9195714/a70b0fb2d7e891ca.png)
ViewGroup遍历每个子View,把每个子View交由dispatchTransformedTouchEvent()方法处理,再进入dispatchTransformedTouchEvent方法内部分析,如图六所示:
![](https://img.haomeiwen.com/i9195714/f2f933f70d553a3d.png)
从dispatchTransformedTouchEvent中可知,child不为空执行child.dipatchTouchEvent(),从而事件传递到了View,即:事件从ViewGroup传递到View。
从上文可知:事件从Activity分发到ViewGroup,再由ViewGroup遍历子View找到可消费事件的View,分发到View。
查看View的dispatchTouchEvent()方法,如图七所示:
![](https://img.haomeiwen.com/i9195714/d145e733e14066b8.png)
在图七中的10017行中的if语句中判断mOnTouchListener是否可空且view是否ENABLED且mOnTouchListener.onTouch()是否为true,如果mOnTouchListener不为空且View是ENABLED,执行onTouch()方法,从而可知onTouch的优先级比onTouchEvent高,如果onTouch返回true,则表明当前View消费事件,事件不会再往下传递,如果过onTouch返回false,则执行View的onTouchEvent方法,再看分析View.onTouchEvent()方法,如图八所示:
![](https://img.haomeiwen.com/i9195714/4621e7122e3e8731.png)
当前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(),如图九所示:
![](https://img.haomeiwen.com/i9195714/fa28051be4243b3b.png)
如图九所示:ViewGroup的dispatchTouchEvent方法内部中2342行中,没有任何View消费事件,即触发目标为空,则dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);null就是子View为空,如图十dispatchTransformedTouchEvent中源码所示:
![](https://img.haomeiwen.com/i9195714/ce1cbf48c87c8e62.png)
图十中2681中,child=null,执行super.dispatchTouchEvent()方法,即:View的dispatchTouchEvent()方法,又执行View的dispatchTouchEvent方法流程,View的dispatchTouchEvent方法内部调用了onTouchEvent方法,onTouchEvent方法决定了dispatchTouchEvent()的返回值。如果当前ViewGroup不消费事件,以此类推回传到上级,直至DecorVIew的superDispatchTouchEvent()方法,DecorView的superDispatchTouchEvent方法返回false,即最顶层View也不消费事件,最后PhoneWindow.superDispatchTouchEvent()方法返回false,如图十一所示:
![](https://img.haomeiwen.com/i9195714/c5248a98eaf3f5c0.png)
phoneWindow.superDispatchTouchEvent()方法是在Acitivity中disaptchTouchEvent()方法中调用,如图十二所示:
![](https://img.haomeiwen.com/i9195714/d16a95d3a7e0cea4.png)
图十二中getWindow().superDispatchTouchEvent()方法返回false,会执行Activity的onTouchEvent()方法,不知你还记否,刚开始事件分发是从Activity中的dispatchTouchEvent()方法开始,如果分发到最顶层还没有任何View消费事件,最后还是要回到Activity的dispatchTouchEvent()方法,最后在调用了onTouchEvent方法,或许这就中国人所说的善始善终,首尾呼应。
注:(1)当同一系列事件中某一事件不被消费,后续的其他事件直接回传到到Activity中dispatchTouchEvent()方法中。
(2)同一系列事件中某一事件被拦截即:onInterceptTouchEvent()返回false,则后续的其他事件不在调用onInterceptTouchEvent()方法,只执行一次。
有错请纠正,不喜勿喷!!!不喜勿喷!!!不喜勿喷!!!
欢迎关注我:順之自然Han
网友评论