2. TouchGFX基础
触摸响应快不快,说白了就看事件从手指到屏幕反馈走了多远的路。我刚开始用TouchGFX时,总觉得点一下按钮没反应,后来一查才发现事件在中间绕了一大圈。今天我们就把它拆开看看。
2.1 触摸事件的源头:硬件中断到驱动层
手指按下触摸屏,最先干活的是硬件。触摸控制器(比如FT5x06、GT911)检测到电容变化,通过I2C或SPI把坐标数据塞进寄存器,然后拉高中断引脚通知MCU。
MCU的中断服务函数里,驱动层会做三件事:
- 读取触摸坐标和触摸状态(按下/抬起/滑动)
- 将原始数据转换成屏幕坐标系下的像素坐标
- 调用
TouchController::sampleTouch()把数据喂给TouchGFX
关键点:驱动层的采样频率直接影响触摸响应延迟。我见过一个项目,驱动里加了10ms的延时滤波,结果触摸总感觉慢半拍。去掉后响应立刻跟手了。
2.2 事件分发核心:HAL层到框架层
TouchGFX的 HAL::touchSample() 会轮询驱动层拿到的坐标数据。拿到后,它不直接丢给控件,而是先做两件事:
- 坐标变换:如果屏幕旋转了,这里会做矩阵变换
- 多点去抖:连续两次采样坐标差小于阈值,认为是抖动,丢弃
处理完的数据被包装成 TouchEvent 对象,包含:
| 字段 | 说明 |
|---|---|
type |
事件类型:TOUCH_DOWN / TOUCH_UP / TOUCH_MOVE / TOUCH_CANCEL |
x, y |
触摸点在屏幕上的像素坐标 |
velocity |
滑动速度(用于惯性滚动等场景) |
我的习惯:在 HAL::touchSample() 里加个GPIO翻转,用示波器看中断到事件入队的延迟。如果超过2ms,就得优化驱动了。
2.3 事件消费流程:谁抢到了谁处理
TouchGFX的事件消费遵循「从顶到底,先到先得」的原则。流程是这样的:
Screen收到TouchEvent- 遍历所有
Container,检查触摸点是否在控件区域内 - 如果某个控件
isTouchable()返回 true,且坐标在它的getRect()内,就调用它的handleTouchEvent() - 如果控件消费了事件(返回 true),分发停止;否则继续往下找
这里有个坑:Z序。后添加的控件在Z序上层,会优先收到事件。我曾经遇到一个透明覆盖层挡住了所有按钮,排查了半天才发现是Z序问题。
我曾经踩过的坑:在 handleClickEvent() 里做了耗时操作(比如读写Flash),导致触摸事件队列阻塞。手指抬起了,事件还没处理完,界面卡死。解决方案是把耗时操作扔到 Application::postDelayedTask() 里异步执行。
2.4 事件流程图:从手指到回调
下面这张图把整个流程串起来了。我建议你把它打印出来贴在工位上。
2.5 消费规则详解:谁可以吃掉事件
一个控件要消费触摸事件,必须同时满足三个条件:
- 可见且启用:
isVisible() == true且isTouchable() == true - 坐标命中:触摸点在
getRect()范围内(考虑父容器的裁剪区域) - 未被上层控件拦截:Z序更高的控件没有先吃掉事件
你想想看,如果一个按钮被一个透明的 Box 盖住了,就算按钮的坐标命中逻辑是对的,事件也到不了它手里。这就是为什么我总强调:调试触摸问题先看Z序。
避坑指南:我曾经在 CustomContainer 里重写了 handleTouchEvent(),但忘了调用基类的 handleTouchEvent(),结果子控件永远收不到事件。嗯,这个坑我踩了两次才记住。
2.6 多点触摸与手势识别
TouchGFX原生支持最多5点触摸。每个触摸点独立走上面的流程,但 Screen 会维护一个 touchPoints 列表,记录所有活跃的触摸点。
手势识别(比如滑动、缩放)是在 Screen 层做的。它监听 TOUCH_MOVE 事件,计算两个触摸点之间的距离变化,然后触发 GestureEvent。我建议你在 handleGestureEvent() 里处理手势,而不是在 handleTouchEvent() 里自己算,因为框架已经帮你做了滤波和阈值判断。
| 手势类型 | 触发条件 | 常用场景 |
|---|---|---|
| SWIPE_HORIZONTAL | 水平滑动距离 > 30px,速度 > 200px/s | 页面左右切换 |
| SWIPE_VERTICAL | 垂直滑动距离 > 30px,速度 > 200px/s | 列表上下滚动 |
| PINCH | 双指间距变化 > 10% | 图片缩放 |
我的经验:手势识别的阈值要根据屏幕尺寸和用户群体调整。工业屏用户戴手套操作,滑动速度会偏慢,我把阈值从200px/s降到了120px/s,体验好了很多。
2.7 性能优化:减少事件传递路径长度
事件从产生到消费,路径越长延迟越大。我总结了几条优化原则:
- 减少嵌套层级:Container 嵌套不要超过5层,每层遍历都有开销
- 避免在事件回调中做重活:比如读写文件、网络请求,扔到后台任务里
- 合理使用
setTouchable(false):不需要触摸的控件关掉触摸检测,减少遍历次数 - 批量处理 MOVE 事件:如果每秒产生60个 MOVE 事件,没必要每个都触发重绘,可以合并成每16ms处理一次
嗯,这一章的内容就到这里。触摸事件机制是TouchGFX性能优化的基石,搞懂了它,后面讲响应延迟优化、帧率调优你才能听得明白。