2. TouchGFX基础回顾

在进入多页面架构设计之前,有必要把 TouchGFX 的运行时骨架重新梳理一遍。很多页面跳转、数据刷新、动画卡顿的问题,根源并不在页面本身,而在于对引擎调度机制和 MVP 职责边界的理解偏差。本节围绕 TouchGFX 引擎、MVP 模式、View、Presenter、Model、Screen 六个核心概念展开,帮助你在后续多页面设计中做出正确的结构决策。

一、TouchGFX 引擎:驱动整个 UI 的“心跳”

TouchGFX 引擎是运行在目标板上的核心框架,它负责把应用逻辑与 LCD 刷新、触摸事件、DMA 传输连接起来。引擎并不直接绘制像素,而是通过抽象层调用硬件加速(如 LTDC、DMA2D、GPU2D)完成渲染。其关键职责包括:

  • 事件循环:持续采集触摸、按键与系统 tick,分发给当前活动 Screen。
  • 渲染调度:按帧率驱动重绘,只刷新发生变化的区域(dirty region),降低带宽占用。
  • 时间基准:为动画、定时器、过渡效果提供统一的 tick 源。
  • 内存管理:管理帧缓冲、位图缓存与动态内存池。

引擎的调度节奏决定了多页面切换的流畅度。若在 Presenter 中执行阻塞操作,会直接拖慢整个事件循环,表现为触摸无响应或动画掉帧。

二、MVP 模式:职责分离的设计基石

TouchGFX 采用 MVP(Model-View-Presenter)模式组织代码,其核心目的是把“界面长什么样”和“业务怎么处理”彻底分开。三者关系如下:

  • Model:持有应用数据与业务逻辑,不感知任何 UI 控件。
  • View:由 Designer 生成的界面类,负责控件布局、绘制与用户输入采集。
  • Presenter:连接 View 与 Model,响应 View 的事件、更新 Model,并把数据变化回写到 View。
MVP 的关键约束:View 不直接读写 Model,Model 不引用 View。所有跨层交互都经过 Presenter,这样界面可以独立替换,业务逻辑也能脱离硬件单独测试。

三、核心对象与协作关系

下面这张图展示了 TouchGFX 引擎、MVP 三层以及 Screen 之间的静态结构与数据流向。注意 View 与 Presenter 是双向绑定,而 Model 只与 Presenter 交互。

TouchGFX 引擎 View 控件布局 / 绘制 / 输入 Presenter 事件响应 / 数据转换 Model 业务数据 / 逻辑 Screen View + Presenter 组合单元 事件分发 / 帧调度 用户事件 更新界面 读写数据 数据变更 绑定为 Screen 成员

四、Screen:多页面架构的基本单元

Screen 是 TouchGFX 中可被激活、切换和销毁的界面单元。一个 Screen 通常由一对 View 与 Presenter 组成,并可能关联一个 Model 或共享全局 Model。Screen 的生命周期由引擎管理,典型阶段包括:

  1. 构造:创建 View 与 Presenter 对象,建立绑定关系。
  2. setupScreen:初始化控件、注册回调、加载初始数据。
  3. 激活:成为当前显示页面,开始接收触摸事件与 tick。
  4. tearDownScreen:离开页面时释放资源、停止定时器。
多页面架构中,Screen 的切换成本主要来自构造与资源加载。把耗时初始化放在 setupScreen 之外,或复用共享 Model,可以显著降低切换延迟。

五、核心对象职责对照

对象 核心职责 不应做的事 多页面中的角色
引擎 事件循环、渲染调度、时间基准 承载业务逻辑 统一驱动所有 Screen
View 控件布局、绘制、输入采集 直接访问 Model 每个 Screen 独立一份
Presenter 事件响应、数据转换、调用 Model 直接操作硬件寄存器 页面逻辑的集中点
Model 业务数据与逻辑 引用 View 或控件 可跨页面共享
Screen View + Presenter 的组合单元 承担全局状态 页面切换的基本单位

六、典型代码结构

在 TouchGFX Designer 生成的基础上,Presenter 通常通过 setView 与 View 绑定,并在 activate 中完成数据初始化。下面是一个简化示例:

class MainPresenter : public touchgfx::Presenter {
public:
    MainPresenter(MainView& v) : view(v) {}

    virtual void activate() {
        // 从 Model 读取数据并刷新界面
        view.setTemperature(model.getTemperature());
        view.setStatusText(model.getStatusText());
    }

    void onStartButtonClicked() {
        model.startMeasurement();
        view.setStatusText("测量中...");
    }

private:
    MainView& view;
    SystemModel& model = SystemModel::getInstance();
};

可以看到,Presenter 只做“翻译”和“调度”:把 View 的点击翻译成 Model 的操作,再把 Model 的状态翻译成 View 的显示。View 本身不关心温度从哪里来,Model 也不关心温度显示在哪个控件上。

七、常见误区与复盘

  • 误区一:在 View 中直接调用 Model。这会让界面与业务耦合,后续换屏或复用逻辑时改动面巨大。
  • 误区二:把全局状态塞进某个 Screen。Screen 会被销毁重建,全局数据应放在共享 Model 或单例中。
  • 误区三:在 Presenter 中做阻塞延时。会卡住引擎事件循环,导致触摸丢失和动画停顿。
  • 误区四:每个页面各建一个 Model。多页面之间数据不同步,容易出现“返回后数据没更新”的问题。
多页面架构中,最常见的返工原因是 Model 边界没有提前规划。建议在动手写第一个 Screen 之前,先列出所有页面共享的数据项,统一收敛到一个或少数几个 Model 中。

理解引擎的调度方式、MVP 的职责边界以及 Screen 的生命周期,是后续设计页面跳转、数据共享和过渡动画的前提。把这些基础对象的关系理清,多页面架构才不会退化成“一堆互相引用的界面类”。