ViewGroup touch事件分发机制

来源:互联网 发布:VPN网络闭关锁国 编辑:程序博客网 时间:2024/04/27 09:44

参考 http://www.cnblogs.com/linjzong/p/4191891.html



当一个Touch事件(触摸事件为例)到达根节点,即Acitivty的ViewGroup时,它会依次下发,


下发的过程是调用子View(ViewGroup)的dispatchTouchEvent方法实现的。


简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,


而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。


上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。dispatchTouchEvent方法只负责事件的分发,


它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,


那么 ⑥-⑦-③-④将都接收不到本次Touch事件。


ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,

而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理

Down方式通过dispatchTouchEvent分发,分发的目的是为了找到真正需要处理完整Touch请求的View。

当某个View或者ViewGroup的onTouchEvent事件返回true时,便表示它是真正要处理这次请求的View,之后的Aciton_UP和Action_MOVE将由它处理


大致流程:

ViewGroup dispatchTouchEvent(MotionEvent event) -> ViewGroup onInterceptTouchEvent(MotionEvent ev) -> View.dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event)方法内部处理过程:

(1) 如果没有被其他窗口遮盖,则继续下面的逻辑


(2) 如果是按下ACTION_DOWN,则 清理之前触摸操作的所有痕迹,即派发取消操作,重置touch状态


(3) 如果是按下ACTION_DOWN 或 mFirstTouchTarget 不为空,则 判断 是否允许拦截,允许拦截,则调用 onInterceptTouchEvent(ev)获取是否被拦截标志
    ViewGroup onInterceptTouchEvent(MotionEvent event)默认是返回false 不拦截,
    如果当前ViewGroup需要拦截传递给其子视图的事件,需要return true


(4) 如果没有被取消同时没有被拦截

   判断当前ACTION是否是 ACTION_DOWN,ACTION_HOVER_MOVE,则循环判断child(过滤出消费了Touch事件的child)
   (a) 首先判断child是否可以接收touch事件或是否在child区域之内, 如果其中一个不满足则跳过此child,继续下个child判断
   (b) 如果满足条件,则从child查找出消费了touch事件的子view,如果存在这样的view则跳转循环判断
   (c) 如果不存在这样的view,则调用dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits),


从头到尾总结一下:


1.Touch事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。

2.ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。

3.触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

4.当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。

 分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。

5.当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。

6.当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

7.onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

ViewGroup是否允许拦截 disallowIntercept(禁止拦截) 可以通过调用ViewGroup的如下方法

public void requestDisallowInterceptTouchEvent(boolean);

//是否禁用拦截,TRUE,那么不会调用 false表示允许拦截, 如果允许拦截,则能够执行到onInterceptTouchEvent方法的;

事件派发先是隧道方式、再是冒泡方式
隧道方式传递,直到某一个元素消耗此事件,由上至下逐层分发视图。
冒泡方式传递,当某个视图消耗事件后其return boolean 是与分发相反的方法向上传递。
具体分发给哪一个视图是通过当前触摸点坐标在当前层哪个视图上判断


0 0
原创粉丝点击