☰
LVGL界面设计太复杂?用网页版工具5分钟搞定嵌入式UI
2026/9/28 1:37:57 网站建设 项目流程

做嵌入式的人应该都体会过,功能逻辑写起来顺风顺水,一提到嵌入式UI设计,很多人就开始原地打转。LVGL在MCU上跑得轻快,性能也好,但新手想把手里的传感器数据变成屏幕上有模有样的仪表盘,却常常卡在第一步:不是代码不会写,而是界面“摆不出来”。移植源码、配字体、编模拟器、调坐标,一套组合拳下来,一天时间说没就没。我最近发现一个网页版的LVGL UI设计工具Anyui,打开浏览器就能拖控件、改样式、看预览、导代码,基本不需要安装任何本地软件,5分钟搭出一个不错的界面原型完全可行。这篇文章就把我从画布拖拽到STM32工程的完整过程梳理出来,适合刚入坑LVGL、想快速看见界面效果的朋友参考。

1. 为什么LVGL新手最容易被界面布局卡住

1.1 传统上手的典型路径,听着容易做起来累

LVGL(Light and Versatile Graphics Library)是目前嵌入式圈子里用得比较多的开源图形库,资源占用小,专门为MCU这类资源受限设备设计。官方文档写得很全,demo也不少,但新手照着文档走一遍,通常会经历这么几条路。

第一条路是源码直接搬进工程。去GitHub把代码下载下来,往自己的项目里塞,然后打开lv_conf.h开始配宏。这个头文件里全是开关和参数,比如LV_COLOR_DEPTH、LV_MEM_SIZE、LV_TICK_CUSTOM等等,每一项都影响最终能不能跑起来。新手不知道哪些要改、哪些保持默认,常常是配完一堆选项,一编译全是错误,连错误信息都看不懂。

第二条路是跑PC模拟器。官方提供了基于SDL的模拟器工程,理论上能双击运行一个桌面窗口,马上看到LVGL界面效果。但SDL在不同系统上安装依赖也是个体力活,Linux上要装一堆库,Windows上要处理mingw环境,有人光配环境就折腾一晚上。这个门槛对只是想“把界面画出来看看”的人来说,实在太重了。

第三条路是直接用开发板跑。如果手头有STM32或者其他板子,需要先搞定显示驱动、触摸驱动、DMA搬运,再初始化LVGL。驱动一旦有点小毛病,屏幕不是白屏就是花屏,而你是来学UI的,结果先被硬件折腾掉半条命。

我并不是说这些路径没用,它们都是嵌入式UI工程师的必修课。但如果你现在最大的诉求是:快速验证一个界面想法,把一个页面原型做出来给同事看,或者想通过“看到效果”来反向理解LVGL的API,那传统路径确实绕远路。

1.2 三个真实痛点:环境、反馈、素材

把这几年带新人的经验总结一下,LVGL新手在界面布局上被卡住,几乎都逃不开三个痛点。

第一个痛点是环境壁垒。不管是Keil工程还是模拟器,配置开发环境都是有学习成本的。很多人其实不是不会写代码,而是环境没跑通,热情先被浇灭了。网页版工具把这一层直接削掉,浏览器打开就能用,不挑操作系统,也不用装额外的运行时,这就是“免安装”这三个字最大的价值。

第二个痛点是反馈慢。用纯代码调界面,流程是:修改坐标和颜色,重新编译,烧录或者运行模拟器,看一眼效果,然后继续改。改一个按钮的位置,往往要循环好几轮,每一轮都以分钟起步。人的大脑对这种“改一下看一次”的模式非常不适应,因为你根本不知道改完之后到底靠不靠谱,只能一遍遍试。可视化编辑器不一样,拖完控件、改完属性,预览画面立刻刷新,反馈延迟几乎为零。

第三个痛点是素材管理麻烦。LVGL里显示中文需要字库,显示图片需要把图片转成C数组,图标也得用字体或者图像资源。新手第一次碰到“编译通过了,但中文全是一个个方块”的时候,大概率是崩溃的。网页版工具通常把这些素材处理环节封装得友好一些,至少不会让你一开始就去折腾字体转换和图片编码。

