Android:onInterceptTouchEvent和dispatchTouchEvent之间的区别?

Ann*_*oid 239 java android event-listener event-handling android-touch-event

Android onInterceptTouchEventdispatchTouchEventAndroid有什么区别?

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

怎么办onInterceptTouchEvent,dispatchTouchEventonTouchEvent视图的层次结构中一起互动(ViewGroup)?

num*_*ati 267

揭开这个神秘面纱的最佳地方是源代码.对于解释这个问题,文档非常不足够.

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

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

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

但是在它将事件分派到适当的子视图之前,父母可以一起监视和/或拦截事件.这就是onInterceptTouchEvent的用途.所以它做的命中测试之前,首先要调用此方法,如果事件被劫持(通过返回从onInterceptTouchEvent真)发送一个ACTION_CANCEL孩子的意见,使他们能够放弃自己的触摸事件处理(从以前的触摸事件),并从那时起父级别的所有触摸事件都将分派到onTouchListener.onTouch(如果已定义)或onTouchEvent().同样在这种情况下,永远不会再调用onInterceptTouchEvent.

你是否想要覆盖[Activity | ViewGroup | View] .dispatchTouchEvent?除非你做一些自定义路由,否则你可能不应该这样做.

如果你想刺探和/或在主事件处理父级别和View.onTouchListener/View.onTouchEvent拦截触摸事件的主要扩展方法是ViewGroup.onInterceptTouchEvent.

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

  • 这是一个很好的,简洁的答案.有关更详细的示例,请参阅培训["在ViewGroup中管理触摸事件"](http://developer.android.com/training/gestures/viewgroup.html) (10认同)

seb*_*seb 234

因为这是谷歌的第一个结果.我想和你分享Dave Smith在Youtube上的精彩演讲:掌握Android Touch系统,这里有幻灯片.它让我对Android Touch系统有了深刻的理解:

活动如何处理触摸:

  • Activity.dispatchTouchEvent()
    • 总是第一次被召唤
    • 将事件发送到附加到Window的根视图
    • onTouchEvent()
      • 如果没有视图消耗该事件则调用
      • 永远被称为

视图如何处理触摸:

  • View.dispatchTouchEvent()
    • 如果存在,则首先将事件发送给侦听器
      • View.OnTouchListener.onTouch()
    • 如果没有消耗,则处理触摸本身
      • View.onTouchEvent()

ViewGroup如何处理触摸:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • 检查它是否应该取代孩子
      • 通过 ACTION_CANCEL 活跃的孩子
      • 返回true一次,消耗所有后续事件
    • 对于每个子视图,以相反的顺序添加它们
      • 如果触摸相关(内部视图), ViewGroup
      • 如果以前没有处理,则调度到下一个视图
    • 如果没有孩子处理事件,听众就有机会
      • child.dispatchTouchEvent()
    • 如果没有听众,或者没有处理
      • OnTouchListener.onTouch()
  • 截获的事件跳过子步

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

答: 基本上onTouchEvent()是在每dispatchTouchEvent()一层上调用以确定a View是否对正在进行的手势感兴趣.在ViewViewGroup有偷他的触摸事件的能力ViewGroup-方法,之前它会叫dispatchTouchEvent()上孩子.该dispatchTouchEvent()如果只会停止调度ViewGroup ViewGroup-方法返回true.的区别是,onInterceptTouchEvent()被调度dispatchTouchEvent()MotionEvents告诉它是否应拦截(不分派onInterceptTouchEvent到子女)或没有(派遣儿童).

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

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


Sur*_*gch 52

补充答案

以下是其他答案的一些视觉补充.我的完整答案就在这里.

在此输入图像描述

在此输入图像描述

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


Mar*_*l W 19

关于这些方法存在很多困惑,但实际上并没有那么复杂.大多数混乱是因为:

  1. 如果您View/ViewGroup或您的任何一个孩子没有返回真实 onTouchEvent,dispatchTouchEvent并且onInterceptTouchEvent只会被要求MotionEvent.ACTION_DOWN.如果没有true onTouchEvent,则父视图将假定您的视图不需要MotionEvents.
  2. 如果ViewGroup的子节点都没有在onTouchEvent中返回true,则只会调用onInterceptTouchEvent MotionEvent.ACTION_DOWN,即使ViewGroup返回true也是如此onTouchEvent.

处理顺序是这样的:

  1. dispatchTouchEvent 叫做.
  2. onInterceptTouchEvent调用MotionEvent.ACTION_DOWN或当ViewGroup的任何子节点返回true时onTouchEvent.
  3. onTouchEvent首先调用ViewGroup的子节点,当没有子节点返回true时,调用它 View/ViewGroup.

如果要在TouchEvents/MotionEvents不禁用孩子事件的情况下进行预览,则必须执行以下两项操作:

  1. 覆盖dispatchTouchEvent预览事件并返回 super.dispatchTouchEvent(ev);
  2. 覆盖onTouchEvent并返回true,否则你不会得到任何 MotionEvent 除外MotionEvent.ACTION_DOWN.

如果您想要检测某些手势,例如滑动事件,只要您没有检测到手势,就可以在子项上禁用其他事件,您可以这样做:

  1. 如上所述预览MotionEvents并在检测到手势时设置标志.
  2. onInterceptTouchEvent当您的标志设置为取消您孩子的MotionEvent处理时返回true .这也是重置标志的一个方便的地方,因为在下一次之前不会再次调用onInterceptTouchEvent MotionEvent.ACTION_DOWN.

a中的覆盖FrameLayout示例(我的示例是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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我不知道为什么这没有更多的赞成票.这是一个很好的答案(IMO),我发现它非常有用. (3认同)

Ngu*_*Dat 9

简短回答: 首先dispatchTouchEvent()会被调用。

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


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

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

  1. 如果在 3 种情况下(ACTION_DOWN、ACTION_MOVE、ACTION_UP)返回 false,它将认为父级不需要此触摸事件,因此onTouch()父级从不调用,但onTouch()子级会调用;但是请注意:

    • onInterceptTouchEvent()仍然继续接收触摸事件,只要它的孩子不叫requestDisallowInterceptTouchEvent(true)
    • 如果没有孩子接收该事件(这可能在两种情况下发生:用户触摸的位置没有孩子,或者有孩子但在 ACTION_DOWN 时返回 false),父母会将该事件发送回onTouch()父母。
  2. 反之亦然,如果你返回 true父母会立即窃取这个触摸事件,并onInterceptTouchEvent()立即停止,而不是onTouch()父母会被调用,所有onTouch()孩子都会收到最后一个动作事件 - ACTION_CANCEL(因此,这意味着父母偷了触摸事件,从那时起孩子们就无法处理了)。onInterceptTouchEvent()return false的流程是正常的,但是 return true 的情况有点混乱,所以我在这里列出:

    • 在 ACTION_DOWN 时返回 true,onTouch()父母将再次收到 ACTION_DOWN和以下操作(ACTION_MOVE,ACTION_UP)。
    • 在 ACTION_MOVE 处返回 true,onTouch()父母将收到下一个ACTION_MOVE(与 中的 ACTION_MOVE 不同onInterceptTouchEvent())和后续动作(ACTION_MOVE、ACTION_UP)。
    • 在 ACTION_UP 时返回 true,onTouch()父母将根本不会调用,因为父母窃取触摸事件为时已晚。

另一件重要的事情是事件的 ACTION_DOWNonTouch()将决定视图是否愿意从该事件接收更多动作。如果视图在 ACTION_DOWN 处返回 true onTouch(),则意味着视图愿意从该事件接收更多操作。否则,在 ACTION_DOWN 处返回 falseonTouch()将意味着该视图不会从该事件接收任何更多操作。


Krz*_*tow 8

我在这个网页http://doandroids.com/blogs/tag/codeexample/上得到了非常直观的解释.从那里采取:

  • boolean onTouchEvent(MotionEvent ev) - 每当检测到以此View为目标的触摸事件时调用
  • boolean onInterceptTouchEvent(MotionEvent ev) - 每当使用此ViewGroup或其子节点作为目标检测到触摸事件时调用.如果此函数返回true,则将拦截MotionEvent,这意味着它不会传递给子节点,而是传递给此View的onTouchEvent.

  • 问题是onInterceptTouchEvent和dispatchTouchEvent.两者都是在onTouchEvent之前调用的.但在那个例子中你看不到dispatchTouchEvent. (2认同)

Day*_*man 8

dispatchTouchEvent在onInterceptTouchEvent之前处理.

使用这个简单的例子:

   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);
Run Code Online (Sandbox Code Playgroud)

您可以看到日志将如下所示:

I/System.out(25900): Event - dispatchTouchEvent
I/System.out(25900): Event - onInterceptTouchEvent
Run Code Online (Sandbox Code Playgroud)

因此,如果您正在使用这两个处理程序,请使用dispatchTouchEvent在第一个实例上处理事件,该事件将转到onInterceptTouchEvent.

另一个区别是,如果dispatchTouchEvent返回'false',则事件不会传播给子节点,在本例中为EditText,而如果在onInterceptTouchEvent中返回false,则事件仍然会传递给EditText


Cha*_*Mic 2

主要区别:

\n\n
\n

\xe2\x80\xa2Activity.dispatchTouchEvent(MotionEvent) - 这允许您的 Activity\n 在将所有触摸事件分派到窗口之前拦截它们。
\n \xe2\x80\xa2ViewGroup.onInterceptTouchEvent(MotionEvent) - 这允许 ViewGroup 在事件分派到子视图时监视事件。

\n
\n