SurfaceFlinge/InputFlinger分析-android画面缩放后依然点击正常原理分析

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进行操作的缩放矩阵。

故到此整个环节就已经清楚了,总结一下如下图:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/4215.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深蓝学院C++基础与深度解析笔记 第 9 章 序列与关联容器

第 9 章 序列与关联容器 1. 容器概述 A、容器&#xff1a; 一种特殊的类型&#xff0c;其对象可以放置其它类型的对象&#xff08;元素&#xff09; – 需要支持的操作&#xff1a;对象的添加、删除、索引、遍历 – 有多种算法可以实现容器&#xff0c;每种方法各有利弊B、容…

Redis的高可用与持久化

目录 一、Redis 高可用1. 持久化2. 主从复制3. 哨兵4. 集群(cluster) 二、Redis 持久化方式1. 持久化的功能2. 持久化的方式 三、RDB 持久化1. 触发条件2.执行流程3. 启动时加载 四、AOF持久化1.开启 AOF2. 执行流程2.1 命令追加2.2 文件写入&#xff08;write&#xff09;和文…

【机器学习】——续上:卷积神经网络(CNN)与参数训练

目录 引入 一、CNN基本结构 1、卷积层 2、下采样层 3、全连接层 二、CNN参数训练 总结 引入 卷积神经网络&#xff08;CNN&#xff09;是一种有监督深度模型框架&#xff0c;尤其适合处理二维数据问题&#xff0c;如行人检测、人脸识别、信号处理等领域&#xff0c;是带…

threejs动画

个人博客地址: https://cxx001.gitee.io 前面我们所用的模型大都是静态的&#xff0c;没有动画&#xff0c;没有生命。这节我们将赋予它们生命。 动画本质是通过改变物体的旋转、缩放、位置、材质、顶点、面以及其它你所能想到的属性来实现的。这些其实在前面章节示例里或多或…

win11,win10睡眠自动被唤醒部分总结

网上查了很多的解决方法&#xff0c;试了关闭 启用快速启动&#xff0c;大致有几点 1. powercfg /lastwake cmd命令行输入powercfg /lastwake&#xff0c;可以查询最后一次被哪个设备唤醒 2. PowerCfg -DEVICEQUERY wake_armed 也是命令行输入&#xff0c;查询所有可以唤醒电…

基于Thinkphp6框架全新UI的AI网址导航系统源码

2023全新UI的AI网址导航系统源码&#xff0c;基于thinkphp6框架开发的 AI 网址导航是一个非常实用的工具&#xff0c;它能够帮助用户方便地浏览和管理自己喜欢的网站。 相比于其他的 AI 网址导航&#xff0c;这个项目使用了更加友好和易用的 ThinkPHP 框架进行搭建&#xff0c…

学习系统编程No.25【核心转储实战】

引言&#xff1a; 北京时间&#xff1a;2023/6/16/8:39&#xff0c;实训课中&#xff0c;大一下学期最后有课的一天&#xff0c;还有两天就要期末考啦&#xff01;目前什么都还没有复习&#xff0c;不到星期天晚上&#xff0c;咱不慌&#xff0c;小小挂科&#xff0c;岂能拦得…

系列二、Maven下载安装配置

一、下载 链接&#xff1a;https://pan.baidu.com/s/1BvwLzAk9kRSP-daxSYe4Vw?pwdyyds 提取码&#xff1a;yyds 二、安装 第一步&#xff1a;下载安装包 第二步&#xff1a;解压至安装目录&#xff0c;例如 第三步&#xff1a;配置settings.xml&#xff08;主要配置maven本…

STM32F407 滴答定时器

介绍STM32F407滴答定时器配置方法、使用方式&#xff0c;封装延时函数得到精确的时间。 【1】介绍滴答定时器的章节 STM32F407参考手册中第10章介绍了滴答定时器的校准值。 M4权威指南介绍滴答定时器的章节&#xff0c;M3权威指南中与M4权威指南中的介绍一样。 【2】滴答定时…

Linux--打印内容或者插入内容:echo

语法&#xff1a; echo 内容 作用&#xff1a;打印内容到显示器echo 内容 > 不存在的文件 作用&#xff1a;文件创建&#xff0c;并将内容插入新创建的文件中echo 内容 > 存在的文件 作用&#xff1a;覆盖文件原有的内容 echo 内容 >> 存在的文件 作用&#xff1a…

Tdengine 时序数据库-安装与客户端连接

使用 TDengine 时序数据库的版本是 2.4.0.0 使用的安装RPM的安装方便安装 TDengine-server-2.4.0.0-Linux-x64.rpm 1. 安装指令: rpm -ivh TDengine-server-2.4.0.0-Linux-x64.rpm [rootnode3 server]# rpm -ivh TDengine-server-2.4.0.0-Linux-x64.rpm Verifying... …

Java对称与非对称加密解密(AES与RSA)

尽可能预想所有残酷的可能性、因为现实永远让你无法预警,而且又吝于给人慈悲。——富坚义博 今天我们讨论一下秘钥这个东西 一、对称加密技术与非对称加密技术简述 加密技术可以分为对称与非对称两种。 对称加密、解密即加密与解密用的是同一把秘钥,常用的对称加密技术有DES,A…