Android 事件分发小结

View的结构

事件分发流程

  • Android的事件分发机制是一个非常典型的责任链模式:上层View可以选择自己拦截事件,也可以交给子View来处理,如果子View选择不处理事件还可以继续回传给父View。
  • 事件收集之后最先传递给 Activity, 然后依次向下传递
    Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
  • 如果没有任何View消费掉事件,那么这个事件会按照反方向回传,最终传回给Activity,如果最后 Activity 也没有处理,本次事件将会被抛弃
    Activity <- PhoneWindow <- DecorView <- ViewGroup <- ... <- View

DispatchTouchEvent

  • 这个方法是事件分发机制中的核心,所有的事件调度都归它管。ViewGroup中这个方法负责处理子View的事件调度,View中这个方法负责onTouchListener、onTouchEvent的调度。
  • ViewGroup的dispatchTouchEvent伪代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean result = false;

    if (!onInterceptTouchEvent(ev)) {
    result = child.dispatchTouchEvent(ev);
    }

    if (!result) {
    result = super.dispatchTouchEvent(ev); //这里会调用View中默认的dispatchTouchEvent方法
    }

    return result;
    }
  • View的dispatchTouchEvent伪代码

    1
    2
    3
    4
    5
    6
    7
    8
    public boolean dispatchTouchEvent(MotionEvent event) {
    if (mOnTouchListener.onTouch(this, event)) {
    return true;
    } else if (onTouchEvent(event)) {
    return true;
    }
    return false;
    }
  • dispatchTouchEvent的返回值决定了是否消耗了当前的事件,不过dispatchTouchEvent的返回值一般和onTouchEvent保持一致。

OnInterceptTouchEvent

此方法只有ViewGroup拥有,返回值决定了是否拦截当前事件,ViewGroup中默认返回false。在事件到来时,此方法并不会每次都被调用,在一个事件序列中,如果返回了true,表示对剩下的的事件也会拦截,此方法将不再会被调用;如果返回了false,在剩下的事件到来时,此方法会被一直调用,直至返回true。这个逻辑由ViewGroup的dispatchTouchEvent来处理。

OnTouchEvent

  • 此方法中用来响应事件,返回值表示是否消耗本次事件,决定了dispatchTouchEvent的返回值,View的默认实现中封装了对onClickListener和onLongClickListener的处理。
  • ACTION_DOWN事件需要特别注意,这个事件表示一系列事件的起点,消耗了ACTION_DOWN事件才会收到后续的事件。这个逻辑由ViewGroup的dispatchTouchEvent来处理。
  • 重写这个方法后要注意点击事件可能不再有效,如RecyclerView

More

这套事件分发机制决定了一次触摸流程中产生的事件只能被同一个View消费,全部接收或者全部拒绝。Google意识到了这套机制的缺陷,在5.0后推出了NestedScrolling机制,可以实现子View和父View同时处理事件。