EventDispatch

image-20220127160157749

Core

事件分发中有一个重要的规则:一个触控点的一个事件序列只能给一个view处理

分析:以DOWN事件为序列分发判定,ViewGroup为消费DOWN事件的View生成一个TouchTarget(这个TouchTarget就包含了该view的实例与触控id,id可以是多个以应对多指触控),后续MOVE、UP都会交给这个TouchTarget。如果TouchTarget为空则ViewGroup自己处理。如果viewGroup消费了down事件,那么子view将无法收到任何事件。

除非异常情况:

1、比如ScrollView嵌套ScrollView情况下DOWN事件虽然MOVE事件被外ScrollView拦截而生成CANCEL事件分发给内ScrollView)。

2、界面跳转等情况。

此时View需要对CANCEL事件做一些状态的恢复工作,如终止动画,恢复view大小等等。

viewGroup是如何分发(dispatch)事件的?

viewGroup分发事件信息分为三个步骤:拦截、寻找子控件、派发事件。

第一步会判断当前ViewGroup是否disallowIntercept,如果不是(也就是允许拦截)则调用onInterceptTouchEvent方法判断是否要进行拦截。(如果拦截则直接走自身的onTouchEvent())
第二步是如果这个事件是down事件并且没有拦截或取消,那么需要为他寻找一个消费此事件的子控件,如果找到则为他创建一个TouchTarget。
第三步是派发事件,如果存在TouchTarget,说明找到了消费事件序列的子view,直接分发给他。如果没有则交给自己处理。

Viewenable属性不影响onTouchEvent的默认返回值, 哪怕一个View是disable状态. 只要它的clickable或者longClickable有一个为true. 那么它的onTouchEvent()就返回true.

简述:当手指触碰到屏幕时,屏幕硬件底层产生中断上报并通过native层传到Activity开始事件分发。Down事件会使Activity先调用onUserInteration,之后由PhoneWindow(ViewGroup)调用superDispatchTouchEvent将事件分发给DecorView,如果DecorView的View树没有处理则再调用到Activity的onTouchEvent消费。

DecorView事实上是一个最顶层存在的一个FrameLayout,以DecorView开始从顶向下的开始ViewGroup的分发过程:首先判断ViewGroup是否被禁止拦截(ViewGroup.requestDisallowInterceptTouchEvent可使当前ViewGroup及其父至顶都不可拦截事件),没有禁止则先由ViewGroup.onInterceptTouchEvent返回true/false判断是否拦截事件,if true,则事件不再往下传递而是调用拦截者的ViewGroup.onTouchEvent,else 继续往下传递,ViewGroup往其child ViewGroup/View传递。ViewGroup则重复此过程。直到最底的View。

ViewView.dispathTouchEvent中分发:先判断本身是否有OnTouchListener且本身状态为enable,由该OnTouchListener.onTouch先处理,由其返回值为true/false,if true 则OnTouchListener已经消费了事件,else 调用View.onTouchEvent进行处理,同样返回true则为View消费了事件,返回false则由其父ViewGroup的onTouchEvent

ps:如果View没有消费ACTION_DOWN事件,则之后的ACTION_MOVE等事件都不会再接收。

经典伪代码

viewGroup是如何分发(dispatch)事件的?

viewGroup分发事件信息分为三个步骤:拦截、寻找子控件、派发事件。

第一步会判断当前ViewGroup是否disallowIntercept,如果不是(也就是允许拦截)则调用onInterceptTouchEvent方法判断是否要进行拦截。(如果拦截则直接走自身的onTouchEvent())
第二步是如果这个事件是down事件并且没有拦截或取消,那么需要为他寻找一个消费此事件的子控件,如果找到则为他创建一个TouchTarget。
第三步是派发事件,如果存在TouchTarget,说明找到了消费事件序列的子view,直接分发给他。如果没有则交给自己处理。

