Android事件分发

关于事件分发的文章确实很多,本文仅是用于自己对该知识点的总结和查漏,针对一些疑惑自问自答

描述

谈及事件分发,难免涉及到三个类,三个方法:

事件传递: Activity -> ViewGroup -> View (可理解为三层)

涉及的方法:dispatchTouchEvent, onInterceptTouchEvent(仅ViewGroup中存在), onTouchEvent

其中,dispatchTouchEvent在本层次中最先收到事件,可以在辞呈

为了加深印象,写了一个demo,如下图:

在三个相关的类中,分别重写涉及事件的三个方法,打上日志,点击View,日志如下:

1
2
3
4
5
6
7
8
9
2019-10-22 23:18:31.732 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:down
2019-10-22 23:18:31.733 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: dispatchTouchEvent:down
2019-10-22 23:18:31.733 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onInterceptTouchEvent:down
2019-10-22 23:18:31.734 31871-31871/com.wjp.eventtransferdemo D/CustomView: dispatchTouchEvent:down
2019-10-22 23:18:31.734 31871-31871/com.wjp.eventtransferdemo D/CustomView: onTouchEvent:down
2019-10-22 23:18:31.734 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onTouchEvent:down
2019-10-22 23:18:31.734 31871-31871/com.wjp.eventtransferdemo D/MainActivity: onTouchEvent:down
2019-10-22 23:18:31.780 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:up
2019-10-22 23:18:31.780 31871-31871/com.wjp.eventtransferdemo D/MainActivity: onTouchEvent:up

可以看到,action_down事件是从dispatchTouchEvent开始,从上到下传递(以树的结构,父View为上,子View为下),经过onInterceptTouchEvent过滤,这里我们不拦截down事件, 事件则能从上流到底端,后onTouchEvent则从下到上传递,可供各级View消费该事件,而之后的action_up事件为什么仅会从在Activity中传递呢?

这里引出一个最重要的结论:(个人觉得哈)
down事件是所有事件的起点,之后才会有其他事件传递过来,
方法onTouchEvent,决定了要不要消费之后分发过来的事件,如action_up, 在此次例子,默认的View和ViewGroup都返回false,所有他们表示都不要 消费事件,这样,使得之后的事件由最上层View来消费,(Activity中本身其实也可以理解为View,其中包含有Window,Window由包含有DecorView,即 DecorView是所有View的顶层View)。

为了验证此结论,将ViewGroup的onTouchEvent返回true,表示该ViewGroup要消费后续的事件,则之后的up事件只会传到ViewGroup消费,(同理View)

1
2
3
4
5
6
7
8
9
2019-10-22 23:48:03.346 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:down
2019-10-22 23:48:03.348 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: dispatchTouchEvent:down
2019-10-22 23:48:03.349 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onInterceptTouchEvent:down
2019-10-22 23:48:03.349 31871-31871/com.wjp.eventtransferdemo D/CustomView: dispatchTouchEvent:down
2019-10-22 23:48:03.349 31871-31871/com.wjp.eventtransferdemo D/CustomView: onTouchEvent:down
2019-10-22 23:48:03.350 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onTouchEvent:down
2019-10-22 23:48:03.400 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:up
2019-10-22 23:48:03.400 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: dispatchTouchEvent:up
2019-10-22 23:48:03.400 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onTouchEvent:up // up事件传给ViewGroup消费

也可在上面ViewGroup滑动,可以看到,所有的事件(move,up)都传给ViewGroup来消费:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
019-10-22 23:52:08.835 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:down
2019-10-22 23:52:08.835 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: dispatchTouchEvent:down
2019-10-22 23:52:08.836 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onInterceptTouchEvent:down
2019-10-22 23:52:08.836 31871-31871/com.wjp.eventtransferdemo D/CustomView: dispatchTouchEvent:down
2019-10-22 23:52:08.836 31871-31871/com.wjp.eventtransferdemo D/CustomView: onTouchEvent:down
2019-10-22 23:52:08.836 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onTouchEvent:down //这里返回true之后,之后的事件都给ViewGroup处理了,同时也不会传给上层(Activity消费了)
2019-10-22 23:52:08.931 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:move
2019-10-22 23:52:08.931 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: dispatchTouchEvent:move
2019-10-22 23:52:08.932 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onTouchEvent:move
2019-10-22 23:52:08.948 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:move
2019-10-22 23:52:08.981 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: dispatchTouchEvent:move
2019-10-22 23:52:08.981 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onTouchEvent:move
2019-10-22 23:52:08.992 31871-31871/com.wjp.eventtransferdemo D/MainActivity: dispatchTouchEvent:up
2019-10-22 23:52:08.992 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: dispatchTouchEvent:up
2019-10-22 23:52:08.992 31871-31871/com.wjp.eventtransferdemo D/CustomViewGroup: onTouchEvent:up

