转载请注明出处:http://blog.csdn.net/chziroy/article/details/44401615

要理解Android事件分发机制,首先得了解几个概念,也算是总结,假设临时看不懂也无妨,本文会解说这几个问题。

1,点击屏幕,首先事件的传递从Activity的dispatchTouchEvent()方法開始。

2,关于Android事件分发机制,相关方法的方法有三个:onTouchEvent(),dispatchTouchEvent(),还有onInterceptTouchEvent(),而相关的类有Activity,View,ViewGroup。

3,时间的分发顺序为dispatchTouchEvent ---onInterceptTouchEvent ---onTouchEvent

4,Android事件分发机制,有一个向下分发过程,该过程主要调用dispatchTouchEvent,另一个向上返回过程,主要依靠onTouchEvent方法,

5,Android事件从父视图分发到子视图,假设事件被拦截,则事件不会继续向下分发,而被当前视图消耗。此时上述的向下分发过程提前结束

6,没有被消耗的事件,从父视图逐级分发到子视图,最后又回到Activity,被Activity中的onTouchEvent()消耗。此时上述的向上返回过程提前结束


本文不会直接贴出上述相关方法和类的源码,而会贴出其“伪代码”,方便理解。


dispatchTouchEvent方法

先从事件分发的起点開始,也就是Activity的dispatchTouchEvent()方法,