具体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class MViewGroup extends MView implements ViewParent {
private MView child;
private boolean isChildNeedEvent = false;
private boolean isSelfNeedEvent = false;
private boolean isDisallowIntercept = false;

public MViewGroup(MView child) {
this.child = child;
// 这里只是示意,实际中不建议这么写,会造成提前发布未构造完成的实例
child.parent = this;
}

@Override
public boolean dispatch(MotionEvent ev) {
boolean handled = false;

if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
clearStatus();

if (!isDisallowIntercept && onIntercept(ev)) {
isSelfNeedEvent = true;
handled = onTouch(ev);
}else{
handled = child.dispatch(ev);
if (handled) isChildNeedEvent = true;

if (!handled) { // 这里没有用 if else 是因为这样写上下一致,更清晰
handled = onTouch(ev);
if (handled) isSelfNeedEvent = true; // 这一步有没有必要呢?还是有的,会更清晰,否则下面的else要多写一个不太清晰的else
}
}
} else {
// 这里 isSelfNeedEvent 条件判断应该放在 isChildNeedEvent 前面
// 因为两个都为真的情况只能是自己之后通过 onIntercept 抢了控制权,那这之后的控制权就不会去 child 那儿了

// 真实源码中 mFirstTouchTarget(TouchTarget类)用来记录消费了DOWN事件的子View,即如果为空也就意味着DOWN事件会由自身处理。
// if (mFirstTouchTarget == null) isSelfNeedEvent = true 相当于 isChildNeedEvent = false,
if (isSelfNeedEvent) {
handled = onTouch(ev);
} else if (isChildNeedEvent) {
if (!isDisallowIntercept && onIntercept(ev)) {
isSelfNeedEvent = true;

MotionEvent cancel = MotionEvent.obtain(ev);
cancel.setAction(MotionEvent.ACTION_CANCEL);
handled = child.dispatch(cancel);
cancel.recycle();
} else {
handled = child.dispatch(ev);
}
}
// 这里不用再 else 了,因为如果 isSelfNeedEvent 和 isChildNeedEvent 都不为 true,上面不会再发事件下来了
}

if (ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
this.clearStatus();
}

return handled;
}

private void clearStatus() {
isChildNeedEvent = false;
isSelfNeedEvent = false;
isDisallowIntercept = false;
}

public boolean onIntercept(MotionEvent ev) {
return false;
}

@Override
public boolean onTouch(MotionEvent ev) {
return false;
}

@Override
public void requestDisallowInterceptTouchEvent(boolean isDisallowIntercept) {
this.isDisallowIntercept = isDisallowIntercept;
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(isDisallowIntercept);
}
}
}

简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean dispatchTouchEvent(MotionEvent event) {

boolean consume = false; //表示这个事件最终的处理结果

if (onInterceptTouchEvent(event)){
//事件被拦截自己处理
consume = onTouchEvent(event);
}else{
//事件被分发到子view的dispatchTouchEvent()中
consume = child.dispatchTouchEvent(event);
}
return consume;
}

流程图

