Android事件分发机制及其拓展

Android事件分发机制

        Android触摸事件的流动方向是从父视图到子视图,在父视图将事件传递给子视图之前,父视图会回调onInterceptTouchEvent方法,检查是否要拦截后续事件。如果拦截,子视图会收到cancel事件,将不会再收到任何后续事件,同时父视图的onTouchEvent方法将会收到后续事件。如果不拦截,该事件会分发给子视图。虽然父视图可以随时拦截事件,但子视图也可以禁止父视图拦截。子视图通过调用父视图的requestDisallowInterceptTouchEvent方法,可以禁止父视图拦截事件,那么子视图将会收到后续所有事件。

       down事件是一次完整Android事件流的起点,有其特殊性。一般的父视图不会拦截down事件,这样down事件会顺着视图层级,一直传达到最顶层子视图。这样就形成了一个链条,每个链条节点的上游是其父视图,下游为其子视图。当down事件到达链条末端的子视图时,它可以表达对此事件不感兴趣–在onTouchEvent回调中返回false,那么它将会被从这个链条中删除。若它对这个事件感兴趣,那么这个事件链条就成功建立了。所以在down事件的整个传递过程中,会初步建立这个事件传递链条。然而这个链条,会因为上游的父视图在onInterceptTouchEvent和onTouchEvent中同时返回true,而被切断。之后这个链条的末端节点就变成了该父视图。

Andoid事件分发机制的局限性

       从上文的事件链条,可以看出消耗事件的永远是末端节点,而上游的父视图,只是不断进行观察,以等待合适时机去切断链条。现在流行的界面设计,往往会采用视图联动的方式,反馈用户的手势动作。简单点说,就是当手指在A视图上滑动时,与A视图并不在同一事件链条中的B视图会联动。显然以往的事件分发机制并不能很好的满足这种交互需求,因为真正消耗事件的可能是多个视图。

Android事件分发机制的扩展

         Android在原有事件分发机制的基础上对其进行了扩展。扩展的方式是以原有末端节点的onTouchEvent方法为起点,进行了事件再分发。这种再分发并不包含原有分发机制中的拦截处理,可以说是一种相对简化的事件分发模型。从末端节点开始,它会向上寻找对触摸事件感兴趣的父视图,如果找到就会建立与该父视图的联系,需要注意的是寻找到的父视图不一定是直接父视图。整个过程大致如下:在该末端节点处理之前,它会先将事件交由该父视图进行预先处理;紧接着会根据父视图的处理情况调整自己的滚动策略;最后再次将自己处理后的事件交由父视图处理。为了便于理解,在此举例说明:用户手指在屏幕滑动了10个像素,子视图交由父视图处理,该父视图消耗了4个像素,或者说父视图向上移动了4个像素;子视图得知父视图消耗了4个像素,它可能也会消耗4个像素,就是向上移动4个像素;最后还剩余2个像素,子视图再次将事件交由父视图,父视图根据自身情况,选择是否消耗剩余像素。

        形象点说,用户手指移动,就像是在画一个饼。饼的大小是固定的,子视图和父视图通过协商去分这个饼。当然在分配的过程中切口要漂亮,就是说要达到协调一致的效果。比如,在一个垂直先行布局中有上下两个子视图,当手指滚动下方视图的时候,上方视图也应该协调滚动,以维持两个子视图的相对位置不变。

后记

          后续我可能会用实例去说明整个事件分发过程,选取也是相当典型的实例–CoordinatorLayout有两个子视图AppBarLayout和SwipeRefreshLayout,同时SwipeRefreshLayout有一个子视图–RecyclerView.当然,我确实不敢写这篇文章,因为太复杂了,更何况是要用文字清晰的表达。说白了,我怀疑我是否能把这其中的逻辑说清楚。

IT文库 » Android事件分发机制及其拓展
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址