Senior programmer Andrews take you to learn View event distribution with another point of view!

I can not forget that night tortured three years ago - I was first learning to distribute the View event, the night he was tortured network text.

Senior programmer Andrews take you to learn View event distribution with another point of view!

Online introductory article View events distributed enough it?

No, on the contrary, countless articles online explosion models until you read, but  rather a kind of "outsiders on a black car" feeling  - Yiyanbuge first on 30 chart, take you outside the city Rao one hundred laps, just do not go straight to explain why there is a phenomenon caused reason why it exists, it is so designed to solve the problem ......

Senior programmer Andrews take you to learn View event distribution with another point of view!

Compared to fog, clear conditions, the establishment of perceptual knowledge, they are more interested in self-packaging.

- There is no help I do not care, to scare people say.

To bluffing, even if only add trouble to others, wasted a lot of time, expense!

Senior programmer Andrews take you to learn View event distribution with another point of view!

It is to never forget that painful experience, so I will share with you in this article.

Here, three years ago I vowed to myself that I will at  the end of 200 words  to speak to understand, others have to say about a word 3000,5000 event distribution do not understand.

Not only that, I would like to additionally help you understand, event distribution process three small details: the design is so, for what consideration. By "know why", to better facilitate a deeper impression.

Have not read the small partners please do not worry, because today talking about the foundation, just read this one, you have not in vain!

View of the nature of the event distribution is recursive!

Senior programmer Andrews take you to learn View event distribution with another point of view!

What is recursive it? What is the nature of recursion is it?

As the name suggests, is a recursive include "delivery" processes and "return" process algorithm. When we look for the goal, that is in the "delivery" process, when we find the target, intend to perform a transaction from start to target, we have opened a "normalization" process.

If that's a bit abstract, may wish to combine real-world example to understand recursive:

Case: issued and reporting, it is a typical workplace tasks recursive

Led  top-down , stepwise assigned tasks, find the target actors, this is the "delivery" process.

直到找到合适的执行者时,便开启了 自下而上 的 “归”流程。若当前执行者无法让结果 OK,那么上报给他的上级,由他的上级来执行,如果上级也不 OK,那么继续向上,直到结果 OK 为止。

伪代码来表示,即:

boolean dispatch(){
    if (hasTargetChild) {
        return child.dispatch();
    } else {
        return executeByMySelf();
    }
}

View 事件分发为何要设计成递归呢?

如此设计,是为了与 View 的排版相呼应。

View 的排版规则是:嵌套越深的,显示层级越高。而显示层级越高,就越容易覆盖层级低的、被用户看见。

再加上,“所见即所得”,要求 “用户看到了什么,触控到的也该是什么”(简言之,操作要符合用户直觉)。

因此,正是考虑到嵌套越深,层级越高,触摸也通常会是交给层级高的来处理,因而也将事件分发设计成递归。

View 排版规则为何设计为“嵌套越深,显示层级越高”呢?

因为这符合常理。越外层的,作为父容器而充当背景,越里层的,作为子控件而至于前景。

<LinearLayout>
    <ScrollView>
        <TextView/>
    </ScrollView>
</LinearLayout>

所以,整个流程大致是怎样的呢?

首先我们要明确的 3 点是:

1.每次完整的事件分发流程,都包含自上而下的 “递”,和自下而上的 “归” 2 个流程。

2.每次完整的事件分发流程,都是针对一个事件(MotionEvent)完成的递归,而一个事件只对应着一个 ACTION,例如 ACTION_DOWN。

3.一次用户触摸操作,我们称之为一个事件序列。一个事件序列会包含 ACTION_DOWN、ACTION_MOVE ... ACTION_MOVE、ACTION_UP 等多个事件。(其中 ACTION_MOVE 的数量是从 0 到多个不等)

也即一个事件序列,包含从 ACTION_DOWN 到 ACTION_UP 的多次事件分发流程。

下面我用一张图概括 View 事件分发的递和归流程:

Senior programmer Andrews take you to learn View event distribution with another point of view!

事先分发包含 3 个重要方法:

dispatchTouchEventonInterceptTouchEventonTouchEvent

我们知道,View 和 ViewGroup 是组合模式的关系,因而 ViewGroup 为了分发的需要,会重写一些 View 的方法,就包括这里的 dispatchTouchEvent。

因而首先,在递的过程中,当前层级是执行 child.dispatchTouchEvent:

  • 如果 child 是 ViewGroup,那么实际执行的就是 ViewGroup 重写的 dispatchTouchEvent 方法。该方法内可以判断,是否在当前层级拦截当前事件、或是递给下一级。
  • 如果 child 是不再有 child 的 View 或 ViewGroup,那么实际执行的就是 View 类实现的 super.dispatchTouchEvent 方法。该方法内可以判断,如果 View enabled 并且实现了 onTouchListener,且 onTouch 返回 true,那么不执行 onTouchEvent,并直接返回结果。否则执行 onTouchEvent。

此外,在 onTouchEvent 中如果 clickable 并且实现了 onClickListener 或 onLongClickListener,那么会执行 onClick 或 onLongClick。

总之,走到没有 child 的层级,即意味着步入“归”流程,如果该层级的 super.dispatchTouchEvent 没有返回 true,那么将继续执行上一级的 super.dispatchTouchEvent,直到被某一级消费,也即返回 true 了为止。