touch

  1. onInterceptTouchEvent返回值true表示事件拦截, onTouch/onTouchEvent 返回值true表示事件消费。
  2. 触摸事件先交由Activity.dispatchTouchEvent。再一层层往下分发,当中间的ViewGroup都不拦截时,进入最底层的View后,开始由最底层的OnTouchEvent来处理,如果一直不消费,则最后返回到Activity.OnTouchEvent
  3. ViewGroup才有onInterceptTouchEvent拦截方法。在分发过程中,中间任何一层ViewGroup都可以直接拦截,则不再往下分发,而是交由发生拦截操作的ViewGroup的OnTouchEvent来处理。
  4. 子View可调用requestDisallowInterceptTouchEvent方法,来设置disallowIntercept=true,从而阻止父ViewGroup的onInterceptTouchEvent拦截操作。
  5. OnTouchEvent由下往上冒泡时,当中间任何一层的OnTouchEvent消费该事件,则不再往上传递,表示事件已处理。
  6. 如果View没有消费ACTION_DOWN事件,则之后的ACTION_MOVE等事件都不会再接收。
  7. 只要View.onTouchEvent是可点击或可长按,则消费该事件.
  8. onTouch优先于onTouchEvent执行,上面流程图中省略,onTouch的位置在onTouchEvent前面。当onTouch返回true,则不执行onTouchEvent,否则会执行onTouchEvent。onTouch只有View设置了OnTouchListener,且是enable的才执行该方法。
  1. 那主要的分发流程是什么:

    在程序的主界面情况下,布局的顶层view是DecorView,他会先把事件交给Activity,Activity调用PhoneWindow的方法进行分发,PhoneWindow会调用DecorView的父类ViewGroup的dispatchTouchEvent方法进行分发。也就是Activity->Window->ViewGroup的流程。ViewGroup则会向下去寻找合适的控件并把事件分发给他。

  2. 事件一定会经过Activity吗?

    不是的。我们的程序界面的顶层viewGroup,也就是decorView中注册了Activity这个callBack,所以当程序的主界面接收到事件之后会先交给Activity。
    但是,如果是另外的控件树,如dialog、popupWindow等事件流是不会经过Activity的。只有自己界面的事件才会经Activity。

  3. Activity的分发方法中调用了onUserInteraction()方法,你能说说这个方法有什么作用吗?

    好的。这个方法在Activity接收到down的时候会被调用,本身是个空方法,需要开发者自己去重写。
    通过官方的注释可以知道,这个方法会在我们以任意的方式开始与Activity进行交互的时候被调用。比较常见的场景就是屏保:当我们一段时间没有操作会显示一张图片,当我们开始与Activity交互的时候可在这个方法中取消屏保;另外还有没有操作自动隐藏工具栏,可以在这个方法中让工具栏重新显示。

  4. 前面你讲到最后会分发到viewGroup,那么viewGroup是如何分发事件的?

    viewGroup处理事件信息分为三个步骤:拦截、寻找子控件、派发事件。

    事件分发中有一个重要的规则:一个触控点的一个事件序列只能给一个view处理,除非异常情况。所以如果viewGroup消费了down事件,那么子view将无法收到任何事件。

    viewGroup第一步会判读这个事件是否需要分发给子view,如果是则调用onInterceptTouchEvent方法判断是否要进行拦截。
    第二步是如果这个事件是down事件,那么需要为他寻找一个消费此事件的子控件,如果找到则为他创建一个TouchTarget。
    第三步是派发事件,如果存在TouchTarget,说明找到了消费事件序列的子view,直接分发给他。如果没有则交给自己处理。

  5. 你前面讲到“一个触控点的一个事件序列只能给一个view处理,除非异常情况”,这里有什么异常情况呢?如果发生异常情况该如何处理?

    这里的异常情况主要有两点:1.被viewGroup拦截,2.出现界面跳转等其他情况。

    当事件流中断时,viewGroup会发送一个ACTION_CANCEL事件给到view,此时需要做一些状态的恢复工作,如终止动画,恢复view大小等等。

  6. 哦?说到多指,那你知道ViewGroup是如何将多个手指产生的事件准确分发给不同的子view吗

    这个问题的关键在于MotionEvent以及ViewGroup内部的TouchTarget。

    每个MotionEvent中都包含了当前屏幕所有触控点的信息,他的内部用了一个数组来存储不同的触控id所对应的坐标数值。

    当一个子view消费了down事件之后,ViewGroup会为该view创建一个TouchTarget,这个TouchTarget就包含了该view的实例与触控id。这里的触控id可以是多个,也就是一个view可接受多个触控点的事件序列。

    当一个MotionEvent到来之时,ViewGroup会将其中的触控点信息拆开,再分别发送给感兴趣的子view。从而达到精准发送触控点信息的目的。

  7. 那view支持处理多指信息吗?

    View默认是不支持的。他在获取触控点信息的时候并没有传入触控点索引,也就是获取的是MotionEvent内部数组中的第一个触控点的信息。多指需要我们自己去重写方法支持他。

  8. 嗯嗯…那View是如何处理触摸事件的?

    首先,他会判断是否存在onTouchListener,存在则会调用他的onTouch方法来处理事件。如果该方法返回true那么就分发结束直接返回。而如果该监听器为null或者onTouch方法返回了false,则会调用onTouchEvent方法来处理事件。

    onTouchEvent方法中支持了两种监听器:onClickListener和onLongClickListener。View会根据不同的触摸情况来调用这两个监听器。同时进入到onTouchEvent方法中,无论该view是否是enable,只要是clickable,他的分发方法都是返回true。

    总结一下就是:先调用onTouchListener,再调用onClickListener和onLongClickListener。

  9. View 是最后一个处理事件分发的节点了为什么了还会有dispatchTouchEvent呢?

