当前位置: 移动技术网 > 移动技术>移动开发>Android > Android事件分发机制的详解

Android事件分发机制的详解

2019年07月24日  | 移动技术网移动技术  | 我要评论

android事件分发机制

我们只考虑最重要的四个触摸事件,即:down,move,up和cancel。一个手势(gesture)是一个事件列,以一个down事件开始(当用户触摸屏幕时产生),后跟0个或多个move事件(当用户四处移动手指时产生),最后跟一个单独的up或cancel事件(当用户手指离开屏幕或者系统告诉你手势(gesture)由于其他原因结束时产生)。当我们说到“手势剩余部分”时指的是手势后续的move事件和最后的up或cancel事件。

在这里我也不考虑多点触摸手势(我们只假设用一个手指)并且忽略多个move事件可以被归为一组这一实际情况。最后,我们假设文中的view都没有注册ontouchlistener。

我们将要讨论的视图层次是这样的:最外层是一个viewgroup a,包含一个或多个子view(children),其中一个子view是viewgroup b,viewgroupb中又包含一个或多个子view,其中一个子view是 view c,c不是一个viewgroup。这里我们忽略同层级view之间可能的交叉叠加。


假设用户首先触摸到的屏幕上的点是c上的某个点,该点被标记为触摸点(touch point),down事件就在该点产生。然后用户移动手指并最后离开屏幕,此过程中手指是否离开c的区域无关紧要,关键是手势(gesture)是从哪里开始的。

默认情况

假设上面的a,b,c都没有覆写默认的事件传播行为,那么下面就是事件传播的过程:

  1. down事件被传到c的ontouchevent方法中,该方法返回false,表示“我不关心这个手势(gesture)”。
  2. 因此,down事件被传到b的ontouchevent方法中,该方法同样返回false,表示b也不关心这个手势。
  3. 同样,因为b不关心这个手势,down事件被传到a的ontouchevent方法中,该方法也返回false。

由于没有view关心这个手势(gesture),它们将不再会从“手势剩余部分”中接收任何事件。

处理事件

现在,让我们假设c实际上是关心这个手势(gesture)的,原因可能是c被设置成可点击的(clickable)或者你覆写了c的ontouchevent方法。

  • down事件被传递给c的ontouchevent方法,该方法可以做任何它想做的事情,最后返回true。
  • 因为c说它正在处理这个手势(gesture),则down事件将不再被传递给b和a的ontouchevent方法。
  • 因为c说它正在处理这个手势(gesture),所以“手势剩余部分”的事件也将传递给c的ontouchevent方法,此时该方法返回true或false都无关紧要了,但是为保持一致最好还是返回true。

个人理解:从这里可以看出,各个view的ontouchevent方法对down事件的处理,代表了该view对以此down开始的整个手势(gesture)的处理意愿,返回true代表愿意处理该gesture,返回false代表不愿意处理该gesture。

onintercepttouchevent

现在我们将讨论一个新的方法:onintercepttouchevent,它只存在于viewgroup中,普通的view中没有这个方法。在任何一个view的ontouchevent被调用之前,它的父辈们(ancestors)将先获得拦截这个事件的一次机会,换句话说,它们可以窃取该事件。在刚才的“处理事件”部分中,我们遗漏了这一过程,现在,让我们把它加上:

  • down事件被传给a的onintercepttouchevent,该方法返回false,表示它不想拦截。
  • down又被传递给b的onintercepttouchevent,它也不想拦截,因此该方法也返回false。
  • 现在,down事件被传递到c的ontouchevent方法,该方法返回true,因为它想处理以该事件为首的手势(gesture)。
  • 现在,该手势的下一个事件move到来了。这个move事件再一次被传递给a的onintercepttouchevent方法,该方法再一次返回false,b也同样如此。
  • 然后,move事件被传递给c的ontouchevent,就像在前一部分中一样。
  • “手势剩余部分”中其他事件的处理过程和上面一样,假如a和b的onintercepttouchevent方法继续返回false的话。       这里有两点需要注意:
  • 虽然viewgroup a和b的onintercepttouchevent方法对down事件返回了false,后续的事件依然会传递给它们的onintercepttouchevent方法,这一点与ontouchevent的行为是不一样的。
  • 假如down事件传给c的ontouchevent方法时,它返回了false,down事件会继续向上传递给b和a的ontouchevent,即使它们在onintercepttouchevent方法中说它们不想拦截这个down事件,但没办法,没有子view愿意处理该事件。

个人理解:感谢@编程世界的孩子 的提醒,由此可见,down事件的处理实际上经历了一下一上两个过程,下是指a->b的onintercepttouchevent,上是指c->b->a的ontouchevent,当然,任意一步的方法中返回true,都能阻止它继续传播。

拦截事件

现在,让我们更进一步,假设b没有拦截down事件,但它拦截了接下来的move事件。原因可能是b是一个scrolling view。当用户仅仅在它的区域内点击(tap)时,被点击到的元素应当能处理该点击事件。但是当用户手指移动了一定的距离后,就不能再视该手势(gesture)为点击了——很明显,用户是想scroll。这就是为什么b要接管该手势(gesture)。
下面是事件被处理的顺序:

  • down事件被依次传到a和b的onintercepttouchevent方法中,它们都返回的false,因为它们目前还不想拦截。
  • down事件传递到c的ontouchevent方法,返回了true。
  • 在后续到来move事件时,a的onintercepttouchevent方法仍然返回false。
  • b的onintercepttouchevent方法收到了该move事件,此时b注意到用户手指移动距离已经超过了一定的threshold(或者称为slop)。因此,b的onintercepttouchevent方法决定返回true,从而接管该手势(gesture)后续的处理。
  • 然后,这个move事件将会被系统变成一个cancel事件,这个cancel事件将会传递给c的ontouchevent方法。
  • 现在,又来了一个move事件,它被传递给a的onintercepttouchevent方法,a还是不关心该事件,因此onintercepttouchevent方法继续返回false。
  • 此时,该move事件将不会再传递给b的onintercepttouchevent方法,该方法一旦返回一次true,就再也不会被调用了。事实上,该move以及“手势剩余部分”都将传递给b的ontouchevent方法(除非a决定拦截“手势剩余部分”)。
  • c再也不会收到该手势(gesture)产生的任何事件了。

下面的一些小事情可能会令你感到吃惊:

  • 如果一个viewgroup拦截了最初的down事件,该事件仍然会传递到该viewgroup的ontouchevent方法中。
  • 另一方面,如果viewgroup拦截了一个半路的事件(比如,move),这个事件将会被系统变成一个cancel事件,并传递给之前处理该手势(gesture)的子view,而且不会再传递(无论是被拦截的move还是系统生成的cancel)给viewgroup的ontouchevent方法。只有再到来的事件才会传递到viewgroup的ontouchevent方法中。

从此开始,你可以更进一步。比如对mouthful-method (实在不知道该怎么翻译啦!)requestdisallowintercepttouchevent,c可以用该方法阻止b窃取事件。如果你想更加疯狂一点,你可以在你自己的viewgroup中直接覆写dispatchtouchevent方法,并对传递进来的事件做任何你想做的处理。但这样的话你可能会破坏一些约定,所以应当小心。

如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网