由此,可以得出一个通俗的结论,事件是从上到下询问,确定之后该由谁来处理后续的事件(接受处理任务),直至有人消费则停止询问,若无人去人给最上层处理。

由上面的结论,可得知,若是我(某个不知名的View)想消费事件,只要在down事件来临时,return true, 表示我可以接受任务,那么若是上层和下层都想要 消费事件,该由谁来消费呢?
默认情况下,下层的onTouchEvent先调用,所以当然优先由下层View来处理后续事件;那么若是上层View想要优先与下层来处理事件呢?

比如在ScrollView中,上下滑动时,开始先由子View来出来滑动事件,达到一定条件后拦截后续move事件让ScrollView来处理,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//ScrollView
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {

//2 . 判断是上下滑动时,将后续的滑动事件交由ScrollView来处理
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
return true;
}

//1 。开始的move事件不拦截,让子View处理,当上下滑动的大小达到一个系统滑动slop值时,(检测到用户是有意上下滑动),将isBeginDraggedd设为true
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
final int y = (int) ev.getY(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
if (yDiff > mTouchSlop && (getNestedScrollAxes() & SCROLL_AXIS_VERTICAL) == 0) {
mIsBeingDragged = true;
mLastMotionY = y;

}
}
}

写一个demo,一个WantToHandlerMoveEventView尽量的想要消费滑动事件,但是放在ScrollView里面,上下滑动该自定义View,日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
2019-10-24 00:31:57.948 27211-27211/com.wjp.eventtransferdemo D/ScrollViewInnerAct: dispatchTouchEvent:down
2019-10-24 00:31:57.948 27211-27211/com.wjp.eventtransferdemo D/WantToHandlerMoveEventView: dispatchTouchEvent:down
2019-10-24 00:31:57.948 27211-27211/com.wjp.eventtransferdemo D/WantToHandlerMoveEventView: onTouchEvent:down
2019-10-24 00:31:58.002 27211-27211/com.wjp.eventtransferdemo D/ScrollViewInnerAct: dispatchTouchEvent:move
2019-10-24 00:31:58.002 27211-27211/com.wjp.eventtransferdemo D/WantToHandlerMoveEventView: dispatchTouchEvent:move
2019-10-24 00:31:58.002 27211-27211/com.wjp.eventtransferdemo D/WantToHandlerMoveEventView: onTouchEvent:move //开始由该自定View消费该move事件
2019-10-24 00:31:58.018 27211-27211/com.wjp.eventtransferdemo D/ScrollViewInnerAct: dispatchTouchEvent:move
2019-10-24 00:31:58.019 27211-27211/com.wjp.eventtransferdemo D/WantToHandlerMoveEventView: dispatchTouchEvent:cancel
2019-10-24 00:31:58.019 27211-27211/com.wjp.eventtransferdemo D/WantToHandlerMoveEventView: onTouchEvent:cancel
2019-10-24 00:31:58.035 27211-27211/com.wjp.eventtransferdemo D/ScrollViewInnerAct: dispatchTouchEvent:move //达到一定滑动距离之后,后续滑动事件都由ScrollView来消费了
2019-10-24 00:31:58.380 27211-27211/com.wjp.eventtransferdemo D/ScrollViewInnerAct: dispatchTouchEvent:move
2019-10-24 00:31:58.381 27211-27211/com.wjp.eventtransferdemo D/ScrollViewInnerAct: dispatchTouchEvent:up

由此,我们又可以得到一个结论,若是上下层View都想消费同一个事件,上层View可通过onInterceptTouchEvent获得有权

这样,通过上面,基本了解三个相关方法的关系。

###相关demo