Android中onInterceptTouchEvent和dispatchTouchEvent的区别是什么?

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

onInterceptTouchEvent, dispatchTouchEvent和onTouchEvent如何在视图(ViewGroup)的层次结构中一起交互?


当前回答

答:小

onInterceptTouchEvent出现在setOnTouchListener之前。

其他回答

揭开这一神秘面纱的最佳地点是源代码。医生们对这一点解释得很不充分。

dispatchTouchEvent实际上定义在Activity, View和ViewGroup上。可以把它看作一个控制器,它决定如何路由触摸事件。

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

ViewGroup。dispatchTouchEvent的事情要复杂得多。它需要找出它的哪个子视图应该获得事件(通过调用child. dispatchtouchevent)。这基本上是一个命中测试算法,其中你计算出哪个子视图的边界矩形包含触点坐标。

But before it can dispatch the event to the appropriate child view, the parent can spy and/or intercept the event all together. This is what onInterceptTouchEvent is there for. So it calls this method first before doing the hit testing and if the event was hijacked (by returning true from onInterceptTouchEvent) it sends a ACTION_CANCEL to the child views so they can abandon their touch event processing (from previous touch events) and from then onwards all touch events at the parent level are dispatched to onTouchListener.onTouch (if defined) or onTouchEvent(). Also in that case, onInterceptTouchEvent is never called again.

你甚至想要覆盖[活动|ViewGroup|View].dispatchTouchEvent?除非您正在进行一些自定义路由,否则您可能不应该这样做。

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

总的来说,在我看来,它的设计过于复杂,但android api更倾向于灵活性而不是简单性。

我在http://doandroids.com/blogs/tag/codeexample/这个网页上看到了非常直观的解释。从这里开始:

boolean onTouchEvent(MotionEvent ev) -当检测到以该视图为目标的触摸事件时调用 boolean onInterceptTouchEvent(MotionEvent ev) -当一个触摸事件被检测到并以这个ViewGroup或它的子对象作为目标时调用。如果这个函数返回true, MotionEvent将被拦截,这意味着它不会被传递给子视图,而是传递给这个视图的onTouchEvent。

简单回答:dispatchTouchEvent()将首先被调用。

简短的建议:不应该重写dispatchTouchEvent(),因为它很难控制,有时它会降低您的性能。恕我直言,我建议重写onInterceptTouchEvent()。


因为大多数答案都非常清楚地提到了活动/视图组/视图上的流触摸事件,我只添加了更多关于ViewGroup中这些方法的代码细节(忽略dispatchTouchEvent()):

onInterceptTouchEvent()将首先被调用,ACTION事件将分别被调用down -> move -> up。有2种情况:

If you return false in 3 cases (ACTION_DOWN, ACTION_MOVE, ACTION_UP), it will consider as the parent won't need this touch event, so onTouch() of the parents never calls, but onTouch() of the children will call instead; however please notice: The onInterceptTouchEvent() still continue to receive touch event, as long as its children don't call requestDisallowInterceptTouchEvent(true). If there are no children receiving that event (it can happen in 2 cases: no children at the position the users touch, or there are children but it returns false at ACTION_DOWN), the parents will send that event back to onTouch() of the parents. Vice versa, if you return true, the parent will steal this touch event immediately, and onInterceptTouchEvent() will stop immediately, instead onTouch() of the parents will be called as well as all onTouch() of children will receive the last action event - ACTION_CANCEL (thus, it means the parents stole touch event, and children cannot handle it from then on). The flow of onInterceptTouchEvent() return false is normal, but there is a little confusion with return true case, so I list it here: Return true at ACTION_DOWN, onTouch() of the parents will receive ACTION_DOWN again and following actions (ACTION_MOVE, ACTION_UP). Return true at ACTION_MOVE, onTouch() of the parents will receive next ACTION_MOVE (not the same ACTION_MOVE in onInterceptTouchEvent()) and following actions (ACTION_MOVE, ACTION_UP). Return true at ACTION_UP, onTouch() of the parents will NOT called at all since it's too late for the parents to steal touch event.

还有一件重要的事情是onTouch()中事件的ACTION_DOWN将决定视图是否希望从该事件接收更多的动作。如果视图在onTouch()中的ACTION_DOWN处返回true,这意味着视图愿意从该事件接收更多操作。否则,在onTouch()中的ACTION_DOWN处返回false将意味着视图不会从该事件接收更多的操作。

ViewGroup子类中的以下代码将阻止它的父容器接收触摸事件:

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    // Normal event dispatch to this container's children, ignore the return value
    super.dispatchTouchEvent(ev);

    // Always consume the event so it is not dispatched further up the chain
    return true;
  }

我使用自定义覆盖来防止背景视图响应触摸事件。

补充回答

以下是对其他答案的视觉补充。我的完整答案在这里。

ViewGroup的dispatchTouchEvent()方法使用onInterceptTouchEvent()来选择是否应该立即处理触摸事件(使用onTouchEvent())或继续通知其子方法的dispatchTouchEvent()。