View的事件分发
4个操作:
- DOWN: 当用户手指按下时
- MOVE: 当用户开始滑动时
- UP: 用户抬起手指
- CANCEL: 取消操作,事件被无法到达时
3个方法:
- dispatchTouchEvent() 负责将事件分发到其子View或当前View中
- onInterceptTouchEvent() 仅存在与ViewGroup中,用于拦截点击事件
- onTouchEvent() 完成对点击事件的处理,消耗事件。
事件的传递顺序是 DecorView -> 子View/ViewGroup
事件发生在Activity先调用 getWindow().superDispatchTouchEvent(ev),如果返回true,那么事件被消费。如果返回false表示事件发下去却没有View可以进行处理,则最后return Activity自己的onTouchEvent方法。
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
PhoneWindow,PhoneWindow又回调DecorView的dispatchTouchEvent,所以事件从DecorView向下传。
class PhoneWindow
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
顶层 ViewGroup 的dispatchTouchEvent方法里:
- 如果这个 ViewGroup 的 onInterceptTouchEvent 返回 true 表示它要拦截这个事件,接着就会调用它的 onTouchEvent 进行处理。
- 如果不拦截则会交给它的子 view 继续进行分发, 如此反复直到事件被最终处理。
onInterceptTouchEvent()是ViewGroup的一个方法,目的是在系统向该ViewGroup及其各个childView触发onTouchEvent()之前对相关事件进行一次拦截.
- down事件首先会传递到onInterceptTouchEvent()方法
- 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。
- 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件。
- 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理。
- 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。
onTouchEvent() 用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。
ViewGroup里的onInterceptTouchEvent默认值是false这样才能把事件传给View里的onTouchEvent.
ViewGroup里的onTouchEvent默认值是false,后续MOVE和UP事件不会传递过来。
View里的onTouchEvent返回默认值是true.这样才能执行多次touch事件。
在View中常用的两个方法OnTouch和OnClick
在dispatchTouchEvent中,会首先判断OnTouchListener是否为空,如果设置了onTouchListener,那么OnTouchListener方法中的onTouch方法会被回调。onTouch方法返回true,则onTouchEvent方法不会被调用(onClick事件是在onTouchEvent中调用),否则会直接执行onTouchEvent事件。
dispatchTouchEvent -> onTouch(setOnTouchListener) -> onTouchEvent -> onClick
每个activity都对应一个窗口window,这个窗口是PhoneWindow的实例,PhoneWindow对应的布局是DecorView,是一个FrameLayout,DecorView内部又分为两部分,一部分是ActionBar,另一部分是ContentParent,即activity在setContentView对应的布局。