Android源码分析(事件传递)

源码基于安卓8.0分析结果

  • 首先如何看安卓SDK源码,作者尝试过几种的方法,感觉这种比较方便
    把在本地找到的Android.jar 放到工程中的libs的目录下,直接编译,就可以看到PhoneWindow 和DecorView的源码了
    image.png
  • 结论:Android事件分发流程 = Activity -> ViewGroup -> View
    事件传递.png

    Activity事件分发机制

  • Activity事件分发机制(Activity源码分析)
    • 当一个点击事件发生时,事件最先传到ActivitydispatchTouchEvent()进行事件分发
      image.png
1
2
3
4
5
6
7
8
9
10
11
12
public boolean dispatchTouchEvent(MotionEvent ev) {
//第一点
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//第二点
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
//第三点
return onTouchEvent(ev);
}
  • 1、触发方法 onUserInteraction();这个事件触发的机制是DOWN事件
    image.png

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * onUserInteraction()
    * 作用:实现屏保功能
    * 注:
    * a. 该方法为空方法
    * b. 当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
    */
    public void onUserInteraction() {
    }
  • 2、触发方法 getWindow().superDispatchTouchEvent(ev)

    • getWindow():::mWindow = new PhoneWindow(this);

image.png

  • 定义:属于顶层View(DecorView)
    • 说明:
    • a. DecorView类是PhoneWindow类的一个内部类
    • b. DecorView继承自FrameLayout,是所有界面的父类
    • c. FrameLayout是ViewGroup的子类,故DecorView的间接父类 = ViewGroup
1
mDecor = (DecorView) preservedWindow.getDecorView();
  • 到此可以证明Activity的根布局是FrameLayout
    image.png

  • 调用父类的方法 = ViewGroup的dispatchTouchEvent()

    • 即 将事件传递到ViewGroup去处理,详细请看ViewGroup的事件分发机制
      1
      2
      3
      4
      5
      6
      7
      8

      private final class DecorView extends FrameLayout{
      public boolean superDispatchTouchEvent(MotionEvent event) {
      // 调用父类的方法 = ViewGroup的dispatchTouchEvent()
      // 即 将事件传递到ViewGroup去处理,详细请看ViewGroup的事件分发机制
      return super.dispatchTouchEvent(event);
      }
      }
  • 3、Activity.onTouchEvent(ev)分析:Activity中的onTouchEvent,个人理解在返回键的时候哦,退出App(或者是一个Activity的结束)

    1
    2
    3
    4
    5
    6
    7
    8
    public boolean onTouchEvent(MotionEvent event) {
    if (mWindow.shouldCloseOnTouch(this, event)) {
    finish();
    return true;
    }
    //只有在点击事件在Window边界外才会返回true,一般情况都返回false
    return false;
    }

image.png

  • 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
    1
    2
    3
    4
    5
    6
    7
    8
    /** @hide */
    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
    if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
    && isOutOfBounds(context, event) && peekDecorView() != null) {
    return true;
    }
    return false;
    }

Android事件分发流程.jpg

ViewGroup件分发机制

  • 上面Activity事件分发机制可知,ViewGroup事件分发机制从dispatchTouchEvent()开始

  • 分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
    image.png

  • a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
    • b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断

      调用dispatchTransformedTouchEvent方法的时机

      有个遍历ViewGroup所有的孩子
      image.png
  • 分析2:dispatchTransformedTouchEvent方法分析
    • 将运动事件转换为特定子视图的坐标空间,
    • 过滤不相关的指针ID,并在必要时重写其操作。
    • 如果孩子是无效的,假设位移事件将被发送到这个ViewGroup相反。
      image.png
  • 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
  • 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),
  • 即自己处理该事件,事件不会往下传递(具体请参考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
    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);
    // 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
    // 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),
    // 即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的View.dispatchTouchEvent())
    // 此处需与上面区别:子View的dispatchTouchEvent()

    // 若点击的是空白处(即无任何View接收事件) / 拦截事件(手动复写onInterceptTouchEvent(),从而让其返回true)
    if (child == null) {
    handled = super.dispatchTouchEvent(event);
    } else {
    //调用View onTouchEvent() ->> performClick() ->> onClick(),
    handled = child.dispatchTouchEvent(event);
    }
    event.setAction(oldAction);
    /**
    调用子View的dispatchTouchEvent后是有返回值的
    若该控件可点击,那么点击时,dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立
    于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出
    即把ViewGroup的点击事件拦截掉
    */
    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
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//手势输入的验证者
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
//如果事件以可访问性为焦点的视图为目标,这就是,开始
//正常事件分派。也许一个后代会处理点击。
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}

boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;