1.3 可视化编辑为什么能解决这几个问题

想明白可视化工具为什么有效,得先理解界面布局的本质。一个UI界面,不管多复杂,底层就是一棵对象树加一堆属性数据。屏幕是一个根对象,上面挂按钮、标签、滑块,每个控件的坐标、大小、颜色、字体、对齐方式,都是它的属性。代码只是这些属性的一种描述方式,并不是唯一的描述方式。

可视化设计器做的事情,其实就是把这个对象树和属性面板画在浏览器里:左侧是控件库,中间是画布,右侧是属性编辑区。你在画布上拖一个按钮,底层只是创建了一个按钮对象;你改它的背景颜色,底层只是更新了一条属性。导出代码的时候,设计器把这些数据和属性翻译成对应的LVGL API调用而已。

所以它不是魔法,也不是黑盒。恰恰因为它是从“数据”到“代码”的翻译,生成出来的代码结构往往比手写的更规整,反而适合新手拿来学习——你可以对着生成的代码反推LVGL的API到底是怎么用的。这也是我个人最喜欢的用法。

2. 用网页版设计器之前,先把LVGL对象模型记在心里

2.1 对象树:所有界面都是父子和兄弟关系

LVGL里面所有可见元素都是对象(lv_obj_t),而对象和对象之间是有层级关系的。每个界面都有一个根对象,就是屏幕(Screen)。屏幕下面可以挂容器(Container),容器下面再挂按钮、标签、滑块,这种父子关系组成了一棵完整的对象树。

为什么要先理解这个?因为可视化设计器里你看到的层级列表,画布上控件的包含关系,还有导出的代码结构,全部是由这棵对象树决定的。父对象移动,子对象跟着移动;父对象隐藏,子对象一起隐藏;父对象的裁剪设置,也会影响子对象是否超出边界可见。这就像文件夹系统,你把一个文件夹拖到另一个文件夹里面,里面的文件就跟着一起搬家了。

我在实际带项目的时候,经常看到新手把几十个控件全部平铺挂在屏幕根对象下面,结果想整体往右挪一点,就得一个一个改坐标。如果在网页版设计器里先建一个Container,把相关的控件全放进去,后面整体布局只需动容器一个对象就够了。这个习惯从一开始就要养成,等界面复杂了再改结构,成本要高得多。

2.2 常用控件和属性,新手先认识这几个

LVGL的控件类型非常丰富,但日常开发里高频用到的基本上就那几个:Label(文本标签)、Button(按钮)、Image(图片)、Bar(进度条)、Slider(滑块)、Arc(弧形仪表)、Switch(开关)、Checkbox(复选框)、Container(容器)。每一个控件在网页版设计器的控件库里都能直接拖出来。

理解了控件,再看属性。LVGL的样式系统里,最常用的属性无非这几类:坐标和尺寸(x、y、width、height)、对齐方式(align)、内边距和外边距(padding、margin)、背景色和背景透明度、边框宽度和颜色、圆角半径(radius)、阴影、字体和文字颜色。这些属性在可视化工具里全部对应着右侧面板上的输入框或者下拉选项。

我建议新手这样去记忆:控件就像积木,属性就是积木的形状和颜色。你要搭一个页面,第一件事是想清楚用哪几种积木,第二件事才是给每块积木调颜色、调大小。如果你连积木的种类都还没搞明白,就先别急着追求“让界面漂漂亮亮”,先把每个控件拖出来看一眼默认效果,有个直观印象。

2.3 网页设计器到底生成了什么

很多人会担心一个问题:网页版工具生成的代码,放到自己的工程里能用吗?答案是可以,但前提是你理解了它生成的是什么。

设计器内部保存的并不是最终的.c文件,而是一份描述UI的数据结构,可以理解成一张“配置表”。这张表里记录着:画布上有哪些控件、每个控件是什么类型、它的父对象是谁、它的属性值是什么。当你点击导出按钮时,工具才把这张表翻译成LVGL的API调用,生成你熟悉的源码。

举个例子,你在画布上放了一个按钮,设置了宽200、高80、背景色红色、居中对齐,导出代码大概率就是:

