前言
仅用于知识点简介,详细描述建议百度,主要是一些个人的理解总结,可以在面试时来回答,所以并不是很详细,只是总结~
Android 点击事件
1. 传递过程
- 分发流程:Activity -> Window -> DecorView -> ViewGroup… -> View
- 消费流程:View -> ViewGroup… -> DecorView -> Window -> Activity
2. 图示
3. 源码解释
3.1 代码1 - Activity.dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 点击事件会在 onUserInteraction 通知
onUserInteraction();
}
// 向 Window 分发
if (getWindow().superDispatchTouchEvent(ev)) {
// 如果消耗 则 返回 true
return true;
}
// Window 下均未消耗,则自己处理 onTouchEvent()
return onTouchEvent(ev);
}
3.2 代码2 - ViewGroup.dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
...
// 检查是否拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
// 是否禁止拦截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 未禁止拦截,调用 onInterceptTouchEvent 检查是否拦截
intercepted = onInterceptTouchEvent(ev);
// 重新载入 action,所以在 onInterceptTouchEvent 中 ev 的 action 的变更是不会生效的
ev.setAction(action);
} else {
intercepted = false;
}
} else {
// 如果没有触摸目标 touchTarget 且 不是 Down 事件,那么说明从 Down 事件就与本视图无关,不关注后续事件,所以直接屏蔽
// 这也是为什么如果拦截了 Down 事件,那么后续的事件都收不到
intercepted = true;
}
...
if (!canceled && !intercepted) {
...
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
for (int i = childrenCount - 1; i >= 0; i--) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 检查点击范围内的 child
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
// 添加到 mFirstTouchTarget
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
...
if (mFirstTouchTarget == null) {
// 没有找到点击目标,自己处理点击事件
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// 根据 mFirstTouchTarget 逐级发放点击事件,根据需要向其中的 target 发放 cancel 事件
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;
}
}
...
return true;
}
3.3 代码3 - View.dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
...
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
// 处理滚动
result = true;
}
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
// 有 mOnTouchListener,且mOnTouchListener.onTouch 返回 true 消耗了事件
result = true;
}
if (!result && onTouchEvent(event)) {
// 如果之前的操作没有消耗事件,则会调用自己的 onTouchEvent 处理事件
result = true;
}
...
return result;
}