LVGL实战:从零构建自定义中文字体与图标库

📅 发布时间:2026/7/12 3:41:43 👁️ 浏览次数:
LVGL实战:从零构建自定义中文字体与图标库
1. 环境准备与核心概念扫盲大家好我是老张一个在嵌入式UI领域摸爬滚打了十来年的老码农。今天咱们不聊那些虚的就手把手地带你走一遍在LVGL里折腾自定义中文字体和图标库的全过程。这活儿听起来有点门槛尤其是对刚接触LVGL的朋友但只要你跟着我的步骤来我保证你能避开我当年踩过的所有坑顺顺利利地把自家产品的漂亮UI跑起来。咱们先搞清楚几个最核心的问题。LVGL是一个为嵌入式设备打造的、开源免费的图形库它轻量、高效特别适合跑在单片机这类资源紧张的芯片上。但它的“出厂设置”对中文和特殊图标支持有限这就需要我们“自定义”。所谓自定义字体就是把我们产品需要的特定风格的中文字体比如圆润的、纤细的转换成LVGL能识别和使用的格式。而图标字体就更巧妙了它把一堆图标做成一个字体文件每个图标对应一个字符编码这样我们就能像显示文字一样用lv_label_set_text(btn, LV_SYMBOL_OK)这样的方式来显示一个对勾图标管理起来极其方便。那么整个流程的骨架是什么样的呢简单来说就是四步找资源选字体、挑图标、做转换生成LVGL能用的字体文件、搞集成把生成的文件放进你的工程、做优化让它在单片机上跑得又快又稳。这篇文章我会把这四步掰开了、揉碎了用最“小白”能懂的语言和案例给你讲明白。你不需要是图形学专家只要会用C语言手头有个STM32或者ESP32这样的开发板就能跟着玩起来。在开始动手前你得准备好这几样东西首先是一个代码编辑器VSCode或者你习惯的任何IDE都行其次是LVGL的官方环境你可以从GitHub上拉取最新的源码然后是一些工具软件比如字体转换工具后面会详细介绍和图标管理网站最后当然是你那颗想要做出酷炫UI的、跃跃欲试的心。别担心工具安装咱们边做边讲。2. 实战第一步获取与生成字体文件字体是UI的衣裳选对字体产品的气质就成功了一半。对于中文产品我们通常需要一款包含常用汉字比如3500-7000个的字体文件。去哪里找呢我强烈推荐“阿里巴巴普惠体”或者“站酷系列字体”它们都是免费可商用的优秀字体风格现代字库齐全。咱们以“阿里巴巴普惠体”为例。2.1 获取并准备TTF字体文件首先去它的官方发布页面下载你需要的字重比如Regular常规体、Bold粗体的TTF文件。下载下来后别急着用咱们先做个“体检”。用系统自带的字体查看器或者在线工具打开这个TTF文件看看它包含了哪些字符。一个完整的中文字体可能包含数万个字符但我们产品界面可能只用得到一两千个常用字。如果直接把整个几十兆的字体文件塞进单片机Flash空间恐怕要报警了。所以我们需要进行“字体子集化”。说白了就是只提取我们UI文本里实际用到的汉字。这里我分享一个我用了很多年的本地工具fonttools。它是一个Python库功能非常强大。你可以在命令行用pip安装它pip install fonttools。假设我们有一个ui_text.txt文件里面列出了所有界面会用到的中文和标点符号。那么生成子集字体的命令是这样的pyftsubset ./AlibabaPuHuiTi-Regular.ttf --text-file./ui_text.txt --output-file./my_app_font.ttf --flavorwoff2简单解释一下--text-file指定了你的文字清单--output-file是输出的字体文件--flavorwoff2是输出格式但LVGL最终需要的是C数组所以这里我们先得到精简后的TTF。运行后你会发现my_app_font.ttf的大小可能从十几兆锐减到了几百K效果立竿见影。这就是为嵌入式开发做的第一步也是最重要的一步优化按需取用。2.2 使用LVGL字体转换工具拿到了精简的TTF文件接下来就要把它变成LVGL的“口粮”。LVGL官方提供了一个超好用的在线转换工具LVGL Font Converter。你可以在GitHub上搜索“lv_font_conv”找到它也有在线网页版。打开转换工具你需要配置几个关键参数TTF文件上传我们刚生成的my_app_font.ttf。输出字体名称起个名字比如my_font_20后面会用在代码里。大小像素设置字体高度比如20。这决定了字体显示有多大。BPP位深度这决定了字体渲染的精细度。1 BPP是纯黑白抗锯齿效果差但体积小4 BPP就有不错的灰度抗锯齿效果看着更舒服但体积会大一些。对于大多数彩色屏我建议选4。字符范围这是关键工具默认只包含ASCII字符。我们必须手动添加中文范围。最常用的就是0x4e00-0x9fffCJK统一表意文字这覆盖了绝大部分常用汉字。如果你确定用字更少可以精确指定比如0x4e00-0x4e10几个特定字。配置好后点击转换你会得到两个文件一个.c文件和一个.h文件。这两个文件就是LVGL可以直接使用的字体源文件了。把它们放到你工程目录下合适的位置比如lvgl/src/fonts/。打开.h文件你会看到类似LV_FONT_DECLARE(my_font_20)的声明这个宏就是我们在代码中引用的桥梁。3. 构建专属图标字体库图标能让界面瞬间变得生动和专业。LVGL自带了一套不错的图标字体但难免有不符合我们产品调性或者找不到特定图标的时候。这时构建自己的图标字体库就是最佳选择。3.1 从图标库挑选与导出国内最知名的免费图标库非阿里巴巴矢量图标库莫属。打开网站注册登录后你可以搜索任何你需要的图标比如“设置”、“用户”、“刷新”。找到心仪的图标后点击“购物车”图标把它加入暂存架。选完一批后点击右上角的购物车选择“添加至项目”可以新建一个以你产品命名的项目。进入项目后关键的一步来了在项目设置里一定要将图标的“字体格式”设置为“Unicode”。然后点击“下载至本地”你会得到一个压缩包。解压后里面最关键的文件就是那个.ttf字体文件比如iconfont.ttf。除此之外还有一个demo.html文件用浏览器打开它你能看到每个图标对应的Unicode码和字体类名这个信息待会儿会用到。3.2 生成LVGL可用的图标字体现在我们有了图标的TTF文件但LVGL不能直接用它。我们需要像处理中文字体一样用lv_font_conv工具把它转换一次。但图标字体转换有个特殊点我们不是按范围提取而是按符号Symbol来指定具体的图标。回到LVGL Font Converter工具。上传你的iconfont.ttf文件。在“字符范围”那里我们不用范围了而是点击“Add Symbol”按钮。符号值从哪里来就是刚才demo.html里看到的每个图标的Unicode码。比如一个“主页”图标的Unicode是e600那么符号值就填0xe600。你可以一口气添加几十个图标对应的符号值。其他参数设置类似设置好输出名如my_icons、大小比如24、BPP图标建议用4边缘更平滑。点击转换同样会得到一对.c和.h文件。现在你的专属图标字体也准备好了。3.3 在代码中优雅地使用图标字体文件生成后如何在代码中使用呢首先确保在包含LVGL头文件后声明你的图标字体LV_FONT_DECLARE(my_icons)。然后你需要一个映射表把好记的标识符和图标的Unicode字符对应起来。因为从demo.html看到的是类似#xe600;的HTML实体我们需要将其转换为C语言字符串。这里有个小技巧你可以利用一些在线Unicode转UTF-8工具或者写个简单的小程序。但更简单的方法是直接使用LVGL提供的宏。在lvgl/src/font/lv_symbol_def.h旁你可以自己创建一个my_symbol_def.h文件// my_symbol_def.h #ifndef MY_SYMBOL_DEF_H #define MY_SYMBOL_DEF_H #define MY_SYMBOL_HOME \xEE\x98\x80 // Unicode UE600 的 UTF-8 编码 #define MY_SYMBOL_SETTINGS \xEE\x98\x81 // 假设 UE601 // ... 更多图标 #endif如何得到\xEE\x98\x80这个奇怪的字符串这就是UE600的UTF-8编码序列。你可以用我上面提到的在线转换工具输入e600选择UTF-8编码就能得到十六进制序列EE 98 80然后在C语言中写成\xEE\x98\x80的形式。使用时创建一个标签并设置字体和文本即可lv_obj_t * icon_label lv_label_create(lv_scr_act()); lv_obj_set_style_text_font(icon_label, my_icons, 0); lv_label_set_text(icon_label, MY_SYMBOL_HOME);这样一个漂亮的“主页”图标就显示出来了。通过这种方式你的代码可读性极高一看MY_SYMBOL_HOME就知道是什么图标。4. 在LVGL工程中的集成与调试字体和图标文件都生成好了现在要把它们“塞进”你的LVGL工程里并让它们正确跑起来。4.1 文件集成与工程配置首先把前面生成的四个文件中文字体的.c/.h图标字体的.c/.h拷贝到你的项目源码目录中我习惯放在lvgl/src/fonts/下和LVGL自带的字体放在一起。然后打开你的主程序文件通常是main.c或者ui.c在初始化LVGL之后、创建UI之前包含字体头文件并注册字体。虽然通过LV_FONT_DECLARE声明后在设置样式时直接引用my_font_20即可但为了确保字体数据被链接进最终的可执行文件最好在某个C文件中比如字体对应的.c文件被编译器“看到”。简单来说只要你调用了设置该字体的函数编译器就不会优化掉它。接下来为你需要显示中文的控件设置字体样式。例如创建一个样式并应用于标签static lv_style_t style_chinese_label; lv_style_init(style_chinese_label); lv_style_set_text_font(style_chinese_label, my_font_20); // 使用我们的中文字体 lv_obj_t * label lv_label_create(lv_scr_act()); lv_obj_add_style(label, style_chinese_label, 0); lv_label_set_text(label, 你好LVGL);如果一切顺利编译下载后你应该能在屏幕上看到正确的中文显示而不是乱码或者方框。4.2 常见问题与调试技巧集成过程很少一帆风顺下面是我总结的几个最常见的坑和解决办法显示乱码方框这是最常见的问题。99%的原因是你的文本字符串的编码格式与字体文件不匹配。请务必确保你的源代码文件.c/.h保存为UTF-8 without BOM编码。在VSCode或Notepad里可以轻松设置和查看。你代码中写的字符串字面量如你好就是UTF-8编码。编译器会按照源文件的编码来处理它。在LVGL的配置文件lv_conf.h中确保LV_TXT_ENC被设置为LV_TXT_ENC_UTF8。图标显示为乱字符检查你在代码中使用的图标字符串是否正确。确保你从my_symbol_def.h中引用的宏其内容那串\x代码与图标字体文件中该图标的Unicode码完全对应。一个字符错了显示就全乱了。可以先用一个最简单的图标测试。编译后程序体积暴增如果字体文件过大超出了单片机的Flash容量。回顾我们2.1节做的字体子集化检查是否包含了太多用不到的字符。对于图标字体只添加真正用到的图标。另外在lv_conf.h中可以尝试禁用一些你不需要的LVGL特性如动画、文件系统等来节省空间。渲染速度慢如果界面刷新明显卡顿除了检查硬件性能如SPI速率可以尝试降低字体的BPP从4降到1或者使用更小的字号。复杂的抗锯齿字体渲染会更耗时。调试时一个非常实用的方法是先用PC模拟器跑通。LVGL官方提供了多种IDE的模拟器工程如CodeBlocks、VS等。在电脑上调试编码、显示问题远比在开发板上烧录、调试方便百倍。确保在模拟器上完美显示后再移植到嵌入式平台这样可以排除大部分软件问题。5. 单片机环境下的深度优化策略当UI在PC模拟器上运行流畅后挑战才真正开始如何让它在资源拮据的单片机上也能跑得欢这里分享几个我压箱底的优化策略。5.1 字体存储策略外部Flash与文件系统对于包含大量中文的字体文件即使子集化后也有几百KB直接链接到代码中即存放在单片机的内部Flash可能不现实会迅速耗尽空间。这时我们必须将字体文件作为“资源”存放在外部存储器中比如SPI Flash或SD卡。有两种主流的加载方式通过文件系统实时读取如果你的项目搭载了文件系统如FATFS、LittleFS可以将.ttf或转换后的.bin格式字体文件存放在文件系统里。LVGL支持通过lv_font_load接口从文件加载字体。但这种方式在首次显示时需要解析字体文件会有一定的延迟。lv_font_t * p_font lv_font_load(S:/fonts/my_font.bin); // 从SD卡加载 if(p_font) { lv_style_set_text_font(style, p_font); // ... 使用字体 // 注意在不再需要时调用 lv_font_free(p_font) 释放内存 }预加载到内存RAM/PSRAM对于需要极致流畅性的场景可以在启动时将整个字体文件从外部Flash读取到一片连续的内存如果单片机有外部PSRAM就太好了。然后使用LVGL提供的工具将字体转换为指向这片内存区域的C描述结构体。这种方式避免了实时读文件的开销但需要占用宝贵的RAM。5.2 运行时内存优化缓存与分级加载字体渲染尤其是抗锯齿字体需要消耗CPU进行光栅化。我们可以通过缓存已渲染的字符位图来大幅提升性能。LVGL内置了字体字形缓存机制。在lv_conf.h中你可以设置LV_FONT_FMT_TXT_LARGE对应的缓存大小#define LV_FONT_CACHE_DEF_SIZE 256 // 缓存的字形数量这个值设得越大缓存命中率越高重绘速度越快但消耗的RAM也越多。你需要根据可用RAM和字体复杂程度做一个权衡。另一个高级策略是字体分级加载。想象一下你的产品界面主界面大标题用36号粗体设置菜单小字用16号常规体。你不需要在启动时就把所有字号、所有字重的字体都加载进来。可以设计成系统启动时只加载最基本的16号常规字体当用户进入某个需要大标题的界面时再动态加载36号粗体字体。这需要你设计一个良好的字体管理模块但能极大优化启动时间和内存占用。5.3 图标管理的最佳实践对于图标字体除了上述存储优化还有一些特定技巧图标合并如果你的产品有多个图标集比如来自不同设计师尽量将它们合并到一个TTF文件中再进行转换。这样可以减少字体文件的数量方便管理也避免了频繁切换字体带来的开销。按屏分组加载和字体分级加载类似将不同界面Screen用到的图标分组。只有当切换到某个界面时才加载该界面所需的图标字体子集。这需要更精细的工程规划但对于复杂应用非常有效。使用符号ID而非字符串在代码中尽量避免直接使用MY_SYMBOL_HOME这样的字符串宏去设置标签文本。因为每次设置LVGL都需要去解析这个UTF-8字符串。更好的做法是直接使用图标的Unicode码点作为整数通过lv_label_set_text_static或者直接操作标签的内部数据来设置效率更高。最后所有的优化都离不开** profiling**性能剖析。利用单片机上的定时器或者调试器测量一下打开一个新界面、滚动列表时字体渲染的实际耗时。数据会告诉你瓶颈在哪里是该增大缓存还是该优化字体文件本身。记住没有银弹最好的优化策略永远是针对你具体产品和硬件平台的。多测试多权衡你就能找到那个性能与资源消耗的完美平衡点。