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

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

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


当前回答

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

如果你的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;
}

其他回答

在拦截事件之前发出指示。

用这个简单的例子:

   main = new LinearLayout(this){
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            System.out.println("Event - onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
            //return false; //event get propagated
        }
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            System.out.println("Event - dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
            //return false; //event DONT get propagated
        }
    };

    main.setBackgroundColor(Color.GRAY);
    main.setLayoutParams(new LinearLayout.LayoutParams(320,480));    


    viewA = new EditText(this);
    viewA.setBackgroundColor(Color.YELLOW);
    viewA.setTextColor(Color.BLACK);
    viewA.setTextSize(16);
    viewA.setLayoutParams(new LinearLayout.LayoutParams(320,80));
    main.addView(viewA);

    setContentView(main);

你可以看到日志是这样的:

I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent

因此,如果你正在使用这两个处理程序,使用dispatchTouchEvent来处理第一个实例的事件,这将去onInterceptTouchEvent。

另一个区别是,如果dispatchTouchEvent返回'false',事件不会被传播到子,在这种情况下的EditText,而如果你在onInterceptTouchEvent返回false,事件仍然得到调度到EditText

主要区别:

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

你可以在这个视频https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19和接下来的三个视频中找到答案。所有的触摸事件都解释得很好,它非常清楚,充满了例子。

ViewGroup的onInterceptTouchEvent()总是ACTION_DOWN事件的入口点,这是第一个发生的事件。

如果你想让ViewGroup处理这个手势,从onInterceptTouchEvent()返回true。 在返回true时,ViewGroup的onTouchEvent()将接收到下一次ACTION_UP或ACTION_CANCEL之前的所有后续事件,在大多数情况下,ACTION_DOWN和ACTION_UP或ACTION_CANCEL之间的触摸事件为ACTION_MOVE,通常会被识别为滚动/抛动手势。

如果你从onInterceptTouchEvent()返回false,目标视图的onTouchEvent()将被调用。它将被重复用于后续的消息,直到你从onInterceptTouchEvent()返回true。

来源: http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html

public boolean dispatchTouchEvent(MotionEvent ev){
    boolean consume =false;
    if(onInterceptTouchEvent(ev){
        consume = onTouchEvent(ev);
    }else{
        consume = child.dispatchTouchEvent(ev);
    }
}