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
13public 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
8public 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同时处理事件。