Senior programmer Andrews take you to learn View event distribution with another point of view!

上面我们介绍了正常流程下,所会执行到的方法,包括 View 实现的 dispatchTouchEvent,ViewGroup 重写的 dispatchTouchEvent,以及 onTouchEvent。

其实在事件的 “递” 流程中,ViewGroup 可以在当前层级,通过设置 onInterceptTouchEvent 方法返回 true,来拦截事件的下发,而直接步入“归”流程。

正所谓 “上有正策、下有对策”。在 ViewGroup 可以拦截事件下发的同时,child 也可以通过 getParent.requestDisallowInterceptTouchEvent 方法,来阻止上一级的下发拦截。

Senior programmer Andrews take you to learn View event distribution with another point of view!

额外需要明确的5个小细节

细节1:明确消费的概念

要将 “消费” 和 “执行” 这两个概念明确区分开。

网上的内容总让人误以为,当前层级不消费,就是不执行 super.dispatchTouchEvent 了。

事实上,不消费,简单地理解就是,“事情做了、只是结果不 OK” —— 在归流程中,如果当前层级的 super.dispatchTouchEvent return true 了,那么再往上的层级都不再执行自己的 super.dispatchTouchEvent,而是直接 return true。并且,当前层级的下级,都执行过 super.dispatchTouchEvent,只是结果返回了 false 而已。

细节2:明确拦截的作用

网上的内容总是让人误以为,当前层级拦截了,就直接在当前层级消费了。

实际上,当前层级拦截了,只是提前结束了 “递” 流程,并从当前层级步入 “归” 流程而已。具体判定是在哪个层级被消费,还是根据 <细节1> 的指标:看在哪个层级的 super.dispatchTouchEvent return true。

细节3:拦截方法只走一次,不代表拦截只走一次

网上的内容总是让人误以为,本次 ACTION_DOWN 被拦截了,那么往后的 ACTION_MOVE 和 ACTION_UP 都不被拦截了。

实际上,是 onInterceptTouchEvent 方法只走一次,一旦走过,就会留下记号(mFirstTouchTarget == null)那么下一次直接根据这个记号来判断拦不拦截。

为什么这么设计呢?因为一连串的事件序列,要求在几百微秒内完成。如果每次都完整走一遍方法,那岂不耽误事?所以本着 “能省即省” 的原则,凡是已确认会拦截的,后续就不再走方法判断,而是直接走变量标记来判断。

细节4:ACTION_DOWN 不执行,那么没下次了

这个很好理解,和 <细节3> 同理。

连事件序列的第一个事件都不接了(父容器走后续事件的分发时发现 mFirstTouchTarget == null),那就意味着不接了呗 —— 那后续的活就不会交给你了(不会再走你的 super.dispatchTouchEvent 来试探),直接根据变量标记(mFirstTouchTarget == null)做出判断,“能省即省”。

细节5:内部拦截并不能阻止父容器对 ACTION_DOWN 的处理

也即在 child 的 onTouch、onTouchEvent 中调用 getParent.requestDisallowInterceptTouchEvent 时,被设计为对父容器的 ACTION_DOWN 无效 —— 在父容器 dispatchTouchEvent 时,会首先重置 mGroupFlags。( ViewGroup 正是根据 mGroupFlags 是否包含 FLAG_DISALLOW_INTERCEPT 来判断是否不拦截的)

Senior programmer Andrews take you to learn View event distribution with another point of view!

为什么这么设计呢?

这个问题读者可以想一想,欢迎在评论区留言 ~

####综上

  • View 事件分发的本质是递归。
  • 递归的本质是,任务的下发和结果的上报。
  • View 事件分发设计成递归,是为了配合 View 的排版规则,形成符合用户直觉的触控体验。
  • View 事件分发的对象是一个 MotionEvent。
  • 一次用户触控操作包含多个 MotionEvent(例如从 ACTION_DOWN 到 ACTION_UP ),也即会走多次事件分发流程。
  • 一次 View 事件分发流程包含 “递” 流程和 “归” 流程,“递” 流程可以因 ViewGroup 的拦截而提前步入 “归” 流程。
  • child 可以通过 getParent.requestDisallowInterceptTouchEvent 阻止父容器的拦截。因而需要差异化地配置阈值,来确保 child 执行 getParent.requestDisallowInterceptTouchEvent 优先于父容器 onInterceptTouchEvent 返回 true(不然都先被拦截了,child 哪有机会阻止?)
  • 在“归”流程中,唯有当前层级的 super.dispatchTouchEvent 返回了 true,才认定被消费,被消费前,下级都有干活,只是结果不 OK。被消费后,上级都不需要干活,直接向上传达消费者的功。

这样说,你理解了吗?

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

Senior programmer Andrews take you to learn View event distribution with another point of view!

上述【高清技术脑图】以及【配套的架构技术PDF】可以 加我wx:X1524478394 免费获取

When programmers easily, when a good programmer is a need to learn from junior programmer to senior programmer, architect from primary to senior architect, or to management, technical director from technical manager to each stage We need to have different capabilities. Early to determine their career direction, in order to throw off their peers at work and in capacity building.

Guess you like

Origin blog.51cto.com/14332859/2456097