Android触摸事件是如何传递的?

时间:2011-09-16 20:02:26

标签: android events

我不是在问如何处理触摸事件,而是在幕后发生了什么?如果有几个嵌套的小部件,他们看到的事件是什么顺序?开发人员是否可以控制它?理想情况下,我想要一个关于这个主题的文件。

4 个答案:

答案 0 :(得分:49)

让我们来看一个视觉示例。

enter image description here

当触摸事件发生时,首先会向每个人通知该事件,从活动开始并一直到顶部的视图。然后每个人都有机会处理事件,从顶部视图开始(视图在触摸区域中具有最高Z顺序)并一直返回到活动。因此,活动是第一个听到它并且最后一个有机会处理它。

enter image description here

如果某个ViewGroup想要立即处理触摸事件(而不是让其他任何人有机会),那么它只能在其true中返回onInterceptTouchEvent()。活动没有onInterceptTouchEvent(),但您可以覆盖dispatchTouchEvent()来执行相同操作。

如果视图(或ViewGroup)具有OnTouchListener,则触摸事件由OnTouchListener.onTouch()处理。否则由onTouchEvent()处理。如果onTouchEvent()针对任何触摸事件返回true,那么处理就会停止。没有其他人可以获得机会。

更详细的解释

上图让事情变得比实际更简单。例如,在Activity和ViewGroup A(根布局)之间还有Window和DecorView。我把它们排除在上面,因为我们通常不必与它们互动。但是,我会将它们包含在下面。以下描述遵循通过源代码的触摸事件。您可以单击链接以查看实际的源代码。

(更新:源代码已更新,因此行号现已关闭,但点击链接仍会将您转到正确的文件。只需搜索方法名称。)

  1. 活动dispatchTouchEvent()会收到触摸事件的通知。触摸事件以MotionEvent传递,其中包含x,y坐标,时间,事件类型和其他信息。
  2. 触摸事件将发送到Window superDispatchTouchEvent()Window是一个抽象类。实际实施是PhoneWindow
  3. 获取通知的下一行是DecorView' superDispatchTouchEvent()DecorView处理状态栏,导航栏,内容区域等It is actually just a FrameLayout subclass,它本身就是ViewGroup的子类。
  4. 下一个收到通知的人(如果我错了,请纠正我)是您活动的内容视图。这是您在Android Studio的布局编辑器中创建布局时在xml中设置为活动的根布局的内容。因此,无论您选择RelativeLayoutLinearLayout还是ConstraintLayout,它们都是ViewGroup的子类。并且ViewGroup会在dispatchTouchEvent()中收到有关触摸事件的通知。这是我上图中的 ViewGroup A
  5. ViewGroup notify any children会有触摸事件,包括任何ViewGroup个孩子。这是我上图中的 ViewGroup B
  6. 在此过程中的任何地方,ViewGroup可以short-circuit通知{* 3}} onInterceptTouchEvent()
  7. 假设没有true将通知缩短,通知行的自然结束就是查看了dispatchTouchEvent()If there is an OnTouchListener
  8. 现在是时候开始处理事件了。 onTouch(),然后第一次有Otherwise处理触摸事件的机会。 onTouchEvent(),View Finally可以处理它。
  9. 现在所有ViewGroups递归上线都有机会以ViewGroup的方式处理触摸事件。虽然,我在上图中没有说明这一点,但ViewViewGroup子类,因此我所描述的关于ViewOnTouchListener.onTouch()的所有内容也适用于ViewGroups
  10. onTouchEvent(),如果没有其他人想要,活动也会获得使用here处理该活动的最后机会。
  11. 常见问题

    我什么时候需要覆盖onTouchEvent()

    如果您想在任何视图获得机会之前捕获触摸事件,请在活动中覆盖它。对于ViewGroup(包括根视图),只需覆盖dispatchTouchEvent()onInterceptTouchEvent()

    我什么时候需要覆盖onTouchEvent()

    如果您只想监视即将发出的触摸通知,可以在此处执行并返回onInterceptTouchEvent()

    但是,重写此方法的主要目的是让ViewGroup处理某种类型的触摸事件,同时让子处理另一种类型。例如,false执行此操作以处理滚动,同时让其子项处理类似按钮单击的操作。相反,如果子视图不想让其父级窃取其触摸事件,则可以调用ScrollView

    有什么触摸事件类型?

    主要是

    • requestDisallowTouchIntercept() - 这是触摸事件的开始。如果您要处理触摸事件,则应始终在ACTION_DOWN中为true事件返回ACTION_DOWN。否则,您将无法再获得任何活动。
    • onTouchEvent - 当您在屏幕上移动手指时,此事件会不断触发。
    • ACTION_MOVE - 这是触摸事件的最后一个事件。

    亚军是ACTION_UP。如果树上的ViewGroup决定拦截触摸事件,则会调用此方法。

    您可以查看其他类型的MotionEvents Part 1。由于Android是多点触控的,当其他手指("指针")触摸屏幕时,也会触发事件。

    进一步研究

答案 1 :(得分:23)

从活动角度来看:

触摸事件首先传递给Activity.dispatchTouchEvent。这是你可以先抓住它们的地方。

在这里,他们将被调度到Window,在那里他们遍历View层次结构,其顺序是最后绘制的Widgets(在其他小部件之上)有机会首先在View.onTouchEvent中处理触摸。如果某些View在onTouchEvent中返回true,则遍历停止,而其他视图不会收到触摸事件。

最后,如果没有View消耗触摸,它将被传递给Activity.onTouchEvent。

这就是你的全部控制权。而且你所看到的东西在其他东西之上绘制是合乎逻辑的,有机会在它下面绘制的东西之前处理触摸事件。

答案 2 :(得分:1)

根据Suragch的回答,

伪代码:

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

        return consume;
    }

REF:机器人开发艺术探索

答案 3 :(得分:0)

我准备了一个高级图,该图应说明一个简单的流程。 请随时comment and edit it

enter image description here