ViewRoot.deliverPointerEvent()的具体过程
private void deliverPointerEvent(MotionEvent event) {
if (mTranslator != null) {
//1. 进行物理像素到逻辑像素的转换,在一般情况下,物理屏幕的像素等于操作系统中定义的屏幕像素,不需要转换,只有当两者不同时才需要转换。
mTranslator.translateEventInScreenToAppWindow(event);
}
boolean handled;
if (mView != null && mAdded) {
// enter touch mode on the down
boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
if (isDown) {
//2. 如果是DOWN消息,调用 ensureTouchMode(true)函数则进入触摸模式,与之相反的是"非触摸模式",即按键模式。该函数会引起相关的View状态的变化。
ensureTouchMode(true);
}
if(Config.LOGV) {
captureMotionLog("captureDispatchPointer", event);
}
if (mCurScrollY != 0) {
//3. 将屏幕坐标转换到视图坐标。触摸消息本身的坐标位置是相对于屏幕左上角,而View可以认为是没有边界的,它内部处理消息时所需要的坐标是相对于View本身的。
//1. 转换方法很简单,变量mCurScollY记录了该View在屏幕坐标中的Y轴滚动,对于ViewRoot而言,没有X轴的滚动,因为ViewRoot的宽度已经被设置为屏幕本身的宽度。
event.offsetLocation(0, mCurScrollY);
}
if (MEASURE_LATENCY) {
lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
}
//4. 调用mView.dispatchTouchEvent()将消息派发给根View,该函数内部会继续将消息派发到整个View树。
// 1. 对于Activity包含的窗口,根View就是 PhoneWindow 中的 DecorView 。
// 2. 对于非应用窗口,根View只是一个普通的ViewGroup。
handled = mView.dispatchTouchEvent(event);
if (MEASURE_LATENCY) {
lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
}
if (!handled && isDown) {
//5. 如果根View及其所有的子View都没有消耗该消息,最后处理屏幕边界偏移。作用是当用户正好触摸到屏幕的边界时,系统自动对原始消息进行一定的偏移,然后在新的偏移后的位置上寻找是否有匹配的View,如果有则将消息派发到该View。
int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
final int edgeFlags = event.getEdgeFlags();
int direction = View.FOCUS_UP;
int x = (int)event.getX();
int y = (int)event.getY();
final int[] deltas = new int[2];
if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
direction = View.FOCUS_DOWN;
if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
deltas[0] = edgeSlop;
x += edgeSlop;
} else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
deltas[0] = -edgeSlop;
x -= edgeSlop;
}
} else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
direction = View.FOCUS_UP;
if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
deltas[0] = edgeSlop;
x += edgeSlop;
} else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
deltas[0] = -edgeSlop;
x -= edgeSlop;
}
} else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
direction = View.FOCUS_RIGHT;
} else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
direction = View.FOCUS_LEFT;
}
if (edgeFlags != 0 && mView instanceof ViewGroup) {
View nearest = FocusFinder.getInstance().findNearestTouchable(
((ViewGroup) mView), x, y, direction, deltas);
if (nearest != null) {
event.offsetLocation(deltas[0], deltas[1]);
event.setEdgeFlags(0);
mView.dispatchTouchEvent(event);
}
}
}
}
}
private void deliverPointerEvent(MotionEvent event) {
if (mTranslator != null) {
//1. 进行物理像素到逻辑像素的转换,在一般情况下,物理屏幕的像素等于操作系统中定义的屏幕像素,不需要转换,只有当两者不同时才需要转换。
mTranslator.translateEventInScreenToAppWindow(event);
}
boolean handled;
if (mView != null && mAdded) {
// enter touch mode on the down
boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
if (isDown) {
//2. 如果是DOWN消息,调用 ensureTouchMode(true)函数则进入触摸模式,与之相反的是"非触摸模式",即按键模式。该函数会引起相关的View状态的变化。
ensureTouchMode(true);
}
if(Config.LOGV) {
captureMotionLog("captureDispatchPointer", event);
}
if (mCurScrollY != 0) {
//3. 将屏幕坐标转换到视图坐标。触摸消息本身的坐标位置是相对于屏幕左上角,而View可以认为是没有边界的,它内部处理消息时所需要的坐标是相对于View本身的。
//1. 转换方法很简单,变量mCurScollY记录了该View在屏幕坐标中的Y轴滚动,对于ViewRoot而言,没有X轴的滚动,因为ViewRoot的宽度已经被设置为屏幕本身的宽度。
event.offsetLocation(0, mCurScrollY);
}
if (MEASURE_LATENCY) {
lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
}
//4. 调用mView.dispatchTouchEvent()将消息派发给根View,该函数内部会继续将消息派发到整个View树。
// 1. 对于Activity包含的窗口,根View就是 PhoneWindow 中的 DecorView 。
// 2. 对于非应用窗口,根View只是一个普通的ViewGroup。
handled = mView.dispatchTouchEvent(event);
if (MEASURE_LATENCY) {
lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
}
if (!handled && isDown) {
//5. 如果根View及其所有的子View都没有消耗该消息,最后处理屏幕边界偏移。作用是当用户正好触摸到屏幕的边界时,系统自动对原始消息进行一定的偏移,然后在新的偏移后的位置上寻找是否有匹配的View,如果有则将消息派发到该View。
int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
final int edgeFlags = event.getEdgeFlags();
int direction = View.FOCUS_UP;
int x = (int)event.getX();
int y = (int)event.getY();
final int[] deltas = new int[2];
if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
direction = View.FOCUS_DOWN;
if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
deltas[0] = edgeSlop;
x += edgeSlop;
} else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
deltas[0] = -edgeSlop;
x -= edgeSlop;
}
} else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
direction = View.FOCUS_UP;
if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
deltas[0] = edgeSlop;
x += edgeSlop;
} else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
deltas[0] = -edgeSlop;
x -= edgeSlop;
}
} else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
direction = View.FOCUS_RIGHT;
} else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
direction = View.FOCUS_LEFT;
}
if (edgeFlags != 0 && mView instanceof ViewGroup) {
View nearest = FocusFinder.getInstance().findNearestTouchable(
((ViewGroup) mView), x, y, direction, deltas);
if (nearest != null) {
event.offsetLocation(deltas[0], deltas[1]);
event.setEdgeFlags(0);
mView.dispatchTouchEvent(event);
}
}
}
}
}