lv_obj_t *btn = lv_btn_create(parent); lv_obj_set_size(btn, 200, 80); lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0); lv_obj_set_style_bg_color(btn, lv_color_hex(0xFF0000), 0);

这几乎就是手写LVGL代码的标准写法。所以使用网页版工具并不会让你“脱离代码”,它只是把从无到有这部分脏活累活先干完了,让你把精力花在更重要的交互逻辑上。反过来说,如果你愿意认真学习生成的代码,这其实是一条非常高效的学习路径。

3. 实操:在Anyui网页版里5分钟做一个温控面板

3.1 动手前的准备:浏览器和需求清单

实操之前先确认两件事:一台能上网的电脑,一个支持现代JavaScript的浏览器。不需要装任何IDE,不需要下载LVGL源码,也不需要配编译器。就这么简单。

但我不建议你打开工具就瞎拖。先花30秒想清楚自己要做一个什么界面。为了演示,我用一个温控面板作为例子:屏幕尺寸480x320,界面包含一个顶部标题“温控面板”,中间一个大大的当前温度值,下面两个加减按钮,再往下是一个加热/待机模式的开关。这个例子足够覆盖标签、按钮、容器、开关这几类常用控件,而且真机应用场景也很典型。

想清楚需求再动手,效率是最高的。你别小看这30秒,很多人做UI失败,不是因为工具不好用,而是因为根本不知道自己想画什么,画了一半推翻重来,时间全耗在选择上了。

3.2 三步完成画布上的界面搭建

在Anyui网页版里搭建界面,核心就三个步骤:拖控件、改属性、调布局。

第一步是拖控件。打开一个新的页面,它通常默认有一个屏幕对象,也就是画布底稿。然后把控件库里的“容器”拖进画布,准备用它来承载页面主体内容;再把“标签”拖进容器,作为标题;再拖两个“按钮”,计划做一个+和一个-;再拖一个“开关”,用来切换模式。不用管最终效果,先把控件铺上去。

第二步是改属性。标题标签选中后,右侧属性面板会显示当前控件的所有属性。我会把文本内容改成“温控面板”,字体大小调到24,颜色调成白色,水平和垂直方向都居中对齐到屏幕顶部中间位置。中间的温度标签,我会把文本改成“26.5°C”,字体调到36,颜色用醒目的青色。两个按钮分别设置文本“+”和“-”,尺寸调成78x78,圆角半径调大一点,做成圆形按钮。开关就保持默认即可,属性面板里把状态设成“ON”。

第三步是布局。LVGL的坐标系统是左上角起点,x向右增加,y向下增加。新手最容易犯的错就是手动去填每个控件的x和y坐标,结果换一个屏幕分辨率就全乱了。网页版工具里要尽量用对齐方式和容器来实现布局:标题对齐到屏幕顶部中间,温度标签对齐到容器中心偏上,“+”按钮放在温度标签右侧,“-”按钮放在左侧,开关对齐到底部中间。这样后续如果整体要挪位置,只需要移动外层容器,内部控件的相对关系不变。

3.3 预览和针对嵌入式屏的微调

界面搭完之后,一定要在预览窗口里放大缩小看几遍,别急着导出。预览功能最大的价值,是让你在不用编译的情况下就能发现潜在的显示问题。

我常用的检查项有三个。第一,文字有没有被截断。LVGL的标签控件默认不会自动换行,如果文字太长,超出了显示区域,会被裁剪掉。第二,按钮的触控区域够不够大。如果这个界面最终要跑在带触摸屏的设备上,按钮尺寸建议不要小于40x40像素,不然手指点起来很费劲。第三,整体配色在低色深下会不会糊在一起。嵌入式屏幕常见的是RGB565,也就是16位色,相比网页上的真彩色,渐变和细微颜色差异会损失很多。预览时尽量别用太接近的颜色做前景和背景。

另外,预览窗口一般可以让选择目标分辨率,这个功能我强烈建议你养成习惯。比如我的目标是480x320的屏幕,我就把预览分辨率设置成480x320,然后看整个布局是不是合适。有几个控件超出了边界,有几处文字溢出,一眼就能看出来,比上机调试高效得多。

