Android事件分发机制源码分析
- 一个事件时事件分发是从 Activity开始的(Activity.dispatchTouchEvent())
来自这里的图片
image.png - Android事件分发总是先传递到ViewGroup、再传递到View
image.png
当用户点击时先调用的是Activity的Activity.dispatchTouchEvent()
image.png
源码分析
1.Activity.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
onUserInteraction:
每当Key,Touch,Trackball事件分发到当前Activity就会被调用。如果你想当你的Activity在运行的时候,能够得知用户正在与你的设备交互,你可以override该方法。
这个回调方法和onUserLeaveHint是为了帮助Activities智能的管理状态栏Notification;特别是为了帮助Activities在恰当的时间取消Notification。
所有Activity的onUserLeaveHint 回调都会伴随着onUserInteraction。这保证当用户相关的的操作都会被通知到,例如下拉下通知栏并点击其中的条目。
注意在Touch事件分发过程中,只有Touch Down 即Touch事件的开始会触发该回调,不会在move 和 up 分发时触发(从Activity 源码中 dispatchTouchEvent 方法中确实是这么做的)。
onUserLeaveHint:
作为Activity的生命周期回调的部分,会在用户决定将Acitivity放到后台时被调用。例如:当用户按下Home键,onUserLeaveHint就会被调用。但是当来电话时,来电界面会自动弹出,onUserLeaveHint就不会被调用。当该方法被调用时,他会恰好在onPause调用之前。
若将getWindow().superDispatchTouchEvent(ev)设置为ture,不会执行下去,也不会分发下去
2.PhoneWindow.superDispatchTouchEvent(MotionEvent event)
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
mDecor这个就是DecorView,布局最顶层,也就是说明这个事件分发到了最顶层
3.ViewGroup.dispatchTouchEvent(MotionEvent event)
仅贴出关键代码
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//1.ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
// a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),就会让第二个值为true,从而进入到条件判断的内部
// b. 若在onInterceptTouchEvent()中返回true(即拦截事件),就会让第二个值为false,从而跳出了这个条件判断
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
// 2.通过for循环,遍历了当前ViewGroup下的所有子View
if (!canceled && !intercepted) {
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
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();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
ev.setTargetAccessibilityFocus(false);
}
//3.判断是否点击的区域是否在子View范围类
if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
}
}
// 若点击的是空白处(即无任何View接收事件) / 拦截事件(手动复onInterceptTouchEvent()//从而让其返回true)
// 调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
// 因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> //onClick(),即自己处理该事件,事件不会往下传递(具体请参考View事件的分发机制中的 //View.dispatchTouchEvent())
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
}
}
}
return handled;
}
1.ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件
a. 若在onInterceptTouchEvent()中返回false(即不拦截事件),从而分发给子类
b. 若在onInterceptTouchEvent()中返回true(即拦截事件),从而自己处理不会分发给子类
2.通过for循环,遍历了当前ViewGroup下的所有子View
3.判断是否点击的区域是否在子View范围类
若点击的是空白处(即无任何View接收事件)拦截事件(手动复onInterceptTouchEvent(从而让其返回true)
调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()
因此会执行ViewGroup的onTouch() ->> onTouchEvent() ->> performClick() ->> onClick(),即自己处理该事件,事件不会往下传递