Android:onInterceptTouchEvent 和 dispatchTouchEvent 之间的区别?

和 在 Android 中有什么区别?onInterceptTouchEventdispatchTouchEvent

根据android开发者指南,这两种方法都可以用来拦截触摸事件(),但有什么区别呢?MotionEvent

如何在视图 () 的层次结构中一起交互 ?onInterceptTouchEventdispatchTouchEventonTouchEventViewGroup


答案 1

揭开这一面纱的最佳位置是源代码。这些文档在解释这一点方面非常不充分。

dispatchTouchEvent 实际上是在 Activity、View 和 ViewGroup 上定义的。可以将其视为决定如何路由触摸事件的控制器。

例如,最简单的情况是 View.dispatchTouchEvent,它将触摸事件路由到 OnTouchListener.onTouch(如果已定义)或路由到扩展方法 onTouchEvent

对于ViewGroup.dispatchTouchEvent来说,事情要复杂得多。它需要弄清楚它的哪个子视图应该获取该事件(通过调用 child.dispatchTouchEvent)。这基本上是一个命中测试算法,您可以在其中找出哪个子视图的边界矩形包含触摸点坐标。

但是,在将事件调度到适当的子视图之前,父级可以监视和/或拦截事件。这就是OnInterceptTouchEvent的用途。因此,它首先在执行命中测试之前调用此方法,如果事件被劫持(通过从onInterceptTouchEvent返回true),它会向子视图发送一个ACTION_CANCEL,以便它们可以放弃触摸事件处理(来自以前的触摸事件),从那时起,父级别的所有触摸事件都将调度到onTouchListener.onTouch(如果已定义)或onTouchEvent()。同样在这种情况下,永远不会再调用 onInterceptTouchEvent。

您是否甚至想要覆盖[活动|视图组|View].dispatchTouchEvent?除非您正在执行某些自定义路由,否则可能不应该这样做。

主要的扩展方法是ViewGroup.onInterceptTouchEvent(如果要在父级别监视和/或拦截触摸事件)和View.onTouchListener/View.onTouchEvent(用于主要事件处理)。

总而言之,它过于复杂的设计imo,但Android apis更倾向于灵活性而不是简单性。


答案 2

因为这是谷歌上的第一个结果。我想与大家分享戴夫·史密斯(Dave Smith)在Youtube上的精彩演讲:掌握Android Touch系统,幻灯片可在此处获得。它让我对Android Touch系统有了很好的深刻理解:

“活动”如何处理触摸:

  • Activity.dispatchTouchEvent()
    • 始终首先被调用
    • 将事件发送到附加到窗口的根视图
    • onTouchEvent()
      • 如果没有视图使用事件,则调用
      • 总是最后被调用

视图如何处理触摸:

  • View.dispatchTouchEvent()
    • 首先将事件发送到侦听器(如果存在)
      • View.OnTouchListener.onTouch()
    • 如果未使用,则处理触摸本身
      • View.onTouchEvent()

视图组如何处理触摸:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • 检查它是否应该取代儿童
      • 活期儿童通票ACTION_CANCEL
      • 如果它返回一次 true,则 消耗所有后续事件ViewGroup
    • 对于每个子视图(按相反的顺序添加它们)
      • 如果触摸是相关的(内部视图),child.dispatchTouchEvent()
      • 如果它未由上一个视图处理,则调度到下一个视图
    • 如果没有子级处理该事件,则侦听器将有机会
      • OnTouchListener.onTouch()
    • 如果没有侦听器,或者未处理
      • onTouchEvent()
  • 截获的事件跳过子步骤

他还提供了 github.com/devunwired/ 上的自定义触摸的示例代码。

答:基本上,在每一层上都调用了 ,以确定 a 是否对正在进行的手势感兴趣。在一个有能力窃取触摸事件在他的-方法,之前它会调用孩子。仅当 -方法返回 true 时,才会停止调度。不同之处在于,它正在调度并告诉它是否应该拦截(不将 MotionEvent 发送给子级)或不进行(调度给子级)。dispatchTouchEvent()ViewViewViewGroupViewGroupdispatchTouchEvent()dispatchTouchEvent()ViewGroupViewGrouponInterceptTouchEvent()dispatchTouchEvent()MotionEventsonInterceptTouchEvent

您可以想象ViewGroup的代码或多或少地执行以下操作(非常简化):

public boolean dispatchTouchEvent(MotionEvent ev) {
    if(!onInterceptTouchEvent()){
        for(View child : children){
            if(child.dispatchTouchEvent(ev))
                return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}

推荐