Android:onInterceptTouchEvent和dispatchTouchEvent之间的区别?

2020/10/31 23:32 · java ·  · 0评论

onInterceptTouchEventdispatchTouchEventAndroid之间有什么区别

根据android开发人员指南,这两种方法均可用于拦截触摸事件(MotionEvent),但是有什么区别?

在View(层次结构中如何进行互动onInterceptTouchEventdispatchTouchEvent以及如何onTouchEvent互动ViewGroup

最好的解密方法是源代码。该文档不足以解释这一点。

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

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

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

但是在将事件分派到适当的子视图之前,父级可以一起监视和/或拦截事件。这就是onInterceptTouchEvent的用途。因此,它会在进行点击测试之前先调用此方法,如果事件被劫持(通过从onInterceptTouchEvent返回true),则会向子视图发送ACTION_CANCEL,以便他们可以放弃对触摸事件的处理(从之前的触摸事件开始),然后再取消父级别的所有触摸事件都将分派到onTouchListener.onTouch(如果已定义)或onTouchEvent()。同样在这种情况下,永远不会再次调用onInterceptTouchEvent。

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

如果要在父级监视和/或拦截触摸事件,则主要扩展方法是ViewGroup.onInterceptTouchEvent,并使用View.onTouchListener / View.onTouchEvent进行主事件处理。

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

因为这是Google的第一个结果。我想与您分享Dave Smith在YouTube上的精彩演讲:掌握Android触摸系统,并在此处找到幻灯片它使我对Android触摸系统有了很好的深入了解:

活动如何处理触摸:

  • Activity.dispatchTouchEvent()
    • 永远先被称为
    • 将事件发送到附加到Window的根视图
    • onTouchEvent()
      • 如果没有视图占用事件,则调用
      • 永远被召唤

视图如何处理触摸:

  • View.dispatchTouchEvent()
    • 首先将事件发送给侦听器(如果存在)

      • View.OnTouchListener.onTouch()
    • 如果不消耗,则自行处理触摸

      • View.onTouchEvent()

ViewGroup如何处理触摸:

  • ViewGroup.dispatchTouchEvent()
    • onInterceptTouchEvent()
      • 检查是否应该取代孩子
      • 传递 ACTION_CANCEL 给活跃的孩子
      • 如果一次返回true,则ViewGroup消耗所有后续事件
    • 对于每个子视图(以相反的顺序添加)

      • 如果触摸是相关的(内部视图), child.dispatchTouchEvent()
      • 如果上一个未处理,则调度到下一个视图
    • 如果没有孩子处理该事件,则听者有机会

      • OnTouchListener.onTouch()
    • 如果没有侦听器,或其未处理

      • onTouchEvent()
  • 拦截事件跳过了子步骤

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

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

您可以想象一个ViewGroup代码差不多可以做到这一点(非常简化):

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

补充答案

这是其他答案的一些视觉补充。我的完整答案在这里

在此处输入图片说明

在此处输入图片说明

用途dispatchTouchEvent()方法来选择是立即处理触摸事件(使用)还是继续通知其子项方法。ViewGrouponInterceptTouchEvent()onTouchEvent()dispatchTouchEvent()

这些方法有很多困惑,但实际上并不那么复杂。大多数的困惑是因为:

  1. 如果您View/ViewGroup还是其子的任何不返回true中
    onTouchEventdispatchTouchEvent并且onInterceptTouchEvent将只被调用MotionEvent.ACTION_DOWN如果没有true from
    onTouchEvent,则父视图将假定您的视图不需要MotionEvents。
  2. 如果ViewGroup的所有子级都没有在onTouchEvent中返回true,则仅会调用onInterceptTouchEvent MotionEvent.ACTION_DOWN,即使您的ViewGroup在中返回true onTouchEvent

处理顺序如下:

  1. dispatchTouchEvent 叫做。
  2. onInterceptTouchEventMotionEvent.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. 如上所述,预览MotionEvent,并在检测到手势时设置标志。
  2. onInterceptTouchEvent当您的标志设置为取消孩子的MotionEvent处理时,返回true 这也是重置标志的方便位置,因为onInterceptTouchEvent直到next才会被再次调用MotionEvent.ACTION_DOWN

中的覆盖FrameLayout示例(我在Xamarin Android上编程时的示例是C#,但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)-每当检测到以此View为目标的触摸事件时调用
  • boolean onInterceptTouchEvent(MotionEvent ev)-每当检测到以该ViewGroup或其子对象为目标的触摸事件时调用。如果此函数返回true,则将拦截MotionEvent,这意味着它将不会传递给子级,而是传递给此View的onTouchEvent。

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

您可以看到该日志将像:

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

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

另一个区别是,如果dispatchTouchEvent返回'false',则该事件不会传播到子级(在本例中为EditText),而如果在onInterceptTouchEvent中返回false,则该事件仍将分发给EditText。

简短的回答: 首先dispatchTouchEvent()将被称为

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


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

onInterceptTouchEvent()首先将被调用,ACTION事件将分别被向下调用->移动->向上。有两种情况:

  1. 如果在3种情况下(ACTION_DOWN,ACTION_MOVE,ACTION_UP)返回false,它将认为父母不需要此触摸事件,因此onTouch()父母永不打电话,但onTouch()孩子会打电话; 但是请注意:

    • onInterceptTouchEvent()仍然继续接收触摸事件,只要它的孩子不叫requestDisallowInterceptTouchEvent(true)
    • 如果没有孩子收到该事件(在2种情况下可能发生:用户触摸的位置没有孩子,或者有孩子,但是在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()意味着该视图将不再接收该事件的任何操作。

您可以在此视频https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19和接下来的3个视频中找到答案所有触摸事件的解释都很好,非常清晰,并有很多示例。

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

我将其与自定义叠加层一起使用,以防止背景视图响应触摸事件。

主要区别:

•Activity.dispatchTouchEvent(MotionEvent)-这使您的活动可以在将所有触摸事件分派到窗口之前对其进行拦截。

•ViewGroup.onInterceptTouchEvent(MotionEvent)-这允许ViewGroup在将事件调度到子View时监视它们。

ViewGrouponInterceptTouchEvent()始终是ACTION_DOWN事件的入口点,这是第一个发生的事件。

如果您希望ViewGroup处理此手势,请从返回true onInterceptTouchEvent()返回true时,ViewGrouponTouchEvent()将接收所有后续事件,直到nextACTION_UPACTION_CANCEL,并且在大多数情况下,ACTION_DOWNandACTION_UPACTION_CANCELare之间的触摸事件ACTION_MOVE通常被识别为滚动/翻转手势。

如果从返回false onInterceptTouchEvent()onTouchEvent()则将调用目标视图后续消息将重复此操作,直到从返回true onInterceptTouchEvent()

资料来源: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);
    }
}

Activity和View都具有方法dispatchTouchEvent()和onTouchEvent。ViewGroup也具有此方法,但是还有另一个方法称为onInterceptTouchEvent。这些方法的返回类型均为布尔值,您可以通过返回值控制分派路由。

Android中的事件分发从“活动”->“视图组”->“视图”开始。

小答案:

onInterceptTouchEvent在setOnTouchListener之前。

本文地址:http://java.askforanswer.com/androidonintercepttoucheventhedispatchtoucheventzhijiandequbie.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!