IMS:开发者选项中指针位置
android11-release
开发者选项中指针位置开关
-
指针位置 pointer_location:Settings.System.POINTER_LOCATION
packages/apps/Settings/src/com/android/settings/development/PointerLocationPreferenceController.java
WindowManagerService监听
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private final Uri mPointerLocationUri = Settings.System.getUriFor(Settings.System.POINTER_LOCATION);
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.javaPointerLocationView 创建并显示十字画线
PointerLocationView 显示
-
P: X / Y
P就是 pointers ; x 是 current number pointers, y 是 max number pointers ,这些都是指在一个完整gesture中的。也就是,当同时用三手指触摸时x=y=3,而当只抬起一根手指时,当前屏幕上只有两根手指了,但是整个手势事件中最大pointers数是3,所以,x=2,y=3。显示为P:2/3 -
X:640.9
Y:1250.9
X是active pointer的X轴坐标;Y是active pointer的Y轴坐标。当多点触摸时只有一个pointer是激活pointer(ActivePointer),所以X,Y表示的就是这个ActivePointer的X和Y轴坐标。dX和dY分别代表整个手势结束后活动点(ActivePointer)在X轴和Y轴方向上起始点到终止点的差值,其中X轴上从左到右为正值,Y轴上从上到下是正值,否则为负值。
-
Xv:0.0
Yv:0.0
Xv和Yv分别代表了pointer当前触摸点point的X轴和Y轴方向上的速度,该速度是将MotionEvent传入VelocityTracker对象中通过计算得到的有正负区分,X轴向右,Y轴向下代表了正方向,否则为负数。多点触摸的情况下,Xv和Yv代表了ActivePointer的状态。 -
Prs:0.50
Prs 表示 Press 是一个归一化值,代表一个手指或者其他设备作用在屏幕上的压力值。它是MotionEvent内部类PointerCoords 中的一个属性,取值范围为0~1,但是也会有大于1的值出现,这取决于设备设置。 -
Size:0.0
Size 也是 MotionEvent 内部类 PointerCoords 中的另一个属性,描述了设备的最大可探测区域上pointer touch area的近似大小,代表了屏幕被按压区域的近似大小,该值可以用在决定是否是fat touch event上,取值范围为0~1
PointerLocationView中onDraw绘制
顶部Labels
绘制: final PointerState ps = mPointers.get(mActivePointerId);
// Labels
if (mActivePointerId >= 0) {
final PointerState ps = mPointers.get(mActivePointerId);
canvas.drawRect(0, mHeaderPaddingTop, itemW-1, bottom,mTextBackgroundPaint);
canvas.drawText(mText.clear()
.append("P: ").append(mCurNumPointers)
.append(" / ").append(mMaxNumPointers)
.toString(), 1, base, mTextPaint);
final int N = ps.mTraceCount;
if ((mCurDown && ps.mCurDown) || N == 0) {
canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
mTextBackgroundPaint);
canvas.drawText(mText.clear()
.append("X: ").append(ps.mCoords.x, 1)
.toString(), 1 + itemW, base, mTextPaint);
canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
mTextBackgroundPaint);
canvas.drawText(mText.clear()
.append("Y: ").append(ps.mCoords.y, 1)
.toString(), 1 + itemW * 2, base, mTextPaint);
} else {
float dx = ps.mTraceX[N - 1] - ps.mTraceX[0];
float dy = ps.mTraceY[N - 1] - ps.mTraceY[0];
canvas.drawRect(itemW, mHeaderPaddingTop, (itemW * 2) - 1, bottom,
Math.abs(dx) < mVC.getScaledTouchSlop()
? mTextBackgroundPaint : mTextLevelPaint);
canvas.drawText(mText.clear()
.append("dX: ").append(dx, 1)
.toString(), 1 + itemW, base, mTextPaint);
canvas.drawRect(itemW * 2, mHeaderPaddingTop, (itemW * 3) - 1, bottom,
Math.abs(dy) < mVC.getScaledTouchSlop()
? mTextBackgroundPaint : mTextLevelPaint);
canvas.drawText(mText.clear()
.append("dY: ").append(dy, 1)
.toString(), 1 + itemW * 2, base, mTextPaint);
}
canvas.drawRect(itemW * 3, mHeaderPaddingTop, (itemW * 4) - 1, bottom,
mTextBackgroundPaint);
canvas.drawText(mText.clear()
.append("Xv: ").append(ps.mXVelocity, 3)
.toString(), 1 + itemW * 3, base, mTextPaint);
canvas.drawRect(itemW * 4, mHeaderPaddingTop, (itemW * 5) - 1, bottom,
mTextBackgroundPaint);
canvas.drawText(mText.clear()
.append("Yv: ").append(ps.mYVelocity, 3)
.toString(), 1 + itemW * 4, base, mTextPaint);
canvas.drawRect(itemW * 5, mHeaderPaddingTop, (itemW * 6) - 1, bottom,
mTextBackgroundPaint);
canvas.drawRect(itemW * 5, mHeaderPaddingTop,
(itemW * 5) + (ps.mCoords.pressure * itemW) - 1, bottom, mTextLevelPaint);
canvas.drawText(mText.clear()
.append("Prs: ").append(ps.mCoords.pressure, 2)
.toString(), 1 + itemW * 5, base, mTextPaint);
canvas.drawRect(itemW * 6, mHeaderPaddingTop, w, bottom, mTextBackgroundPaint);
canvas.drawRect(itemW * 6, mHeaderPaddingTop,
(itemW * 6) + (ps.mCoords.size * itemW) - 1, bottom, mTextLevelPaint);
canvas.drawText(mText.clear()
.append("Size: ").append(ps.mCoords.size, 2)
.toString(), 1 + itemW * 6, base, mTextPaint);
}
手指点击Pointer trace
十字线绘制: final PointerState ps = mPointers.get(p);
// Pointer trace.
for (int p = 0; p < NP; p++) {
final PointerState ps = mPointers.get(p);
// Draw path.
final int N = ps.mTraceCount;
float lastX = 0, lastY = 0;
boolean haveLast = false;
boolean drawn = false;
mPaint.setARGB(255, 128, 255, 255);
for (int i=0; i < N; i++) {
float x = ps.mTraceX[i];
float y = ps.mTraceY[i];
if (Float.isNaN(x)) {
haveLast = false;
continue;
}
if (haveLast) {
canvas.drawLine(lastX, lastY, x, y, mPathPaint);
final Paint paint = ps.mTraceCurrent[i] ? mCurrentPointPaint : mPaint;
canvas.drawPoint(lastX, lastY, paint);
drawn = true;
}
lastX = x;
lastY = y;
haveLast = true;
}
if (drawn) {
// Draw velocity vector.
mPaint.setARGB(255, 255, 64, 128);
float xVel = ps.mXVelocity * (1000 / 60);
float yVel = ps.mYVelocity * (1000 / 60);
canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
// Draw velocity vector using an alternate VelocityTracker strategy.
if (mAltVelocity != null) {
mPaint.setARGB(255, 64, 255, 128);
xVel = ps.mAltXVelocity * (1000 / 60);
yVel = ps.mAltYVelocity * (1000 / 60);
canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
}
}
if (mCurDown && ps.mCurDown) {
// Draw crosshairs.
canvas.drawLine(0, ps.mCoords.y, getWidth(), ps.mCoords.y, mTargetPaint);
canvas.drawLine(ps.mCoords.x, 0, ps.mCoords.x, getHeight(), mTargetPaint);
// Draw current point.
int pressureLevel = (int)(ps.mCoords.pressure * 255);
mPaint.setARGB(255, pressureLevel, 255, 255 - pressureLevel);
canvas.drawPoint(ps.mCoords.x, ps.mCoords.y, mPaint);
// Draw current touch ellipse.
mPaint.setARGB(255, pressureLevel, 255 - pressureLevel, 128);
drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.touchMajor,
ps.mCoords.touchMinor, ps.mCoords.orientation, mPaint);
// Draw current tool ellipse.
mPaint.setARGB(255, pressureLevel, 128, 255 - pressureLevel);
drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor,
ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint);
// Draw the orientation arrow.
float arrowSize = ps.mCoords.toolMajor * 0.7f;
if (arrowSize < 20) {
arrowSize = 20;
}
mPaint.setARGB(255, pressureLevel, 255, 0);
float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation)
* arrowSize);
float orientationVectorY = (float) (-Math.cos(ps.mCoords.orientation)
* arrowSize);
if (ps.mToolType == MotionEvent.TOOL_TYPE_STYLUS
|| ps.mToolType == MotionEvent.TOOL_TYPE_ERASER) {
// Show full circle orientation.
canvas.drawLine(ps.mCoords.x, ps.mCoords.y,
ps.mCoords.x + orientationVectorX,
ps.mCoords.y + orientationVectorY,
mPaint);
} else {
// Show half circle orientation.
canvas.drawLine(
ps.mCoords.x - orientationVectorX,
ps.mCoords.y - orientationVectorY,
ps.mCoords.x + orientationVectorX,
ps.mCoords.y + orientationVectorY,
mPaint);
}
// Draw the tilt point along the orientation arrow.
float tiltScale = (float) Math.sin(
ps.mCoords.getAxisValue(MotionEvent.AXIS_TILT));
canvas.drawCircle(
ps.mCoords.x + orientationVectorX * tiltScale,
ps.mCoords.y + orientationVectorY * tiltScale,
3.0f, mPaint);
// Draw the current bounding box
if (ps.mHasBoundingBox) {
canvas.drawRect(ps.mBoundingLeft, ps.mBoundingTop,
ps.mBoundingRight, ps.mBoundingBottom, mPaint);
}
}
}
ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
在 onPointerEvent 增加 PointerState ,属性 MotionEvent 事件上报
PointerEventDispatcher 注册和接收MotionEvent
mDisplayContent.registerPointerEventListener(mPointerLocationView) -> mPointerEventDispatcher.registerInputEventListener(listener)
PointerEventDispatcher 的 InputChannel,IMS:InputChannel通过socket发送Input给App 最后通过 onInputEvent 通知到 listeners[i].onPointerEvent(motionEvent),即是 PointerLocationView 的 onPointerEvent
@Override
public void onPointerEvent(MotionEvent event) {
final int action = event.getAction();
int NP = mPointers.size();
if (action == MotionEvent.ACTION_DOWN
|| (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
if (action == MotionEvent.ACTION_DOWN) {
for (int p=0; p<NP; p++) {
final PointerState ps = mPointers.get(p);
ps.clearTrace();
ps.mCurDown = false;
}
mCurDown = true;
mCurNumPointers = 0;
mMaxNumPointers = 0;
mVelocity.clear();
if (mAltVelocity != null) {
mAltVelocity.clear();
}
}
mCurNumPointers += 1;
if (mMaxNumPointers < mCurNumPointers) {
mMaxNumPointers = mCurNumPointers;
}
final int id = event.getPointerId(index);
while (NP <= id) {
PointerState ps = new PointerState();
mPointers.add(ps);
NP++;
}
if (mActivePointerId < 0 ||
!mPointers.get(mActivePointerId).mCurDown) {
mActivePointerId = id;
}
final PointerState ps = mPointers.get(id);
ps.mCurDown = true;
InputDevice device = InputDevice.getDevice(event.getDeviceId());
ps.mHasBoundingBox = device != null &&
device.getMotionRange(MotionEvent.AXIS_GENERIC_1) != null;
}
final int NI = event.getPointerCount();
mVelocity.addMovement(event);
mVelocity.computeCurrentVelocity(1);
if (mAltVelocity != null) {
mAltVelocity.addMovement(event);
mAltVelocity.computeCurrentVelocity(1);
}
final int N = event.getHistorySize();
for (int historyPos = 0; historyPos < N; historyPos++) {
for (int i = 0; i < NI; i++) {
final int id = event.getPointerId(i);
final PointerState ps = mCurDown ? mPointers.get(id) : null;
final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
event.getHistoricalPointerCoords(i, historyPos, coords);
if (mPrintCoords) {
logCoords("Pointer", action, i, coords, id, event);
}
if (ps != null) {
ps.addTrace(coords.x, coords.y, false);
}
}
}
for (int i = 0; i < NI; i++) {
final int id = event.getPointerId(i);
final PointerState ps = mCurDown ? mPointers.get(id) : null;
final PointerCoords coords = ps != null ? ps.mCoords : mTempCoords;
event.getPointerCoords(i, coords);
if (mPrintCoords) {
logCoords("Pointer", action, i, coords, id, event);
}
if (ps != null) {
ps.addTrace(coords.x, coords.y, true);
ps.mXVelocity = mVelocity.getXVelocity(id);
ps.mYVelocity = mVelocity.getYVelocity(id);
mVelocity.getEstimator(id, ps.mEstimator);
if (mAltVelocity != null) {
ps.mAltXVelocity = mAltVelocity.getXVelocity(id);
ps.mAltYVelocity = mAltVelocity.getYVelocity(id);
mAltVelocity.getEstimator(id, ps.mAltEstimator);
}
ps.mToolType = event.getToolType(i);
if (ps.mHasBoundingBox) {
ps.mBoundingLeft = event.getAxisValue(MotionEvent.AXIS_GENERIC_1, i);
ps.mBoundingTop = event.getAxisValue(MotionEvent.AXIS_GENERIC_2, i);
ps.mBoundingRight = event.getAxisValue(MotionEvent.AXIS_GENERIC_3, i);
ps.mBoundingBottom = event.getAxisValue(MotionEvent.AXIS_GENERIC_4, i);
}
}
}
if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL
|| (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP) {
final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
final int id = event.getPointerId(index);
if (id >= NP) {
Slog.wtf(TAG, "Got pointer ID out of bounds: id=" + id + " arraysize="
+ NP + " pointerindex=" + index
+ " action=0x" + Integer.toHexString(action));
return;
}
final PointerState ps = mPointers.get(id);
ps.mCurDown = false;
if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL) {
mCurDown = false;
mCurNumPointers = 0;
} else {
mCurNumPointers -= 1;
if (mActivePointerId == id) {
mActivePointerId = event.getPointerId(index == 0 ? 1 : 0);
}
ps.addTrace(Float.NaN, Float.NaN, false);
}
}
invalidate();
}
绘制属性
待续~~~