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

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

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


当前回答

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

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

其他回答

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;
  }

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

关于这些方法有很多困惑,但实际上并没有那么复杂。大部分的困惑是因为:

如果你的View/ViewGroup或它的任何子视图在中没有返回true onTouchEvent, dispatchTouchEvent和onInterceptTouchEvent将ONLY 为MotionEvent.ACTION_DOWN调用。没有真正的 onTouchEvent,父视图会假设你的视图不需要 MotionEvents。 当一个ViewGroup的子类在onTouchEvent中都没有返回true时,onInterceptTouchEvent只会在MotionEvent中被调用。ACTION_DOWN,即使你的ViewGroup在onTouchEvent中返回true。

处理顺序如下:

dispatchTouchEvent被调用。 onInterceptTouchEvent为MotionEvent调用。ACTION_DOWN或when ViewGroup的任何子对象在onTouchEvent中返回true。 onTouchEvent首先在ViewGroup和的子对象上被调用 当没有子函数返回true时,调用 视图/ ViewGroup。

如果你想预览TouchEvents/MotionEvents而不禁用子类上的事件,你必须做两件事:

重写dispatchTouchEvent以预览事件并返回 super.dispatchTouchEvent (ev); 重写onTouchEvent并返回true,否则你不会得到任何 除了MotionEvent. action_down。

如果你想检测一些手势,如滑动事件,而不禁用其他事件在你的孩子只要你没有检测到的手势,你可以这样做:

预览如上所述的MotionEvents,并设置一个标志 发现了你的手势。 当你的标志被设置为取消时,在onInterceptTouchEvent中返回true 你的孩子的动作事件处理。这也是一种方便 重置标志的位置,因为onInterceptTouchEvent不会被重置 再次调用,直到下一次MotionEvent.ACTION_DOWN。

在framamelayout中覆盖的例子(我的例子是c#,因为我用Xamarin Android编程,但在Java中逻辑是相同的):

public override bool DispatchTouchEvent(MotionEvent e)
{
    // Preview the touch event to detect a swipe:
    switch (e.ActionMasked)
    {
        case MotionEventActions.Down:
            _processingSwipe = false;
            _touchStartPosition = e.RawX;
            break;
        case MotionEventActions.Move:
            if (!_processingSwipe)
            {
                float move = e.RawX - _touchStartPosition;
                if (move >= _swipeSize)
                {
                    _processingSwipe = true;
                    _cancelChildren = true;
                    ProcessSwipe();
                }
            }
            break;
    }
    return base.DispatchTouchEvent(e);
}

public override bool OnTouchEvent(MotionEvent e)
{
    // To make sure to receive touch events, tell parent we are handling them:
    return true;
}

public override bool OnInterceptTouchEvent(MotionEvent e)
{
    // Cancel all children when processing a swipe:
    if (_cancelChildren)
    {
        // Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down:
        _cancelChildren = false;
        return true;
    }
    return false;
}

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

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

主要区别:

•Activity. dispatchtouchevent (MotionEvent) -这允许您的Activity 来拦截所有的触摸事件,然后再将它们分派到 窗口。 •ViewGroup.onInterceptTouchEvent(MotionEvent) -这允许一个 ViewGroup来监视事件,因为它们被分派到子视图。

简单回答: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将意味着视图不会从该事件接收更多的操作。