是因为,在该方法中,要对view的长按事件,触摸事件,点击事件进行调度处理

整理自:Android事件分发机制 - Gityuan博客 | 袁辉辉的技术博客

(32条消息) Android事件分发机制五:面试官你坐啊_一只修仙的猿-CSDN博客_android事件分发机制面试

相关源码

Activity和PhoneWindow相关

Activity是Android四大基本组件之一,当手指触摸到屏幕时,屏幕硬件一行行不断地扫描每个像素点,获取到触摸事件后,从底层产生中断上报。再通过native层调用Java层InputEventReceiver中的dispatchInputEvent方法。经过层层调用,交由Activity的dispatchTouchEvent方法来处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#Activity.java
class Activity extends...implement ... {
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//第一次按下操作时,用户希望能与设备进行交互,可通过实现该方法
onUserInteraction();
}

//获取当前Activity的顶层窗口是PhoneWindow [见PhoneWindow]
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//当没有任何view处理时,交由activity的onTouchEvent处理
return onTouchEvent(ev); // [见小节2.2.1]
}

public boolean onTouchEvent(MotionEvent event) {
//当窗口需要关闭时,消费掉当前event
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}

return false;
}
}

PhoneWindow的最顶View是DecorView,再交由DecorView处理。而DecorView的父类的父类是ViewGroup,接着调用 ViewGroup.dispatchTouchEvent()方法。

1
2
3
4
5
6
#PhoneWindow.java
PhoneWindow implement Window {
public boolean superDispatchTouchEvent(KeyEvent event) {
return mDecor.superDispatcTouchEvent(event); // [见小节2.4]
}
}

ViewGroup

首先判断ViewGroup是否被禁止拦截(ViewGroup.requestDisallowInterceptTouchEvent可使当前ViewGroup及其父至顶都不可拦截事件),没有禁止则先由ViewGroup.onInterceptTouchEvent返回true/false判断是否拦截事件,if true,则事件不再往下传递而是调用拦截者的ViewGroup.onTouchEvent,else 继续往下传递。

dispatchTransformedTouchEvent是真正处理ViewGroup事件的方法,其中会判断是否当前ViewGroup如果已经没有child ViewGroup/View了,则由自身的onTouchEvent处理;有的话ViewGroup 遍历child传入该方法中判断点击坐标是否在child区域中,是则调用对应child ViewGroup/View的dispatchTouchEvent分发。

ViewGroup则重复此过程。直到最底的View。

只有当Down事件被dispatchTouchEvent接受时才会继续处理其系列MOVE、UP等事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
//根据隐私策略而来决定是否过滤本次触摸事件,
if (onFilterTouchEventForSecurity(ev)) { // [见小节2.4.1]
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;

if (actionMasked == MotionEvent.ACTION_DOWN) {
// 发生ACTION_DOWN事件, 则取消并清除之前所有的触摸targets
cancelAndClearTouchTargets(ev);
resetTouchState(); // 重置触摸状态
}

// 发生ACTION_DOWN事件或者已经发生过ACTION_DOWN;才进入此区域,主要功能是拦截器
//只有发生过ACTION_DOWN事件,则mFirstTouchTarget != null;
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//可通过调用requestDisallowInterceptTouchEvent,当前ViewGroup及其到顶的父ViewGroup禁止拦截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//判断是否允许调用拦截器
if (!disallowIntercept) {
//调用拦截方法
intercepted = onInterceptTouchEvent(ev); // [见小节2.4.2]
ev.setAction(action);
} else {
intercepted = false;
}
} else {
// 当没有触摸targets,且不是down事件时,开始持续拦截触摸。
intercepted = true;
}
...