public boolean dispatchTouchEvent(MotionEvent ev) {        //第一步,将事件分发        //第二步,假设事件在分发中没被消耗,则传递给Activity的onTouchEvent()方法 }

上述代码的第一步“将事件分发”,那事件会分发到哪里呢,分发到它的根布局的一个ViewGroup(事实上就算你的activity的布局文件没有设置一个LinearLayout这种根布局,系统也会默认给你加一个默认的FrameLayout)。事件分发到了ViewGroup中,就进入了ViewGroup的dispatchTouchEvent 方法,在查看该方法的源代码时,Android3.0之前该方法的源代码和更高版本号中的源代码是不同的。只是原理大致同样。下面是该方法的原理。

public boolean dispatchTouchEvent(MotionEvent ev) { 调用onInterceptTouchEvent检查是否拦截事件 if(没有拦截){ 在ViewGroup中遍历查找眼下是点击了哪个子视图 if(找到了){ 调用该子视图的dispatchTouchEvent,递归下去 }else{ 没找到,则将事件传给onTouchListener,没有Listener则传给onTouchEvent() 假设再listener或者onTouchEvent()中down事件返回了true,代表事件被消费,兴许的move和up都被Listener或者onTouchEvent()处理, 假设down事件返回false,则兴许的move,up事件将不会到这一层的Viewgroup,而直接在上一层视图被消费。 }  }else{ 事件被拦截了,原本被点击的子视图将接收到一个ACTION_CANCEL事件,而down事件传给onTouchListener,没有Listener则传给onTouchEvent(),依旧遵从上面的down和move,up事件的关系 }}

上述代码中,关于dispatchTouchEvent 在子视图中的递归调用,假设子视图是ViewGroup,则依旧进入ViewGroup的dispatchTouchEvent 方法,和上述代码一样,假设子视图是View,则进入View的dispatchTouchEvent ,其代码和ViewGroup的代码不大同样,例如以下

public boolean dispatchTouchEvent(MotionEvent ev) {             //假设有listener,则把事件传递给listener     //假设没有listener,则把事件传递给onTouchEvent()}


onInterceptTouchEvent 方法

该方法仅仅在ViewGroup中出现,该源代码非常easy,并且其凝视非常简单扼要的描写叙述了该方法的原理,甚至也描写叙述了dispatchTouchEvent 的原理,例如以下
 /**     * Implement this method to intercept all touch screen motion events.  This     * allows you to watch events as they are dispatched to your children, and     * take ownership of the current gesture at any point.     *     * <p>Using this function takes some care, as it has a fairly complicated     * interaction with {@link View#onTouchEvent(MotionEvent)     * View.onTouchEvent(MotionEvent)}, and using it requires implementing     * that method as well as this one in the correct way.  Events will be     * received in the following order:     *     * <ol>     * <li> You will receive the down event here.     * <li> The down event will be handled either by a child of this view     * group, or given to your own onTouchEvent() method to handle; this means     * you should implement onTouchEvent() to return true, so you will     * continue to see the rest of the gesture (instead of looking for     * a parent view to handle it).  Also, by returning true from     * onTouchEvent(), you will not receive any following     * events in onInterceptTouchEvent() and all touch processing must     * happen in onTouchEvent() like normal.     * <li> For as long as you return false from this function, each following     * event (up to and including the final up) will be delivered first here     * and then to the target's onTouchEvent().     * <li> If you return true from here, you will not receive any     * following events: the target view will receive the same event but     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further     * events will be delivered to your onTouchEvent() method and no longer     * appear here.     * </ol>     *     * @param ev The motion event being dispatched down the hierarchy.     * @return Return true to steal motion events from the children and have     * them dispatched to this ViewGroup through onTouchEvent().     * The current target will receive an ACTION_CANCEL event, and no further     * messages will be delivered here.     */    public boolean onInterceptTouchEvent(MotionEvent ev) {        return false;    }

onTouchEvent方法

假设设定了onTouchListener,则不会进入View/ViewGroup的onTouchEvent(),而是进入onTouchListener。假设down事件在某个View/ViewGroup中返回true,则兴许的move和up事件将继续来到当前View/ViewGroup,否则,兴许的事件将去到父视图的onTouchListener或者onTouchEvent,所以假设全部onTouchListener或者onTouchEvent对全部事件都返回false,则终于事件将回到Activity的onTouchEvent,所以事实上Activity中该方法返回什么都似乎无所谓了。

总结

事实上事件分发机制,能够分为两个过程,一个是向下分发过程,一个是向上返回过程,当中向下分发是靠dispatchTouchEvent 方法,从Activity的dispatchTouchEvent 直到目标视图的dispatchTouchEvent,然后进入目标视图的onTouchEvent,向上返回则是靠onTouchEvent,从目标视图的onTouchEvent直到Activity的onTouchEvent。
而在向下分发的过程可能会提前结束,这受onInterceptTouchEvent影响,也可能是ViewGroup没有子视图了,在两个因素都能够使该过程提前结束,从而提前进入向上返回的过程。向上返回过程也可能提前结束,假设事件在onTouchEvent中返回true,即事件被消费,那么事件不会继续向上传递,该过程提前结束。


一言以蔽之:也就是“两个过程,两个截断


參考过的几篇比較优秀的博客

http://codetheory.in/understanding-android-input-touch-events/

http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html#top

https://gist.github.com/Leaking/16e682b1ffac3a59c3df

更多相关文章

  1. 两分钟彻底让你明白Android中onInterceptTouchEvent与onTouchEve
  2. Android(安卓)UI事件处理[isInTouchMode()]
  3. android SD卡文件监听
  4. Android中使用WebView, WebChromeClient和WebViewClient加载网页
  5. android测试工具大全
  6. Android事件分发机制
  7. Android(安卓)嵌套滑动机制(NestedScrolling)
  8. Android(安卓)3.0 r1 API中文文档(105) ―― ViewParent
  9. Android(安卓)API 中文 (53) ―― BaseAdapter

随机推荐

  1. Android(安卓)cursor query方法详解
  2. 《Android》Lesson02-第1个Project,Log
  3. Android(安卓)Client-side OAUTH
  4. Android(安卓)动画
  5. Android(安卓)Studio配置SVN
  6. Android(安卓)访问权限设置
  7. android edittext 边框 源码实现
  8. Android——扩大ImageButton的点击区域
  9. android 属性系统 及其 补充
  10. Android(安卓)Bluetooth 移植(上)