3.4 导出代码并放进自己的工程

界面满意之后,点击导出按钮,网页版会生成一份描述界面的源码。有的工具会给完整的.c文件,有的会把它拆成函数,这取决于工具的具体实现。通常生成后的结构大概长这样:

// 这段是示意代码,不同LVGL大版本的API写法有差异 void ui_Screen_init(void) { lv_obj_t *ui_Screen = lv_obj_create(NULL); lv_obj_clear_flag(ui_Screen, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_style_bg_color(ui_Screen, lv_color_hex(0x1E1E2E), 0); lv_obj_t *ui_Title = lv_label_create(ui_Screen); lv_label_set_text(ui_Title, "温控面板"); lv_obj_set_style_text_font(ui_Title, &lv_font_montserrat_24, 0); lv_obj_align(ui_Title, LV_ALIGN_TOP_MID, 0, 16); lv_obj_t *ui_Temp = lv_label_create(ui_Screen); lv_label_set_text(ui_Temp, "26.5°C"); lv_obj_set_style_text_font(ui_Temp, &lv_font_montserrat_36, 0); lv_obj_align(ui_Temp, LV_ALIGN_CENTER, 0, -20); lv_obj_t *ui_BtnUp = lv_btn_create(ui_Screen); lv_obj_set_size(ui_BtnUp, 78, 78); lv_obj_align(ui_BtnUp, LV_ALIGN_CENTER, 80, -20); lv_obj_t *ui_BtnDown = lv_btn_create(ui_Screen); lv_obj_set_size(ui_BtnDown, 78, 78); lv_obj_align(ui_BtnDown, LV_ALIGN_CENTER, -80, -20); lv_obj_t *ui_SwitchMode = lv_switch_create(ui_Screen); lv_obj_align(ui_SwitchMode, LV_ALIGN_BOTTOM_MID, 0, -24); lv_scr_load(ui_Screen); }

把这段代码放到工程里之后,注意两件事。第一,生成的代码可能依赖你当前选择的目标LVGL版本。比如LVGL 8.x里创建屏幕用的是lv_obj_create(NULL),到LVGL 9.x有些API就有变化,所以别直接无脑复制,编译报错时要会看提示。第二,这段代码只是负责“创建界面”,你不能把它丢在main函数里跑一次就完事。通常在初始化lv_init和显示驱动之后调用它,并且保证LVGL的定时器循环(lv_timer_handler)在后台持续运行,界面才会稳定显示。

4. 从网页设计到真机运行:绕不开的几个关键细节

4.1 版本匹配是头号问题

我在帮同事们排查LVGL相关问题的时候,遇到最多的就是版本不匹配。LVGL近几年的版本迭代很快,尤其是8.x和9.x之间,API变化非常大。很多教程和工具生成的代码是基于某个特定版本的,你拿LVGL 9.x的工程去编译LVGL 8.x风格的代码,报错能刷满整个屏幕。

常见的API差异包括:flag操作方式(lv_obj_clear_flag还是lv_obj_remove_flag)、样式设置函数(lv_obj_set_style_bg_color的参数个数)、事件回调注册方式、动画API、内置字体的命名方式等。

所以在用网页版工具之前,先确认你的目标工程用的LVGL大版本是什么。工具里如果提供版本选择,就选对应的版本;如果不提供,导出代码后也要把它作为参考,拿到工程里逐段核对API。我自己的经验是:先写一个空的lv_obj_create,编译通过,再逐步加入更多控件,这样定位错误会快很多。

注意:如果你在PC模拟器上跑通了,但烧到开发板上编译不过,优先检查LVGL版本配置和颜色深度宏定义,比如LV_COLOR_DEPTH是否为16(RGB565)。

4.2 中文字体和图标字体处理

网页版做出来的界面默认字体通常是LVGL内置的Montserrat英文字体,尺寸有12、14、16、20、24、28、32、36等几个档位。但它不包括中文字库。如果你在页面里放了中文“温控面板”,在真机上大概率显示成一个个方块。