// 处理初始值。第一个点击的ACTION_DOWN 事件
if (actionMasked == MotionEvent.ACTION_DOWN) {
//在开始一个新的触摸手势时丢弃所有以前的状态。
//框架可能会取消上一个手势的上或取消事件。
//由于程序开关,ANR,或一些其他的状态变化。
cancelAndClearTouchTargets(ev);
resetTouchState();
}

//检查拦截。
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// // 判断值1:disallowIntercept = 是否禁用事件拦截的功能(默认是false),
// 可通过调用requestDisallowInterceptTouchEvent()修改
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// // 重点分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件

// 判断值2:intercepted= onInterceptTouchEvent(ev)返回值
/**
a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断
intercepted=false 不拦截
*/
intercepted = onInterceptTouchEvent(ev);
//恢复操作,以防更改
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
//没有触摸目标,这个动作不是初始的。
//因此,此视图组继续拦截触摸。
intercepted = true;
}

///如果被拦截,启动正常事件分派。如果已经有了
// /正在处理手势的视图,执行正常事件调度。
/**
a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断
*/
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}

// Check for cancelation. 检查取消。
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
//如果需要,指针更新的触摸目标更新列表。
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {

// /如果事件是针对accessiiblity焦点我们给它的
// 具有可访问性焦点的视图,如果它不处理它
// 我们清理标记,像往常一样把事件发给所有的孩子。
// /我们正在查找可访问性集中的主机以避免保留。
// /由于这些事件非常罕见。
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;

if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
//按下永远为0
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;

///清除这个指针id的早期触摸目标,以防它们
//变得不同步。
removePointersFromTouchTargets(idBitsToAssign);

final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);

///找到一个可以接收事件的孩子。
//对孩子进行前后扫描。
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//,遍历了当前ViewGroup下的所有子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);

///如果有可访问性焦点的视图,我们希望它
// 获取事件,如果不处理,我们将执行一个
// 正常调度。我们可以做双重迭代,但这是
// 更安全鉴于时间表。
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}

if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}

newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
///孩子在其范围内已经收到了触摸。
//给它一个新指针,除了它正在处理的指针。
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}

resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
//孩子想在自己的范围内接受边界。
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// 子索引点预设列表,找到原始指标
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}

///可访问性焦点没有处理事件,所以很清楚。
//[并]向所有的孩子正常派遣。
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}

if (newTouchTarget == null && mFirstTouchTarget != null) {
//没有找到一个孩子来接这件事。
///将指针分配给最近添加的目标。
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}

// Dispatch to touch targets./发送到触摸目标。
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
//没有触及目标,所以把它当作普通的观点看待。
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// //发送到触摸目标,不包括新的触摸目标,如果我们已经
// 被派往。如有必要取消触摸目标。
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;
// 条件判断的内部调用了该View的dispatchTouchEvent()
// 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面的View事件分发机制)
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;
}
}

//更新需要更新或取消的触摸目标列表。
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 (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}

Activity事件分发流程(ViewGroup).png