//不取消事件,同时不拦截事件, 并且是Down事件才进入该区域
if (!canceled && !intercepted) {
//把事件分发给所有的子视图,寻找可以获取焦点的视图。
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;

if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

//......
if (newTouchTarget == null && childrenCount != 0) {
//......
for (int i = childrenCount - 1; i >= 0; i--) {
//...... 省略若干代码......

//重置取消或抬起标志位
//如果触摸位置在child的区域内,则把事件分发给子View或ViewGroup
//================ <mark> dispatchTransformedTouchEvent <!mark>================
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // [见小节2.4.4]
// 获取TouchDown的时间点
mLastTouchDownTime = ev.getDownTime();
// 获取TouchDown的Index
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}

//获取TouchDown的x,y坐标
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//添加TouchTarget,则mFirstTouchTarget != null。 [见小节2.4.5]

//=========================!addTouchTarget!==========================
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//表示已经分发给NewTouchTarget
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
// 清除视图列表
if (preorderedList != null) preorderedList.clear();
}

if (newTouchTarget == null && mFirstTouchTarget != null) {
//将mFirstTouchTarget的链表最后的touchTarget赋给newTouchTarget
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}

// mFirstTouchTarget赋值是在通过addTouchTarget方法获取的;
// 只有处理ACTION_DOWN事件,才会进入addTouchTarget方法。
// 这也正是当View没有消费ACTION_DOWN事件,则不会接收其他MOVE,UP等事件的原因
if (mFirstTouchTarget == null) {
//没有触摸target,则由当前ViewGroup来处理。
//================<mark> dispatchTransformedTouchEvent <!mark>================
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
//如果View消费ACTION_DOWN事件,那么MOVE,UP等事件相继开始执行
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}

//当发生抬起或取消事件,更新触摸targets
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
} //此处大括号,是if (onFilterTouchEventForSecurity(ev))的结尾

//通知verifier由于当前时间未处理,那么该事件其余的都将被忽略
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) {
final boolean handled;

// 发生取消操作时,不再执行后续的任何操作
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}

final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

//由于某些原因,发生不一致的操作,那么将抛弃该事件
if (newPointerIdBits == 0) {
return false;
}

//分发的主要区域
final MotionEvent transformedEvent;
//判断预期的pointer id与事件的pointer id是否相等
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
//不存在子视图时,ViewGroup调用View.dispatchTouchEvent分发事件,再调用ViewGroup.onTouchEvent来处理事件
handled = super.dispatchTouchEvent(event); // [见小节2.4]
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
//将触摸事件分发给子ViewGroup或View;
//如果是ViewGroup,则调用代码(2.1);
//如果是View,则调用代码(3.1);
handled = child.dispatchTouchEvent(event);

event.offsetLocation(-offsetX, -offsetY); //调整该事件的位置
}
return handled;
}
transformedEvent = MotionEvent.obtain(event); //拷贝该事件,来创建一个新的MotionEvent
} else {
//分离事件,获取包含newPointerIdBits的MotionEvent
transformedEvent = event.split(newPointerIdBits);
}

if (child == null) {
//不存在子视图时,ViewGroup调用View.dispatchTouchEvent分发事件,再调用ViewGroup.onTouchEvent来处理事件
handled = super.dispatchTouchEvent(transformedEvent); // [见小节2.4]
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
//将该视图的矩阵进行转换
transformedEvent.transform(child.getInverseMatrix());
}
//将触摸事件分发给子ViewGroup或View;
//如果是ViewGroup,则 [见小节2.4]; 如果是View,则[见小节2.5];
handled = child.dispatchTouchEvent(transformedEvent);
}

//回收transformedEvent
transformedEvent.recycle();
return handled;
}
1
2
3
4
5
6
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}

View

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public boolean dispatchTouchEvent(MotionEvent event) {
...

final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
//在Down事件之前,如果存在滚动操作则停止。不存在则不进行操作
stopNestedScroll();
}

// mOnTouchListener.onTouch优先于onTouchEvent。
if (onFilterTouchEventForSecurity(event)) {
//当存在OnTouchListener,且视图状态为ENABLED时,调用onTouch()方法
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true; //如果已经消费事件,则返回True
}
//如果OnTouch()没有消费Touch事件则调用OnTouchEvent()
if (!result && onTouchEvent(event)) { // [见小节2.5.1]
result = true; //如果已经消费事件,则返回True
}
}

if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}

// 处理取消或抬起操作
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}

return result;
}
Author

white crow

Posted on

2021-04-10

Updated on

2024-03-25

Licensed under