中文显示的原理说起来不复杂:LVGL需要把汉字对应的字形数据以位图形式保存下来,使用的时候按字索引去取。问题是中文常用字就有两三千,如果全部转换成字体数组,会在MCU的Flash和内存里占掉相当可观的空间。所以实际项目里通常只转自己用到的那些汉字,几十个字就够,内存占用会小很多。

处理方式通常是:把需要显示的中文整理成一段文本,用字体转换工具(比如LVGL官方在线字体转换工具)选择字体、字号、颜色深度,把这段文本里的字转换成C数组,生成的.c文件放进工程,然后在代码里设置label的字体为这个中文字体。网页版设计器如果自带字体管理,可以在这里先把中文字体配好,导出时一并带上,省得后面再手动补。

注意:中文字体转换时,字号越大,每个字占用的存储空间越大。一个16x16像素的汉字在RGB565下大概是几百字节,32x32就会翻好几倍。能用小字号就不用大字号,能用图片就不用文字。

4.3 图片与图标资源的正确导入方式

界面设计里难免要放logo、状态图标、触摸按钮背景图。LVGL的图片也不是直接支持jpg和png的,它需要把图片转换成C数组格式,常见的有索引色和真彩色两种格式。网页版工具一般提供图片上传功能,上传后自动完成格式转换,并且可以调整输出颜色深度。

新手最容易踩的坑是图片颜色和透明度。比如你上传了一张带圆角的PNG图标,导出后真机背景变成黑块,多半是透明度信息没处理好。LVGL里支持带alpha通道的图片格式,但需要正确配置。另一个坑是图片尺寸太大,一块200x200的RGB565图片,光数据就差不多80KB,如果MCU的RAM不够大,稍不注意就溢出。

所以我的习惯是:图标和按钮图片尽量控制在100x100以内,尺寸大的背景图尽量用JPEG压缩或者改成纯绘制实现,不要硬塞大图片。设计器里看到图片效果很棒,不代表你的MCU能吃得消。真机上跑不动,再好看也没用。

4.4 配合STM32和FreeRTOS的集成套路

很多人用网页版工具导出UI代码之后,下一步就是把它集成到STM32工程里,配合FreeRTOS跑起来。这套流程说难也不难,但有几个固定的步骤要理清楚。

第一步是LVGL基础初始化,包括lv_init()、显示器驱动初始化,以及设置tick时钟源。第二步是注册显示器的flush回调函数,LVGL每次刷新画面的时候会调用这个回调,把显存里的像素数据推送到LCD控制器。第三步是调用用户界面的初始化函数,也就是把网页版生成的那段ui_Screen_init(或者其他名字)跑起来,加载屏幕对象。第四步,也是最容易被忽略的:LVGL需要一个“心跳”来驱动它的内部状态机,对应函数是lv_timer_handler(),它处理动画、输入、屏幕刷新调度。

在FreeRTOS模式下,我建议单独建一个任务来跑这个心跳,任务循环里每5ms调用一次lv_timer_handler(),让出CPU给其他任务。这样做的好处是LVGL的工作不会阻塞其他实时任务,也不会被其他任务抢掉刷新周期。

注意:LVGL本身不是线程安全的。如果你有多个任务都要操作界面控件,一定不要在多个任务里直接调用LVGL API,否则会出现莫名其妙的崩溃和花屏。做法是统一通过队列或者信号量把UI操作投递到同一个LVGL任务里执行,这样最稳。

5. 网页版工具边界、常见问题速查与工作流建议

5.1 什么事该交给网页版,什么事别硬交给它

老实说,网页版工具不是万能的。搞清楚它的边界,你才不会在某个场景里把它用出火气来。

适合用网页版工具的场景,我总结下来有三类。第一类是原型验证,产品经理改了需求,你需要在半小时内出一个新界面的示意,网页版工具的价值无与伦比。第二类是学习LVGL,你还不熟悉控件样式和API,但想看看不同参数组合出来的效果,可视化编辑器的即时反馈能帮你快速建立手感。第三类是常规业务界面,比如仪表盘、参数设置页、状态监控页,这些界面控件类型固定、交互不复杂,网页版工具生成的代码直接可用。

