/**
* API 30
*/
@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
/**
* ViewGroup中的事件分发方法,每次有事件发生都会调用dispatchTouchEvent()方法
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 原理关键代码
// ↓ ↓ ↓
// 是否拦截事件
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
// FLAG_DISALLOW_INTERCEPT标志位可以帮助我们去有效的处理滑动冲突
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
// disallowIntercept:是否禁用事件拦截,通过ViewGroup中的requestDisallowInterceptTouchEvent()方法设置
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
// 如果ViewGroup不拦截事件,则将该事件下发给子View进行处理
if (!canceled && !intercepted) {
...
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
if (newTouchTarget == null && childrenCount != 0) {
...
final View[] children = mChildren;
// for循环遍历ViewGroup下所有的子View
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// 判断子View可见且没有播放动画(View.java)且点击位置落于View的范围内(ViewGroup.java)
// 如果不满足条件就循环下一个View
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
...
newTouchTarget = getTouchTarget(child);
// 满足条件的child传入dispatchTransformedTouchEvent()方法中
// 这个方法内部调用了child.dispatchTouchEvent()方法
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;
// 子View处理了事件,则break跳出for循环
break;
}
...
}
if (preorderedList != null) preorderedList.clear();
}
...
}
}
...
return handled;
}
/**
* ViewGroup特有的事件拦截方法
* @retrun true表示拦截事件(需要手动重写让其返回true),false表示不拦截
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
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;
}
/**
* 判断点击是否在View范围内
*/
protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) {
final float[] point = getTempLocationF();
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
}
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
/**
* 事件分发从Activity的dispatchTouchEvent()方法开始
*/
public boolean dispatchTouchEvent(MotionEvent ev){
// 一般事件流都是从ACTION_DOWN开始,所以基本上都是true
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// 屏保功能方法,是个空方法
// 当此Activity位于栈顶时,点击home、back、meun键等都会触发这个方法
onUserInteraction();
}
// getWindow()获取Window对象,这是个抽象类,唯一实现类是PhoneWindow
// 所以这里最后是调用PhoneWindow中的superDispatchTouchEvent()方法
// 调用路线是:Activity -> Window -> PhoneWindow -> DecorView -> ViewGroup
if (getWindow().superDispatchTouchEvent(ev)) {
// 如果getWindow().superDispatchToucheEvent()返回true,则Activity.dispatchTouchEvent就返回true,
// 就表示该方法执行结束
return true;
}
// 上面的没走的话调用Activity.onTouchEvent()
return onTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent event) {
// 当一个点击事件没有被Activity下的任一个View接受或者处理时
// 判断点击是否在Window边界外
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
// 返回ture表示在Window边界外
return true;
}
// 返回false表示在边界内
return false;
}
}
/**
- 顶层View
*/
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
public boolean superDispatchTouchEvent(MotionEvent event) {
// 调用父类ViewGroup的事件分发方法
return super.dispatchTouchEvent(event);
}
}
/**
* Window类的唯一实现类
*/
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// 顶层View
private DecorView mDecor;
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
// 调用DecorView中的superDispathchTouchEvent()方法
return mDecor.superDispatchTouchEvent(event);
}
}
/**
* 安卓View中的方法解析
*/
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
/**
* 判断view可见且没有播放动画的方法
*/
protected boolean canReceivePointerEvents() {
return (mViewFlags & VISIBILITY_MASK) == VISIBLE || getAnimation() != null;
}
}
public abstract class Window {
// 由Activity.superDispatchTouchEvent()调用
public abstract boolean superDispatchTouchEvent(MotionEvent event);
/**
* 由Activity.onTouchEvent()方法调用
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
final boolean isOutside =
event.getAction() == MotionEvent.ACTION_UP && isOutOfBounds(context, event)
|| event.getAction() == MotionEvent.ACTION_OUTSIDE;
// 主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等
if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
// 返回true表示事件被消费
return true;
}
// 返回false表示事件未被消费
return false;
}
}