4. 字体与文本优化:Unicode范围裁剪、自定义字体缓存、动态文本与静态文本取舍
字体和文本,往往是嵌入式UI里最容易被忽视的内存大户。我见过不少项目,UI做得很漂亮,一跑起来内存就爆了,查到最后发现是字体文件占了上百KB甚至几百KB。你想想看,一个中文字体动辄几MB,就算只提取常用字,也得几十KB起步。这一章我们就来聊聊怎么把字体这块的内存压到最低。
4.1 Unicode范围裁剪——别让字体文件“虚胖”
TouchGFX的字体生成工具TextConverter,默认会把字体文件里所有字符都打包进去。但你的产品真的需要显示所有汉字吗?
我做过一个智能家居面板,只需要显示“开关、温度、湿度、模式”这几个词。结果字体文件里包含了GB2312的几千个汉字。嗯,这就是典型的“虚胖”。
核心原则:只包含你真正用到的字符。
具体做法很简单:
- 在
.ttf或.otf字体文件上,用FontForge或High-Logic FontCreator打开。 - 删除所有不需要的字符(只保留你UI里出现的文字)。
- 导出为裁剪后的字体文件。
- 在TouchGFX Designer中重新生成字体。
举个例子,一个只显示数字和冒号的时钟界面:
// 你只需要这些字符
"0123456789:"
// 字体文件从几MB -> 不到1KB
小技巧:如果你不确定哪些字符会被用到,可以在TouchGFX Designer里先跑一遍UI,然后查看生成的GeneratedFont.cpp,里面会列出实际使用的字符范围。拿这个列表去裁剪,最精准。
4.2 自定义字体缓存——按需加载,用完就扔
有些场景下,你没法提前知道所有字符。比如用户输入、动态加载的菜单项。这时候如果还把所有字符都预加载到内存里,就太浪费了。
TouchGFX提供了自定义字体缓存机制。说白了就是:用到了才加载,用完了就释放。
我记得有个项目需要显示用户自定义的WiFi名称,里面可能有各种特殊字符。我总不能把所有Unicode字符都塞进去吧?
实现思路是这样的:
- 创建一个
FontCache对象。 - 设置缓存大小(比如4KB)。
- 当需要显示某个字符时,检查缓存里有没有。
- 没有就去Flash里读取,然后缓存起来。
- 缓存满了,就淘汰最久没用的字符。
代码骨架:
// 头文件
#include <touchgfx/FontCache.hpp>
// 创建缓存,大小4KB
FontCache cache(4096);
// 设置字体文件在Flash中的地址
cache.setFontFileAddress(0x08040000); // 假设字体存在这个地址
// 使用缓存渲染文本
TypedText typedText(T_MyDynamicText);
cache.setCacheFor(typedText.getFont());
注意:缓存大小不是越大越好。我建议根据你的UI复杂度来定。一般2KB~8KB就够了。太大反而会占用宝贵的RAM。
4.3 动态文本 vs 静态文本——选对了省一半内存
这是很多新手容易踩的坑。TouchGFX里文本有两种存在形式:
| 类型 | 存储位置 | 内存占用 | 修改方式 | 典型场景 |
|---|---|---|---|---|
| 静态文本 | Flash(只读) | 低(只存一次) | 不可修改 | 固定标签、标题、提示 |
| 动态文本 | RAM(可读写) | 高(每个实例独立拷贝) | 运行时修改 | 用户输入、实时数据、多语言切换 |
我见过一个项目,所有文本都用动态文本,包括那些永远不会变的“设置”、“返回”、“确认”。结果RAM被文本缓冲区占了一大半。其实这些完全可以用静态文本,零RAM开销。
我的建议:能用静态文本的,坚决不用动态文本。只有那些运行时必须修改的文本,才用动态文本。
具体取舍原则:
- 固定标题、菜单项、按钮文字 → 静态文本
- 数值显示(温度、时间、进度) → 动态文本(因为值会变)
- 多语言切换 → 动态文本(或者用静态文本+多语言表切换)
- 用户输入 → 动态文本(没得选)
4.4 实战:一个文本优化的前后对比
拿我之前做的一个智能门锁界面举例:
- 界面有10个固定标签(“请输入密码”、“验证中”、“开锁成功”等)
- 1个动态密码输入框(6位数字)
- 1个动态状态提示(“剩余尝试次数:3”)
优化前:
- 所有文本都用动态文本
- 每个动态文本分配64字节缓冲区
- 总共11个动态文本 → 704字节RAM
- 字体文件包含全部ASCII + 常用汉字 → 约120KB Flash
优化后:
- 10个固定标签改为静态文本 → 0字节RAM
- 密码输入框用动态文本,缓冲区缩小到8字节(只存6位数字+结束符)
- 状态提示用动态文本,缓冲区32字节
- 字体文件只裁剪出用到的字符(数字、冒号、汉字“请输入密码验证中开锁成功剩余尝试次数”)→ 约8KB Flash
结果:RAM从704字节降到40字节,Flash从120KB降到8KB。效果非常明显。
额外提醒:动态文本的缓冲区大小要精确计算。比如显示“剩余尝试次数:3”,最多也就20个字符。给32字节足够了,别给128字节。我曾经看到有人给每个动态文本都分配256字节,结果界面一多,RAM直接爆了。
4.5 知识体系总览
下面这张图总结了字体与文本优化的核心逻辑:
4.6 避坑指南
最后分享几个我踩过的坑:
- 别用全量中文字体:一个GB2312字体文件至少200KB+,你的Flash可能连UI都放不下。一定要裁剪。
- 动态文本缓冲区别贪大:精确计算最大字符数,多一个字节都是浪费。
- 字体缓存别设太小:2KB以下的话,频繁换页会导致缓存抖动,反而降低性能。
- 静态文本也能“动态”:如果你需要多语言切换,可以用多个静态文本+条件显示,比动态文本省内存。
我曾经在一个项目里,把所有文本都改成静态文本,然后通过切换TypedText的ID来实现多语言。结果RAM占用直接降了60%。虽然代码稍微复杂了一点,但效果非常值。