不适合的场景也有三类。第一类是复杂动画,比如多段式缓动动画、粒子效果、控件之间的联动动画,这些在可视化工具里很难精细调整,必须手动写动画API。第二类是高度自定义绘制,比如曲线图、波形图、自定义旋转仪表,这些通常要用LVGL的画布(Canvas)或绘图API手写。第三类是性能敏感场景,比如大列表滚动、实时刷新的数据图表,生成代码通常优先保证可读性,可能不是最高效的实现,需要人工优化。

所以我的建议是:网页版工具负责“从0到1”,把界面框架搭起来;手写代码负责“从1到100”,做交互优化、性能调整、动画细节。这样分工最合理,也最能发挥工具的价值。

5.2 常见问题速查表

我把平时用网页版工具生成UI、再集成到真机时遇到的典型问题整理了一张速查表,方便你按图索骥。

问题现象常见原因处理方式
英文正常,中文全是方块没有加载中文字库用字体转换工具生成中文子集字体,代码里设置label字体
烧录后屏幕白屏屏幕驱动未初始化完成就调用了LVGL检查屏幕初始化顺序,确保在lv_init之后注册驱动
颜色明显偏色屏幕颜色深度与LVGL配置不匹配检查LV_COLOR_DEPTH是否为16,屏幕RGB/GRB顺序是否配置正确
页面元素位置错乱不同分辨率下直接用了绝对坐标改用对齐方式和容器布局,让父子关系决定相对位置
Touch没反应输入驱动未注册或未配置坐标转换检查输入设备读取和LVGL输入注册回调的坐标范围
内存不足导致卡死图片和字库占用过多RAM减小图片尺寸、降低字体数量、增大LV_MEM_SIZE或改外部内存
导出的API编译报错生成代码对应版本与工程版本不一致先确定大版本,再逐条核对API差异
界面显示了但刷新闪烁flush回调没有做双缓存或撕裂控制检查显示驱动的buffer配置,改用双buffer模式
按钮点击无反应控件被其他对象遮挡在对象树里检查层级关系,把遮挡控件移开或调整层级

这张表里的每个问题,我在项目里都实打实遇到过。尤其是颜色偏色和中文方块这两个,频率最高,也最打击新手信心。如果你遇到类似问题,先对照表格排查,大部分都能解决。

5.3 把网页版放进团队的UI开发流程

最后聊一个偏团队协作的层面。UI开发不是一个人的事,一个产品界面在设计、开发、测试之间反复流转,如果每次都用全量代码交接,效率非常低。网页版工具让“界面设计文件”这个概念变得很轻,它本身就是一个链接,发给谁都能打开看效果,不用装环境、不用编译。

我现在的团队流程是这样:产品提出界面需求,我先用Anyui网页版快速拼一版高保真原型,拉上产品和硬件沟通一遍布局和交互;确认没问题之后,导出代码集成进嵌入式工程;后续样式微调在代码里改,但如果是大改版,再回到网页版里重新搭,拉一轮评审。这样既避开了“设计完再人工翻译成代码”的沟通损耗,也保住了“最终代码可控”的底线。

另一个建议是版本管理。网页版生成的代码要当成正式工程代码对待,提交进Git。如果你中间手动改过某些地方,下次从工具里重新导出时可能覆盖掉手动修改,所以我习惯在生成的代码里加一行注释:This file is generated by UI design tool, manual changes may be overwritten. 这样谁都能一眼看出哪些文件是工具生成的,哪些是手写维护的,避免团队协作时互相踩脚。

最后再分享一个小技巧:用完网页版工具之后,别急着关页面,把自己手动改过的属性在脑内过一遍,想想它对应代码里的哪一行。几次下来,LVGL的API会变得特别熟。因为这相当于有人带着你写了一遍代码,而且每一步都能看见结果。

我自己的体会是,这类工具最大的价值不是“替代手写代码”,而是把界面这层从前期的黑盒变成了白盒,让新手能够快速跨过环境门槛,把注意力放在真正重要的交互逻辑上。等你在工具辅助下积累了足够多的界面经验,再回过去手写LVGL代码,会顺手很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询