Activity事件分发的流程(View.dispatchTouchEvent(event)

image.png
关键代码的分析:

    1. mOnTouchListener != null (设置setOnTouchListener(this))
    1. (mViewFlags & ENABLED_MASK) == ENABLED (这个控件能否点击,)
    1. mOnTouchListener.onTouch(this, event) (返回了true事件被拦截)
      只有以下3个条件都为真,dispatchTouchEvent()才返回true
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      // 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;
      // 否则执行onTouchEvent()
      // 1. mOnTouchListener != null
      // 2. (mViewFlags & ENABLED_MASK) == ENABLED
      // 3. mOnTouchListener.onTouch(this, event)

      /**
      * 条件2:(mViewFlags & ENABLED_MASK) == ENABLED
      * 说明:
      * a. 该条件是判断当前点击的控件是否enable
      * b. 由于很多View默认enable,故该条件恒定为true
      */
      //当li恒等于null的时候,后面的判断会执行
      //但是li在设置了setOnTouchListener就不为空,如果onTouch返回为true的话,
      // onclick就不会走了
      if (li != null && li.mOnTouchListener != null
      && (mViewFlags & ENABLED_MASK) == ENABLED
      && li.mOnTouchListener.onTouch(this, event)) {
      result = true;
      }

当上面的result返回为false的时候才会走

1
2
3
4
//没被拦截走到onTouchEvent(event)
if (!result && onTouchEvent(event)) {
result = true;
}

关于onTouchEvent方法:记住这点就行ACTION_UP—》手机抬起来了,才会执行performClick()

目前大多数的app都是只设置了setOnClickListener(),可以自己手动点击一个按钮,会发现只有抬起手,这个事件才会执行,如果让用户体验的比较好一点,对那些有强迫症的用户,应该也重写setOnTouchListener(this),但是记住不能拦击了,我的影像中,鲁大师App,就是重写了这个方法,所以在手指没有抬起来,下面tab的颜色已经发生了改变。

1
2
3
4
5
6
7
8
9
10
11
  // 若在onTouch()返回true,就会让上述三个条件全部成立,
// 从而使得View.dispatchTouchEvent()直接返回true,事件分发结束

// 若在onTouch()返回false,就会使得上述三个条件不全部成立,
// 从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});

image.png

只要我们通过setOnClickListener()为控件View注册1个点击事件 ,那么就会给mOnClickListener变量赋值(即不为空), 则会往下回调onClick() & performClick()返回true,performClick 就是模拟了一个请求,点击事件的请求,之类的东西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
// ListenerInfo mOnClickListener=getListenerInfo();
// getListenerInfo().mOnClickListener.onClick(this);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
  • 通过对performClick() 源码解读,你可能可以发现有点怪怪的东西,就是一个方法中的全局变量。以前觉得有点怪怪的,但是目前看来,需要去判断下是否为null,不用去调用方法初始化值。
1
2
3
4
5
6
7
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
  • 涉及到的所有的代码
    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
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
      @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public boolean dispatchTouchEvent(MotionEvent event) {
    // 如果事件应该通过可访问性焦点来处理。
    if (event.isTargetAccessibilityFocus()) {
    // 我们没有焦点,也没有虚拟后代,没有处理事件。
    if (!isAccessibilityFocusedViewOrHost()) {
    return false;
    }
    // 我们有焦点,得到事件,然后使用正常事件调度。
    event.setTargetAccessibilityFocus(false);
    }

    boolean result = false;

    if (mInputEventConsistencyVerifier != null) {
    mInputEventConsistencyVerifier.onTouchEvent(event, 0);
    }

    final int actionMasked = event.getActionMasked();
    if (actionMasked == MotionEvent.ACTION_DOWN) {
    //防御新手势
    stopNestedScroll();
    }

    if (onFilterTouchEventForSecurity(event)) {
    if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
    result = true;
    }
    //noinspection SimplifiableIfStatement
    ListenerInfo li = mListenerInfo;
    // 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;
    // 否则执行onTouchEvent()
    // 1. mOnTouchListener != null
    // 2. (mViewFlags & ENABLED_MASK) == ENABLED
    // 3. mOnTouchListener.onTouch(this, event)

    /**
    * 条件2:(mViewFlags & ENABLED_MASK) == ENABLED
    * 说明:
    * a. 该条件是判断当前点击的控件是否enable
    * b. 由于很多View默认enable,故该条件恒定为true
    */
    //当li恒等于null的时候,后面的判断会执行
    //但是li在设置了setOnTouchListener就不为空,如果onTouch返回为true的话,
    // onclick就不会走了
    if (li != null && li.mOnTouchListener != null
    && (mViewFlags & ENABLED_MASK) == ENABLED
    && li.mOnTouchListener.onTouch(this, event)) {
    result = true;
    }
    //没被拦截走到onTouchEvent(event)
    if (!result && onTouchEvent(event)) {
    result = true;
    }
    }

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

    //在嵌套滚动之后清理,如果这是手势的结束;
    //也取消了如果我们试着action_down但是我们不想休息
    //手势。
    if (actionMasked == MotionEvent.ACTION_UP ||
    actionMasked == MotionEvent.ACTION_CANCEL ||
    (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
    stopNestedScroll();
    }

    return result;
    }
    public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();
    // 若该控件可点击,则进入switch判断中
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

    if ((viewFlags & ENABLED_MASK) == DISABLED) {
    if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
    setPressed(false);
    }
    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
    // A disabled view that is clickable still consumes the touch
    // events, it just doesn't respond to them.
    return clickable;
    }
    if (mTouchDelegate != null) {
    if (mTouchDelegate.onTouchEvent(event)) {
    return true;
    }
    }
    // 若该控件可点击,则进入switch判断中
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
    switch (action) {
    // a. 若当前的事件 = 抬起View(主要分析)
    case MotionEvent.ACTION_UP:
    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
    if ((viewFlags & TOOLTIP) == TOOLTIP) {
    handleTooltipUp();
    }
    if (!clickable) {
    removeTapCallback();
    removeLongPressCallback();
    mInContextButtonPress = false;
    mHasPerformedLongPress = false;
    mIgnoreNextUpEvent = false;
    break;
    }
    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
    // take focus if we don't have it already and we should in
    // touch mode.
    boolean focusTaken = false;
    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
    focusTaken = requestFocus();
    }

    if (prepressed) {
    // The button is being released before we actually
    // showed it as pressed. Make it show the pressed
    // state now (before scheduling the click) to ensure
    // the user sees it.
    setPressed(true, x, y);
    }

    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
    // This is a tap, so remove the longpress check
    removeLongPressCallback();

    // Only perform take click actions if we were in the pressed state
    if (!focusTaken) {
    // Use a Runnable and post this rather than calling
    // performClick directly. This lets other visual state
    // of the view update before click actions start.
    if (mPerformClick == null) {
    mPerformClick = new PerformClick();
    }
    if (!post(mPerformClick)) {
    // 执行performClick() ->>分析1
    performClick();
    }
    }
    }

    if (mUnsetPressedState == null) {
    mUnsetPressedState = new UnsetPressedState();
    }

    if (prepressed) {
    postDelayed(mUnsetPressedState,
    ViewConfiguration.getPressedStateDuration());
    } else if (!post(mUnsetPressedState)) {
    // If the post failed, unpress right now
    mUnsetPressedState.run();
    }

    removeTapCallback();
    }
    mIgnoreNextUpEvent = false;
    break;
    // b. 若当前的事件 = 按下View
    case MotionEvent.ACTION_DOWN:
    if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
    mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
    }
    mHasPerformedLongPress = false;

    if (!clickable) {
    checkForLongClick(0, x, y);
    break;
    }

    if (performButtonActionOnTouchDown(event)) {
    break;
    }

    // Walk up the hierarchy to determine if we're inside a scrolling container.
    boolean isInScrollingContainer = isInScrollingContainer();

    // For views inside a scrolling container, delay the pressed feedback for
    // a short period in case this is a scroll.
    if (isInScrollingContainer) {
    mPrivateFlags |= PFLAG_PREPRESSED;
    if (mPendingCheckForTap == null) {
    mPendingCheckForTap = new CheckForTap();
    }
    mPendingCheckForTap.x = event.getX();
    mPendingCheckForTap.y = event.getY();
    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
    } else {
    // Not inside a scrolling container, so show the feedback right away
    setPressed(true, x, y);
    checkForLongClick(0, x, y);
    }
    break;
    // c. 若当前的事件 = 结束事件(非人为原因)
    case MotionEvent.ACTION_CANCEL:
    if (clickable) {
    setPressed(false);
    }
    removeTapCallback();
    removeLongPressCallback();
    mInContextButtonPress = false;
    mHasPerformedLongPress = false;
    mIgnoreNextUpEvent = false;
    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
    break;
    // d. 若当前的事件 = 滑动View
    case MotionEvent.ACTION_MOVE:
    if (clickable) {
    drawableHotspotChanged(x, y);
    }

    // Be lenient about moving outside of buttons
    if (!pointInView(x, y, mTouchSlop)) {
    // Outside button
    // Remove any future long press/tap checks
    removeTapCallback();
    removeLongPressCallback();
    if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
    setPressed(false);
    }
    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
    }
    break;
    }
    // 若该控件可点击,就一定返回true
    return true;
    }
    // 若该控件不可点击,就一定返回false
    return false;
    }
    /**
    * Call this view's OnClickListener, if it is defined. Performs all normal
    * actions associated with clicking: reporting accessibility event, playing
    * a sound, etc.
    * *把这种观点的OnClickListener,如果它的定义。执行所有正常
    *与点击相关的操作:报告可访问性事件,播放
    *声音等。
    * @return True there was an assigned OnClickListener that was called, false
    * otherwise is returned.
    */
    // 只要我们通过setOnClickListener()为控件View注册1个点击事件
    // 那么就会给mOnClickListener变量赋值(即不为空)
    // 则会往下回调onClick() & performClick()返回true
    //performClick 就是模拟了一个请求,点击事件的请求,子类的东西
    public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
    playSoundEffect(SoundEffectConstants.CLICK);
    // TODO: 2018/4/18 这里看源码 有点意思
    // ListenerInfo mOnClickListener=getListenerInfo();
    // getListenerInfo().mOnClickListener.onClick(this);
    li.mOnClickListener.onClick(this);
    result = true;
    } else {
    result = false;
    }

    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

    notifyEnterOrExitForAutoFillIfNeeded(true);

    return result;
    }

Activity事件分发的流程(View.dispatchTouchEvent(event).png

最后做了一张图

事件分发.png


 上一篇
Android源码分析(Handler机制) Android源码分析(Handler机制)
源码基于安卓8.0分析结果关键类ActivityThread、Handler、Looper、Message、MessageQueue ActivityThread中的流程:应用程序入口是在ActivityThread的main方法中,程序启
2018-05-26 Shiming_Li
下一篇 
安卓代码、图片、布局、网络和电量优化 安卓代码、图片、布局、网络和电量优化
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 写在前面的话,前段时间写了一篇文章 二十三种设计模式,写的不详细,因为如果要写的很详细,估计一年半载都写不完,完全都是按照自己理解,每个设计模式就画了一个简单的图,同时
2018-05-06 Shiming_Li
  目录