LVGL界面设计太复杂?用网页版工具5分钟搞定嵌入式UI
做嵌入式的人应该都体会过功能逻辑写起来顺风顺水一提到嵌入式UI设计很多人就开始原地打转。LVGL在MCU上跑得轻快性能也好但新手想把手里的传感器数据变成屏幕上有模有样的仪表盘却常常卡在第一步不是代码不会写而是界面“摆不出来”。移植源码、配字体、编模拟器、调坐标一套组合拳下来一天时间说没就没。我最近发现一个网页版的LVGL UI设计工具Anyui打开浏览器就能拖控件、改样式、看预览、导代码基本不需要安装任何本地软件5分钟搭出一个不错的界面原型完全可行。这篇文章就把我从画布拖拽到STM32工程的完整过程梳理出来适合刚入坑LVGL、想快速看见界面效果的朋友参考。1. 为什么LVGL新手最容易被界面布局卡住1.1 传统上手的典型路径听着容易做起来累LVGLLight 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是否为16RGB565。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代码会顺手很多。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →