2011-09-16 48 views
17

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

回答

21

活动观点:

触摸事件首先被传递给Activity.dispatchTouchEvent。这是你第一次接触到它的地方。

在这里,它们被调度到窗口,在窗口中它们遍历View层次结构,以便最后绘制的窗口小部件(在其他窗口小部件之上)有机会先处理View.onTouchEvent中的接触。如果某些View在onTouchEvent中返回true,则遍历停止和其他视图不会收到触摸事件。

最后,如果没有View消耗touch,它会传递给Activity.onTouchEvent。

这就是你的全部控制。从逻辑上看,你在其他东西上绘制的东西,有机会在绘制它之前的东西之前处理触摸事件。

+0

你有什么样的描述这个文件? – DJClayworth

+0

关于调度:我可以阅读源代码和功能评论。 关于查看遍历的顺序 - 我已经在一些文档中阅读过,当然在developer.android.com上,但是不能准确地告诉现在哪个。 –

+0

无论如何,好开始:http://developer.android.com/guide/topics/ui/ui-events.html –

10

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

enter image description here

当触摸事件发生时,起初大家被告知事件的开始,活动中去,一路到顶部的视图。然后,每个人都有机会处理该事件,从顶层视图开始,一直回到活动。所以活动是第一个听到它,最后有机会处理它。

enter image description here

如果活动或某些ViewGroup中要处理的触摸事件马上(而不是给别人下了线它的机会),那么它可以只在其onInterceptTouchEvent()返回true

如果视图(或视图组)具有OnTouchListener,则触摸事件由OnTouchListener.onTouch()处理。否则它由onTouchEvent()处理。如果onTouchEvent()针对任何触摸事件返回true,则处理在那里停止。没有其他人能够得到机会。

更详细的解释

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

  1. 活动的dispatchTouchEvent()被通知触摸事件。触摸事件作为MotionEvent传入,其中包含x,y坐标,时间,事件类型和其他信息。
  2. 触摸事件发送到窗口的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. ViewGroupnotify any children它有触摸事件,包括任何ViewGroup孩子。在我上面的图中,这是ViewGroup B
  6. 随时随地都可以,ViewGroup可以short-circuit通知处理通过返回trueonInterceptTouchEvent()
  7. 假设没有ViewGroup将通知缩短,则通知行的自然结束时是查看的dispatchTouchEvent() get被调用。
  8. 现在是时候开始处理事件了。 If there is an OnTouchListener,那么它将以第一个机会处理触摸事件onTouch()Otherwise,视图的onTouchEvent()得到处理它。
  9. 现在所有ViewGroups递归上线都有机会像View一样处理触摸事件。虽然我没有在上图中指出这一点,但ViewGroupView的子类,所以我所描述的有关OnTouchListener.onTouch()onTouchEvent()的所有内容也适用于ViewGroups。
  10. Finally,如果没有其他人需要它,活动也有最后机会处理onTouchEvent()事件。

FAQ

当我会永远需要重写dispatchTouchEvent()

也许你不需要,除非你需要做一些额外的路由,默认情况下不会发生。要监控触摸事件通知,您可以改写onInterceptTouchEvent()

我什么时候需要重写onInterceptTouchEvent()

如果您只是想监视正在进入的触摸通知,您可以在此处执行此操作并返回false

但是,覆盖此方法的主要目的是让ViewGroup处理特定类型的触摸事件,同时让子处理另一种类型。例如,一个ScrollView这样做来处理滚动,同时让它的孩子处理像点击按钮。相反,如果子视图不想让父母窃取其触摸事件,则可以拨打requestDisallowTouchIntercept()

什么是触摸事件类型?

主要的有

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

亚军是ACTION_CANCEL。如果树上的ViewGroup决定拦截触摸事件,则会被调用。

您可以查看其他种类的MotionEvents here。由于Android是多点触控,当其他手指(“指针”)触摸屏幕时,事件也会被触发。

进一步研究