4、UI组件基础(上):Box容器、TextArea文本显示、Button按钮、Image图像控件
好,咱们今天开始啃 TouchGFX 里最基础、也最常用的四个 UI 组件。说实话,不管多花哨的界面,拆开来看,底子就是这些玩意儿——容器、文字、按钮、图片。我刚开始用 TouchGFX 做项目时,就是靠这四个组件搭出了第一个能跑的原型。
你想想看,一个嵌入式设备的屏幕,无非就是:把东西摆好(Box),写上说明(TextArea),让用户点一下(Button),再放个图标装饰一下(Image)。就这么简单。但简单归简单,每个组件都有它的脾气,用不对照样翻车。
本章核心脉络:四个组件,各自独立又互相配合。Box 负责布局和背景,TextArea 负责信息输出,Button 负责交互输入,Image 负责视觉增强。掌握它们,你就掌握了 TouchGFX 界面搭建的 80%。
4.1 Box 容器——界面里的「隐形骨架」
Box 在 TouchGFX 里是最不起眼的组件,但也是我用得最多的。说白了,它就是一个矩形区域,可以设置颜色、透明度、位置和大小。没有交互,没有事件,就是个「安静的美男子」。
那它有什么用?我举几个实际场景:
- 背景色块:给整个屏幕或某个区域铺底色,比如深色模式下的黑色背景。
- 分割线:拉一个细长的 Box,颜色调浅,就是一条完美的分隔线。
- 占位符:在布局阶段先用 Box 占个位置,等后期再替换成实际组件。
- 容器分组:把多个组件放在一个 Box 上,统一移动或隐藏。
我的小技巧:调试布局时,我会给每个 Box 设置不同的半透明颜色,比如红色 30%、蓝色 30%。这样一眼就能看出每个组件的边界和重叠情况。调完再统一改成透明或最终颜色。
在 TouchGFX Designer 里拖一个 Box 出来,你会看到它的属性面板里有几个关键参数:
| 属性 | 说明 | 我的建议 |
|---|---|---|
| Color | 填充颜色,支持 24 位 RGB | 用色板选,别手写十六进制,除非你记性好 |
| Alpha | 透明度,0~255 | 做遮罩层时常用 100~150 的半透明值 |
| Position | x, y 坐标 | 建议用相对布局,别写死绝对坐标 |
| Size | 宽度和高度 | 可以设为 0,运行时再动态调整 |
代码里操作 Box 也很直接。比如我想在运行时改变背景颜色:
// 在 setupScreen() 或 handleTickEvent() 中
boxBackground.setColor(touchgfx::Color::getColorFrom24BitRGB(0x2C, 0x3E, 0x50));
boxBackground.setAlpha(200);
boxBackground.invalidate(); // 别忘了刷新
嗯,这里要注意 invalidate() 这个函数。我刚开始做的时候经常忘记调,结果改了颜色屏幕没反应,还以为是 Bug。其实 TouchGFX 的绘制是增量式的,你改了属性必须告诉框架「这块区域需要重绘」。
4.2 TextArea 文本显示——让屏幕「说话」
TextArea 就是用来显示文字的。别小看它,嵌入式设备上的文本处理比 PC 上麻烦得多。为什么?因为字体是预编译的,不是系统自带的。
TouchGFX 的 TextArea 有几个核心概念你必须搞懂:
- TypedText:文字和字体样式的绑定。你在 Designer 里定义的每个文本样式,都会生成一个 TypedText ID。
- Wildcard(通配符):用于显示动态内容,比如温度值、时间、计数。
- TextAreaWithOneWildcard:带一个动态占位符的文本区域,最常用。
我记得第一次做温控器界面时,需要实时显示当前温度。一开始我傻傻地每次重新创建 TextArea,后来才发现有 Wildcard 这种好东西。
用法很简单:
// 在头文件中声明
touchgfx::TextAreaWithOneWildcard textTemperature;
touchgfx::Unicode::UnicodeChar temperatureBuffer[10];
// 在实现文件中更新
Unicode::snprintf(temperatureBuffer, 10, "%d°C", sensorValue);
textTemperature.setWildcard(temperatureBuffer);
textTemperature.invalidate();
踩坑提醒:Wildcard 缓冲区的大小一定要预留足够。我曾经只给了 5 个字符的空间,结果温度显示到三位数时就溢出了,屏幕出现乱码。排查了半天才发现是缓冲区越界。建议至少留 10~20 个字符的空间。
关于字体,TouchGFX 使用预渲染位图字体。你在 Designer 里添加的每个字体文件(.ttf 或 .otf),都会被转换成二进制字体资源。这意味着:
- 字体大小是固定的,不能像 PC 那样随意缩放
- 中文字体文件很大,建议只包含用到的字符(子集化)
- 字体颜色在渲染时就定好了,运行时改颜色需要重新生成
我个人习惯在项目初期就把所有可能用到的中文字符列出来,生成一个精简字体。不然一个完整的宋体字库能吃掉几百 KB 的 Flash,对于资源紧张的 MCU 来说太奢侈了。
4.3 Button 按钮——用户交互的「入口」
按钮是用户和界面交互最直接的方式。TouchGFX 提供了好几种按钮类型,但最常用的是 Button 和 TextButton。
一个按钮的核心就三件事:
- 外观:按下前和按下后显示不同的图片或颜色
- 触发:点击、长按、释放等事件
- 回调:事件发生后执行什么代码
在 Designer 里添加一个 Button,你会看到它需要两张图片:一张是「弹起状态」(Released),一张是「按下状态」(Pressed)。这两张图通常只有颜色或阴影不同,用来给用户视觉反馈。
代码里绑定按钮事件是这样写的:
// 在 view 的构造函数或 setupScreen() 中
buttonStart.setAction(buttonClickedHandler);
// 定义回调函数
void MyView::buttonClickedHandler(const touchgfx::AbstractButton& src) {
if (&src == &buttonStart) {
// 启动设备
startDevice();
} else if (&src == &buttonStop) {
// 停止设备
stopDevice();
}
}
你可能会问:为什么用 AbstractButton 引用?因为 TouchGFX 的按钮回调机制是基于基类的,这样同一个回调函数可以处理多个按钮的事件。我在项目中就经常用一个回调函数管理十几个按钮,通过比较地址来区分是哪个按钮被按了。
避坑指南:按钮的图片资源不要太大。我曾经用了一张 200x100 的 PNG 做按钮图,结果在 STM32F429 上点击响应有明显的延迟。后来把图片压缩到 80x40,并且转成 RGB565 格式,响应就快多了。嵌入式设备上,图片大小直接影响交互体验。
另外,TouchGFX 还支持 RepeatButton(长按连续触发)和 ToggleButton(开关按钮)。前者适合音量加减这种需要连续调节的场景,后者适合开关切换。
4.4 Image 图像控件——视觉的「点睛之笔」
Image 控件用来显示位图。在 TouchGFX 里,所有图片资源都要先通过 Designer 导入,转换成内部格式(通常是 RGB565 或 ARGB8888)。
Image 的用法很直观:
// 在代码中动态设置图片
imageLogo.setBitmap(Bitmap(BITMAP_LOGO_ID));
imageLogo.setXY(10, 10);
imageLogo.setVisible(true);
imageLogo.invalidate();
但有几个关键点我要强调一下:
- 图片格式:RGB565 占内存小,但颜色精度低;ARGB8888 颜色丰富,但内存翻倍。根据你的屏幕色深来选择。
- 图片大小:尽量使用 2 的幂次尺寸(如 64x64、128x128),这样 DMA 传输效率更高。
- 透明通道:PNG 图片的透明区域在 TouchGFX 里是支持的,但需要启用 Alpha 通道。
注意:不要直接在 Image 上叠加太多透明层。我记得有一次为了做一个毛玻璃效果,叠了三层半透明图片,结果帧率直接掉到 15fps。对于没有 GPU 的 MCU,每多一层透明混合,CPU 的负担就重一分。
Image 还有一个很实用的功能——Scale 和 Rotation。不过这两个操作比较耗资源,我一般只在初始化时做一次,不会在每一帧都去缩放旋转。
4.5 四个组件的协同工作
实际项目中,这四个组件很少单独使用。我举个例子:一个典型的设置页面,可能是这样的结构:
- 最底层是一个 Box,铺满全屏,颜色深灰
- 中间层是一个 Image,显示设置页面的标题图标
- 图标旁边是一个 TextArea,显示「系统设置」字样
- 下方排列着几个 Button,分别是「网络设置」「显示设置」「关于」
你看,四个组件各司其职,组合起来就是一个完整的界面。我在做智能家居面板时,这种结构用了不下二十次。
最后说一句:别急着在代码里堆组件。先在 Designer 里把布局拖好,确认位置和大小没问题了,再写交互逻辑。这样效率最高,也最不容易出错。