事件分发机制

注意:本文中所有源码分析部分均基于 API23(Android 6.0)

一 事件

事件 简介
ACTION_DOWN 手指 初次接触到屏幕 时触发。
ACTION_MOVE 手指 在屏幕上滑动 时触发,会会多次触发。
ACTION_UP 手指 离开屏幕 时触发。
ACTION_CANCEL 事件 被上层拦截 时触发。

对于单指触控来说,一次简单的交互流程是这样的:

手指落下(ACTION_DOWN) -> 移动(ACTION_MOVE) -> 离开(ACTION_UP)

ACTION_MOVE 有多次触发。
如果仅仅是单击,不会触发 ACTION_MOVE。

二 事件分发、拦截与消费

下表省略了 PhoneWidow 和 DecorView。

√ 表示有该方法。
X 表示没有该方法。

类型 方法 activity viewGroup view
事件分发 dispatchTouchEvent
事件拦截 onInterceptTouchEvent X X
事件消费 onTouchEvent

1 方法功能介绍

  1. dispatchTouchEvent 事件分发
    activity,view,viewGroup都拥有该方法
  2. onInterceptTouchEvent 事件拦截
    viewGroup独有的方法,负责如果viewGroup需要拦截事件,此函数返回true,交于viewGroup处理
  3. onTouchEvent

这个三个方法均有一个 boolean(布尔) 类型的返回值,通过返回 true 和 false 来控制事件传递的流程。
PS:从上表可以看到 Activity 和 View 都是没有事件拦截的,这是因为:

  1. Activity 作为原始的事件分发者,如果 Activity 拦截了事件会导致整个屏幕都无法响应事件,这肯定不是我们想要的效果。
  2. View最为事件传递的最末端,要么消费掉事件,要么不处理进行回传,根本没必要进行事件拦截。

2 事件分发、拦截与消费三个方法执行调用顺序

viewGroup :分发->拦截->消费
view :分发->消费
Activity :分发->消费

调用流程

事件分发机制_第1张图片
事件调用流程.jpg

activity 源码

activity #dispatchTouchEvent源码

 public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
// 调用phoneWindow的superDispatchTouchEvent
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

activity #onTouchEvent

 public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }
        return false;
    }

2 view各个事件的调用顺序

  1. 单击事件(onClickListener) 需要两个两个事件(ACTION_DOWN 和 ACTION_UP )才能触发,如果先分配给onClick判断,等它判断完,用户手指已经离开屏幕,黄花菜都凉了,定然造成 View 无法响应其他事件,应该最后调用。(最后)
  2. 长按事件(onLongClickListener) 同理,也是需要长时间等待才能出结果,肯定不能排到前面,但因为不需要ACTION_UP,应该排在 onClick 前面。(onLongClickListener > onClickListener)
  3. 触摸事件(onTouchListener) 如果用户注册了触摸事件,说明用户要自己处理触摸事件了,这个应该排在最前面。(最前)
  4. View自身处理(onTouchEvent) 提供了一种默认的处理方式,如果用户已经处理好了,也就不需要了,所以应该排在 onTouchListener 后面。(onTouchListener > onTouchEvent)
    所以事件的调度顺序应该是
    onTouchListener > onTouchEvent > onLongClickListener > onClickListener

三 事件分发流程

事件收集之后最先传递给 Activity, 然后依次向下传递,大致如下:

Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View

如果view没有处理事件,再反向交回事件,判断处理

Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View

核心要点

  1. 事件分发原理: 责任链模式,事件层层传递,直到被消费。
  2. ** View 的 dispatchTouchEvent 主要用于调度自身的监听器和 onTouchEvent。**
  3. ** View的事件的调度顺序是onTouchListener > onTouchEvent > onLongClickListener > onClickListener 。**
  4. ** 不论 View 自身是否注册点击事件,只要 View 是可点击的就会消费事件。**
  5. 事件是否被消费由返回值决定,true 表示消费,false 表示不消费,与是否使用了事件无关。
  6. ViewGroup 中可能有多个 ChildView 时,将事件分配给包含点击位置的 ChildView。
  7. ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),由 ChildView 消费。
  8. 一次触摸流程中产生事件应被同一 View 消费,全部接收或者全部拒绝。
  9. 只要接受 ACTION_DOWN 就意味着接受所有的事件,拒绝 ACTION_DOWN 则不会收到后续内容
  10. 如果当前正在处理的事件被上层 View 拦截,会收到一个 ACTION_CANCEL,后续事件不会再传递过来。

六 参考资料

安卓自定义View进阶-事件分发机制原理

你可能感兴趣的:(事件分发机制)