Android事件分发机制

转载: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()方法,

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean dispatchTouchEvent(MotionEvent ev) {  
  2.         //第一步,将事件分发  
  3.         //第二步,如果事件在分发中没被消耗,则传递给Activity的onTouchEvent()方法  
  4.  }  

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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean dispatchTouchEvent(MotionEvent ev) {  
  2.      调用onInterceptTouchEvent检查是否拦截事件  
  3.      if(没有拦截){  
  4.          在ViewGroup中遍历查找目前是点击了哪个子视图  
  5.          if(找到了){  
  6.              调用该子视图的dispatchTouchEvent,递归下去  
  7.          }else{  
  8.              没找到,则将事件传给onTouchListener,没有Listener则传给onTouchEvent()  
  9.              如果再listener或者onTouchEvent()中down事件返回了true,代表事件被消费,后续的move和up都被Listener或者onTouchEvent()处理,  
  10.              如果down事件返回false,则后续的move,up事件将不会到这一层的Viewgroup,而直接在上一层视图被消费。  
  11.          }   
  12.      }else{  
  13.          事件被拦截了,原本被点击的子视图将接收到一个ACTION_CANCEL事件,而down事件传给onTouchListener,没有Listener则传给onTouchEvent(),依然遵从上面的down和move,up事件的关系  
  14.      }  
  15. }  

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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean dispatchTouchEvent(MotionEvent ev) {  
  2.             //如果有listener,则把事件传递给listener  
  3.      //如果没有listener,则把事件传递给onTouchEvent()  
  4. }  


onInterceptTouchEvent 方法

该方法只在ViewGroup中出现,该源码很容易,而且其注释非常简单扼要的描述了该方法的原理,甚至也描述了dispatchTouchEvent 的原理,如下
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     * Implement this method to intercept all touch screen motion events.  This 
  3.     * allows you to watch events as they are dispatched to your children, and 
  4.     * take ownership of the current gesture at any point. 
  5.     * 
  6.     * <p>Using this function takes some care, as it has a fairly complicated 
  7.     * interaction with {@link View#onTouchEvent(MotionEvent) 
  8.     * View.onTouchEvent(MotionEvent)}, and using it requires implementing 
  9.     * that method as well as this one in the correct way.  Events will be 
  10.     * received in the following order: 
  11.     * 
  12.     * <ol> 
  13.     * <li> You will receive the down event here. 
  14.     * <li> The down event will be handled either by a child of this view 
  15.     * group, or given to your own onTouchEvent() method to handle; this means 
  16.     * you should implement onTouchEvent() to return true, so you will 
  17.     * continue to see the rest of the gesture (instead of looking for 
  18.     * a parent view to handle it).  Also, by returning true from 
  19.     * onTouchEvent(), you will not receive any following 
  20.     * events in onInterceptTouchEvent() and all touch processing must 
  21.     * happen in onTouchEvent() like normal. 
  22.     * <li> For as long as you return false from this function, each following 
  23.     * event (up to and including the final up) will be delivered first here 
  24.     * and then to the target's onTouchEvent(). 
  25.     * <li> If you return true from here, you will not receive any 
  26.     * following events: the target view will receive the same event but 
  27.     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further 
  28.     * events will be delivered to your onTouchEvent() method and no longer 
  29.     * appear here. 
  30.     * </ol> 
  31.     * 
  32.     * @param ev The motion event being dispatched down the hierarchy. 
  33.     * @return Return true to steal motion events from the children and have 
  34.     * them dispatched to this ViewGroup through onTouchEvent(). 
  35.     * The current target will receive an ACTION_CANCEL event, and no further 
  36.     * messages will be delivered here. 
  37.     */  
  38.    public boolean onInterceptTouchEvent(MotionEvent ev) {  
  39.        return false;  
  40.    }  

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值