hi,粉丝朋友们:
这两天刚好在做自由窗口相关国内需求,刚好遇到一个疑惑,那就是画面进行缩放后发现依然触摸画面可以正常反映问题。
具体疑惑背景
疑问点如下:
坐标是针对屏幕的,按钮也是相对Activity的,Activity本身宽度和屏幕一样只是画面被缩放了,Activity和View其实依然是屏幕的宽度,那么为啥屏幕坐标点点击到缩放Activity时候,按钮依然可以正常相应呢?
疑惑解答坐标追踪:
回忆坐标传递流程:
触摸事件坐标肯定是 inputdispatcher -----> app
inputdispatcher坐标确定
那么先确定inputDispatcher传递坐标,这个inputdispatcher的坐标其实是可以通过dumpys来查看:
RecentQueue: length=10MotionEvent(deviceId=11, eventTime=37449374871000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 909.9)]), policyFlags=0x62000000, age=1525msMotionEvent(deviceId=11, eventTime=37449382843000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 901.0)]), policyFlags=0x62000000, age=1517msMotionEvent(deviceId=11, eventTime=37449390835000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 898.9)]), policyFlags=0x62000000, age=1509msMotionEvent(deviceId=11, eventTime=37449470965000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 901.0)]), policyFlags=0x62000000, age=1429msMotionEvent(deviceId=11, eventTime=37449478951000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 903.0)]), policyFlags=0x62000000, age=1421msMotionEvent(deviceId=11, eventTime=37449486891000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 907.9)]), policyFlags=0x62000000, age=1413msMotionEvent(deviceId=11, eventTime=37449599726000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 904.9)]), policyFlags=0x62000000, age=1300msMotionEvent(deviceId=11, eventTime=37449607604000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 901.0)]), policyFlags=0x62000000, age=1292msMotionEvent(deviceId=11, eventTime=37449615411000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 896.9)]), policyFlags=0x62000000, age=1284msMotionEvent(deviceId=11, eventTime=37449783797000, source=TOUCHSCREEN | STYLUS, displayId=0, action=UP, actionButton=0x00000000, flags=0x00000000, metaState=0x00000000, buttonState=0x00000000, classification=NONE, edgeFlags=0x00000000, xPrecision=22.8, yPrecision=11.1, xCursorPosition=nan, yCursorPosition=nan, pointers=[0: (1011.9, 896.9)]), policyFlags=0x62000000, age=1116ms
明显发现坐标pointers=[0: (1011.9, 901.0)]), 这个属于屏幕的绝对坐标,这个时候在inputdispatcher的传递时候并没有被变化成和Activity的View一致坐标,即这里基本可以断定inputdispatcher传递到app依然是屏幕的绝对坐标。
app端坐标确定
app端的坐标,这里一般可以通过debug调试方式,或之打印log的方式:
这里在最初的的dispatchInputEvent进行了断点,发现坐标已经变成了 1341,1633了
private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}
即坐标一旦到了java层面其实就已经变成了和Activity正常的坐标了,那么只能继续往app层面地方往下追,这里就需要对native层面的input流程比较了解了,这里可以看马哥的input专题有讲解
那就来重点看看NativeInputEventReceiver::consumeEvents和InputConsumer.consume方法读取相关input数据情况,最终发现MotionEvent是在void InputConsumer::initializeMotionEvent进行构造出来的:
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {uint32_t pointerCount = msg->body.motion.pointerCount;PointerProperties pointerProperties[pointerCount];PointerCoords pointerCoords[pointerCount];for (uint32_t i = 0; i < pointerCount; i++) {pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties);pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);ALOGE("initializeMotionEvent: pointerCount x= %f y = %f" ,pointerCoords[i].getX(),pointerCoords[i].getY());}//这里是坐标变化的关键转换矩阵ui::Transform transform;transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});ui::Transform displayTransform;displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,msg->body.motion.txRaw, msg->body.motion.dtdyRaw,msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1});event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source,msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action,msg->body.motion.actionButton, msg->body.motion.flags,msg->body.motion.edgeFlags, msg->body.motion.metaState,msg->body.motion.buttonState, msg->body.motion.classification, transform,msg->body.motion.xPrecision, msg->body.motion.yPrecision,msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition,displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime,pointerCount, pointerProperties, pointerCoords);ALOGE("initializeMotionEvent:2 pointerCount x= %f y = %f" ,event->getX(0),event->getY(0));
}
这里加入了相关的打印如下:
06-15 23:40:24.413 1850 1850 E InputTransport: initializeMotionEvent: pointerCount x= 1045.942383 y = 930.961914
06-15 23:40:24.413 1850 1850 E InputTransport: initializeMotionEvent:2 pointerCount x= 1371.884766 y = 1693.923828
即可以看出,其实第一步从socket接受到的event坐标依然是和inputdispatcher一样,但是经过event->initialize方法后,再打印时候就发现已经变化了,变成和实际Activity一样的相匹配的坐标了。
其实本质原因还是因为initialize时候有传递相关的transform影响的,因为getX方法时候实际会调用到对应方法进行坐标转换返回:
/*** Get the X coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'.* Identical to calling getHistoricalX(pointerIndex, getHistorySize()).*/inline float getX(size_t pointerIndex) const {return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex);}float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const {return getHistoricalAxisValue(axis, pointerIndex, getHistorySize());
}float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,size_t historicalIndex) const {const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);//注意这个时候就是会有对应的mTransform进行影响,导致坐标和屏幕坐标产生差异return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
}
上面清楚了,那剩下问题就是这个mTransform根源是来自哪里呢?
这里就又要回到inputdispatcher是有传递类似这种mTransform的。
// Publish the motion event.status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,dispatchEntry->resolvedEventId,motionEntry.deviceId, motionEntry.source,motionEntry.displayId, std::move(hmac),dispatchEntry->resolvedAction,motionEntry.actionButton,dispatchEntry->resolvedFlags,motionEntry.edgeFlags, motionEntry.metaState,motionEntry.buttonState,motionEntry.classification,dispatchEntry->transform,//这里就是关键的transform也会传递motionEntry.xPrecision, motionEntry.yPrecision,motionEntry.xCursorPosition,motionEntry.yCursorPosition,dispatchEntry->rawTransform,motionEntry.downTime, motionEntry.eventTime,motionEntry.pointerCount,motionEntry.pointerProperties, usingCoords);
这个transform其实dumpsys的input中也有展示:
5: name='692067c com.example.myapplication11/com.example.myapplication11.MainActivity', id=163, displayId=0, inputConfig=0x0, alpha=1.00, frame=[360,84][1080,972], globalScale=1.000000, applicationInfo.name=ActivityRecord{f9088f2 u0 com.example.myapplication11/.MainActivity} t465}, applicationInfo.token=0x7079fae2c370, touchableRegion=[308,32][1133,1025], ownerPid=1850, ownerUid=10116, dispatchingTimeout=5000ms, hasToken=true, touchOcclusionMode=BLOCK_UNTRUSTEDtransform (ROT_0) (SCALE TRANSLATE)2.0000 -0.0000 -720.0000-0.0000 2.0000 -168.00000.0000 0.0000 1.0000
可以看到这个transform,大致矩阵即可以看出有放大2倍和偏移-720.0000,-168.0000,也正是这个转换才导致的由屏幕的绝对坐标转换到Activity的绝对坐标,转换后Activity可以获取到正常的坐标进行响应。
那么问题又来了请问inputdispatcher这个transform的转换又来自哪里呢?
哈哈这里就又回到之前input的专题讲解的,这个windowinfo信息实际是来自于SurfaceFlinger的,这里来看看dumpsys SurfaceFlinger是否又相关信息:
* Layer 0x71a7e2b523f0 (com.example.myapplication11/com.example.myapplication11.MainActivity#268)isSecure=false geomUsesSourceCrop=true geomBufferUsesDisplayInverseTransform=false geomLayerTransform (ROT_0) (SCALE TRANSLATE)0.5000 0.0000 360.00000.0000 0.5000 84.00000.0000 0.0000 1.0000
找到对应的window Layer确实看到了一个矩阵,但是好像这个矩阵和input的矩阵不一样,但是好像有点关系,这个关系是啥呢?哈哈就是一个类是逆向过程,相当于surface显示时候缩小到原来0.5,那么回去时候就需要放大2倍才可以还原。而surfaceflinger这个矩阵就是进行surfacecontrol进行操作的缩放矩阵。
故到此整个环节就已经清楚了,总结一下如下图: