目录
- XML 基础概念
- LVGL Pro XML 的创新点
- 解决的核心问题
- XML 工作原理
- XML 的能力与限制
- 滑动页面的实现
- 实际应用示例
XML 基础概念
什么是 XML
XML(eXtensible Markup Language,可扩展标记语言) 是一种通用的文本标记语言,用于结构化地存储和传输数据。
XML 与 HTML 的区别
- HTML 关注数据的"显示"------标签如
<div>、<p>预定义了浏览器如何渲染内容 - XML 关注数据的"结构和语义"------你可以自定义任意标签名,如
<user>、<product>,用来表达数据含义,而不关心如何显示
XML 的核心特性
- 标签可自定义 :
<lv_btn>、<lv_label>、<component>都是由使用者定义的 - 树形结构:通过嵌套标签表达层级关系
- 属性和文本内容 :每个标签可以有属性(如
width="100")和子元素或文本 - 纯文本格式:易于版本控制、解析和人类阅读
基础 XML 示例
<?xml version="1.0" encoding="UTF-8"?>
<lv_screen>
<lv_container layout="flex" width="100%" height="100%">
<lv_btn width="100" height="50" bg_color="#0066FF">
<lv_label text="Click Me" text_color="#FFFFFF"/>
</lv_btn>
<lv_label text="Hello LVGL Pro"/>
</lv_container>
</lv_screen>
LVGL Pro XML 的创新点
创新点 1:文本化 UI 定义 --- 彻底解决版本管理与自动化问题
传统 LVGL 开发完全依赖 C 代码编写界面,导致 UI 逻辑与业务代码混淆,难以进行 Git 版本控制、代码审查和自动化测试。
LVGL Pro 将整个 UI 定义为 XML 文本文件,使得:
- 版本控制友好:所有 UI 变更都可以用文本 diff 展现,完全兼容 Git 工作流
- 自动化测试:UI 可以纳入 CI/CD 自动化验证和测试流程
- AI 辅助开发:因为 XML 是结构化的人类和机器都能理解的格式
传统方式与新方式对比:
/* 传统 LVGL C 代码 */
lv_obj_t * btn = lv_btn_create(parent);
lv_btn_set_size(btn, 100, 50);
lv_obj_set_style_bg_color(btn, lv_color_hex(0x0066FF), 0);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Click Me");
<!-- LVGL Pro XML 方式 -->
<lv_btn width="100" height="50" bg_color="#0066FF" on_click="btn_event_cb">
<lv_label text="Click Me"/>
</lv_btn>
创新点 2:双重交付模式 --- 满足不同部署需求
LVGL Pro XML 支持两种导出方式:
方案 A:静态导出(编译时)
- 将 XML 编译成标准 LVGL C 代码,内置于固件
- 无需运行时解析器,性能最优
- 适合对性能要求极高、不需要动态更新的场景
方案 B:动态加载(运行时)
- 在目标设备上直接解析 XML
- 可在不刷固件的情况下更新 UI
- 支持 OTA(Over-The-Air)灰度部署
- 适合需要快速迭代、频繁更新的应用
这解决了嵌入式开发中常见的"部署灵活性与性能"之间的矛盾。
创新点 3:模块化组件系统 --- 复用与协作
XML 支持自定义组件(component),可以参数化使用:
<!-- 定义可复用组件 -->
<component name="ButtonGroup">
<parameter name="label" type="string" default="Submit"/>
<parameter name="width" type="number" default="100"/>
<lv_btn width="${width}">
<lv_label text="${label}"/>
</lv_btn>
</component>
<!-- 使用组件 -->
<ButtonGroup label="Click Me" width="120"/>
<ButtonGroup label="Cancel" width="100"/>
优势:
- 实现真正的按需复用,而不是传统的复制粘贴代码
- 支持多人协作------不同开发者可以维护不同的组件
- 一次修改,处处生效------修改组件定义后,所有使用该组件的地方自动更新
创新点 4:所见即所得 (WYSIWYG) 的像素级预览
LVGL Pro 提供:
- 实时预览:修改 XML 后立即在编辑器中看到效果,无需编译和刷机
- 可视化检查器:直接测量间距、调整大小和位置,获得"设计师级"的精准度
- Figma 集成:设计稿可自动导入并转换为 XML 和样式
这把嵌入式 UI 开发的反馈周期从"分钟级"(编译+烧录)降到秒级。
解决的核心问题
| 传统 LVGL 开发的问题 | LVGL Pro XML 的解决方案 |
|---|---|
| UI 与业务代码混淆,难以管理 | XML 将 UI 完全分离为声明式配置,便于团队分工 |
| 每次 UI 改动都要重新编译和烧录(10 分钟+) | 运行时加载 XML,秒级更新,支持 OTA 灰度部署 |
| 无法进行有效的版本控制和代码审查 | 文本化 XML 天生兼容 Git,所有变更可 diff |
| 设计与开发脱节,像素偏差频发 | Figma 集成,自动同步样式、颜色、间距,消除手工错误 |
| UI 测试依赖手工,难以自动化 | 内置 UI 测试框架,可编写自动化测试脚本 |
| 多语言支持需要大量胶水代码 | 内置翻译系统,XML 中直接通过 key 绑定多语言文本 |
| 动画制作复杂,需要手写时间线逻辑 | 可视化时间线编辑器,直接在 XML 中描述动画流程 |
| 团队协作困难,无法远程预览 UI | 在线浏览器预览和协作,GitHub 集成,无需本地环境 |
XML 工作原理
整体流程概念
LVGL Pro 的 XML 工作流程大致是:
- 设计与编辑:在 XML 文件中描述组件、控件、界面以及它们的属性、样式和层级关系
- 实时预览:LVGL Pro 工具在 PC 上解析 XML,做实时预览、校验和辅助编辑
- 导出与部署:通过 CLI 或编辑器导出标准 LVGL C 代码,或者把经过打包的 XML 文件放到目标设备上
- 运行时加载:由运行库在运行时解析 XML,生成界面
XML 描述的核心内容
-
结构与层级
<lv_screen>
<lv_container>
<lv_btn/>
<lv_label/>
</lv_container>
</lv_screen>
每个元素都有父子关系,XML 的嵌套结构完美表达了 LVGL 对象树。
-
属性与样式
<lv_btn
width="100"
height="50"
x="10"
y="20"
bg_color="#0066FF"
border_radius="5"
opacity="255">
<lv_label text="Submit" text_color="#FFFFFF" font_size="14"/>
</lv_btn>
所有属性最终映射到 LVGL 的 style 和 layout API。
-
交互与逻辑连接
<lv_slider
min="0"
max="100"
value="50"
on_value_changed="handleSliderChange"
on_released="handleSliderReleased"/><lv_input
placeholder="Enter name"
on_ready="handleInputReady"/>
事件回调名在 XML 中声明,实际逻辑在 C 代码中实现。
解析与运行时机制
PC 编辑器中的解析
- 语法检查:验证 XML 结构是否合法
- 语义检查:验证属性类型是否正确,引用的样式、动画、翻译 key 是否存在
- 实例化:根据 XML 树生成 LVGL 对象并渲染预览
运行时加载流程
XML 文件
↓
[XML 解析器]
↓
内存中的 UI 描述树
↓
[LVGL 对象创建] → lv_obj_create / lv_btn_create / ...
↓
[属性应用] → lv_obj_set_* / lv_style_set_*
↓
[样式绑定] → 应用 CSS 样式规则
↓
[事件绑定] → 注册回调函数
↓
界面渲染完成
导出 C 代码的原理
导出 C 代码时,本质上是把"XML 描述树"反向映射成标准 LVGL C API 调用序列。
结构映射
XML 的层级被展开为一系列 lv_obj_create、lv_btn_create 等创建语句,按父子顺序写入生成的 C 文件:
<!-- XML 输入 -->
<lv_screen id="main_screen">
<lv_container id="main_cont">
<lv_btn id="submit_btn"/>
</lv_container>
</lv_screen>
/* 导出的 C 代码 */
lv_obj_t * main_screen = lv_obj_create(NULL);
lv_obj_t * main_cont = lv_obj_create(main_screen);
lv_obj_t * submit_btn = lv_btn_create(main_cont);
属性与样式映射
XML 中的每一个属性会转换为相应的 lv_obj_set_*、lv_style_set_* 等调用:
<!-- XML 输入 -->
<lv_btn width="100" height="50" bg_color="#0066FF" text="Click Me"/>
/* 导出的 C 代码 */
lv_obj_set_size(submit_btn, 100, 50);
lv_obj_set_style_bg_color(submit_btn, lv_color_hex(0x0066FF), 0);
lv_label_set_text(submit_btn, "Click Me");
公共样式、组件会转换成可复用的函数或结构体,方便在工程中集中维护。
组件化、数据绑定和扩展特性
-
组件化与复用
<component name="FormField"> <parameter name="label" type="string"/> <lv_label text="${label}"/> <lv_input placeholder="Enter value"/> </component> <FormField label="Username"/> <FormField label="Password"/> -
数据绑定
<lv_label text="{userData.name}"/>
运行时,数据绑定层在业务逻辑(C/C++)与 UI(XML)之间充当中介: - 当
userData.name变化时,自动更新标签文本(单向绑定) - 当用户操作滑块时,自动调用
handleBrightnessChange回调(双向绑定)
这完全消除了手写事件处理和数据同步代码。
-
多语言支持
<lv_label text_key="greeting_message"/>
<lv_btn>
<lv_label text_key="button_submit"/>
</lv_btn>
翻译表(JSON 或其他格式):
{ "zh_CN": { "greeting_message": "欢迎使用", "button_submit": "提交" }, "en_US": { "greeting_message": "Welcome", "button_submit": "Submit" } }-
动画与时间线
<lv_animation id="fade_in" target="label1" duration="500">
<keyframe time="0" opacity="0"/>
<keyframe time="500" opacity="255"/>
</lv_animation><lv_label id="label1" animation="fade_in"/>
XML 的能力与限制
XML 能做的事
XML 可以完美描述基于组件的图形界面(GUI),因为 GUI 本质上是组件树:
<!-- 一个屏幕包含多个控件 --> <lv_screen> <lv_container layout="flex"> <lv_btn width="100" height="50"> <lv_label text="Submit"/> </lv_btn> <lv_input placeholder="Enter name"/> </lv_container> </lv_screen>这种"容器-控件"的递归结构完全符合 XML 的树形模型。
XML 的根本限制
限制 1:不能描述任意像素级图形
如果你要画一个自由形状的图像(如笑脸、曲线、复杂插画),XML 无法直接表示。
这是为什么有 SVG(可缩放矢量图形) 这样的专门格式。但 LVGL Pro 没有用 SVG,因为 LVGL 本身的目标就是基于组件的嵌入式 UI(按钮、滑块、列表等),而不是任意图形绘制。
<!-- SVG 可以描述矢量图,但 LVGL Pro XML 不支持 --> <svg width="100" height="100"> <circle cx="50" cy="50" r="40" fill="yellow"/> <circle cx="35" cy="35" r="5" fill="black"/> <circle cx="65" cy="35" r="5" fill="black"/> <path d="M 30 60 Q 50 75 70 60" stroke="black" fill="none"/> </svg>限制 2:栅格图像需要外部文件
位图(PNG、JPEG)的像素数据太大,不能直接写在 XML 里。XML 只能引用外部资源:
<lv_image src="path/to/image.png" width="200" height="150"/>限制 3:实时绘制逻辑需要代码
如果图形需要动态计算或实时更新(如波形图、曲线图),XML 本身无法描述绘制算法。
LVGL Pro 的解决方案是:
-
在 XML 中声明要使用哪个自定义控件
-
该控件的绘制逻辑在 C 代码中实现
-
XML 只负责参数化这个控件
<lv_chart data_source="sensor.temperature" update_interval="1000"/>
/* C 代码实现绘制逻辑 /
void lv_chart_draw(lv_obj_t * obj) {
/ 读取 data_source,绘制曲线 */
}
XML 在 LVGL Pro 中的实际角色
LVGL Pro XML 不是通用的图形描述语言 ,而是专门为组件式 UI 设计的声明语言。
可以用 XML 描述 不能用 XML 描述 解决方案 按钮、输入框、滑块等标准控件的布局、样式、事件 自由绘制的曲线、复杂图形 绑定 C 实现的自定义控件 屏幕间的导航、多状态切换 实时计算的动态图表 XML 声明参数,C 实现绘制逻辑 多语言文本、动画时间线 像素级图像处理 外部资源文件(PNG/JPEG)引用 组件参数、数据绑定 3D 渲染、特效 使用专门库(OpenGL、Vulkan)+ XML 封装 对比其他 UI 描述语言
语言 目标领域 特点 适用场景 HTML/CSS 网页 描述结构和样式,依赖浏览器渲染引擎 Web 应用、跨平台 UI SVG 矢量图形 支持路径、形状、渲染 2D 矢量图、图表 QML(Qt) 桌面/移动 UI 更强大的动画和绑定能力 桌面应用、移动应用 Flutter Dart 移动应用 编程语言级的声明式 UI,支持任意绘制 高性能移动应用 LVGL Pro XML 嵌入式设备 UI XML 格式轻量,解析快,支持动态加载和 OTA 更新 轻量级嵌入式设备、IoT
滑动页面的实现
滑动页面是指用户可以上下或左右滑动来切换不同内容的界面。LVGL Pro XML 通过以下几种方式实现。
方式 1:使用 Tileview(瓷砖视图)--- 最常用
Tileview 是 LVGL 原生支持的多页面滑动容器,可以按网格排列多个页面。
<lv_tileview row="0" col="0" dir="lv_dir_top" anim_time="300"> <!-- 第一页 --> <lv_tile row="0" col="0"> <lv_label text="Page 1"/> <lv_btn> <lv_label text="Button on Page 1"/> </lv_btn> </lv_tile> <!-- 第二页 --> <lv_tile row="0" col="1"> <lv_label text="Page 2"/> <lv_image src="image2.png"/> </lv_tile> <!-- 第三页 --> <lv_tile row="0" col="2"> <lv_label text="Page 3"/> <lv_chart/> </lv_tile> </lv_tileview>关键属性
row、col:起始位置dir="lv_dir_top":向上滑动(上下翻页);lv_dir_left:向左滑动(左右翻页);lv_dir_all:四向滑动anim_time="300":滑动动画时间(毫秒)
导出的 C 代码示意
lv_obj_t * tileview = lv_tileview_create(parent); lv_tileview_set_tile(tileview, 0, 0, LV_ANIM_ON); lv_obj_t * tile1 = lv_tileview_add_tile(tileview, 0, 0, LV_DIR_TOP); lv_label_create(tile1); /* Page 1 content */ lv_obj_t * tile2 = lv_tileview_add_tile(tileview, 0, 1, LV_DIR_TOP); lv_image_create(tile2); /* Page 2 content */方式 2:使用 Tabview(标签页视图)--- 适合水平翻页
如果希望页面切换时显示标签栏指示器,可用 Tabview:
<lv_tabview> <lv_tab name="Home"> <lv_label text="Home Page"/> <lv_btn> <lv_label text="Go Home"/> </lv_btn> </lv_tab> <lv_tab name="Settings"> <lv_label text="Settings Page"/> <lv_dropdown options="Language,Theme,Brightness"/> </lv_tab> <lv_tab name="About"> <lv_label text="About Page"/> <lv_text text="App Version 1.0"/> </lv_tab> </lv_tabview>优点
- 自动生成标签栏,显示当前页面指示
- 用户可点击标签栏快速切换页面,也可滑动切换
- 适合页面数较少(3-5 个)且需要清晰导航的场景
C 代码示意
lv_obj_t * tabview = lv_tabview_create(parent); lv_obj_t * tab1 = lv_tabview_add_tab(tabview, "Home"); /* Add content to tab1 */ lv_obj_t * tab2 = lv_tabview_add_tab(tabview, "Settings"); /* Add content to tab2 */方式 3:使用 Roller(滚轮)--- 适合选择器样式
用于竖向滚动选择,常见于时间选择器、数字选择器:
<lv_roller options="January\nFebruary\nMarch\nApril\nMay" visible_row_count="3" on_value_changed="handleMonthChange"/>特点
- 循环滚动选择
- 自动"对齐"到中心最佳项
- 常用于日期、时间、数值选择
方式 4:使用自定义容器 + 滑动处理 --- 完全自由
如果以上组件都不满足需求,可以用通用容器 + 滑动事件绑定:
<lv_obj width="100%" height="100%" on_scroll="handlePageScroll" on_gesture="handleSwipe"> <!-- 虚拟或动态内容,通过 C 代码管理 --> <lv_container id="page_container"/> </lv_obj>在 C 代码中,监听滑动事件并动态管理页面加载和卸载:
void handlePageScroll(lv_event_t * e) { lv_obj_t * obj = lv_event_get_target(e); int scroll_y = lv_obj_get_scroll_y(obj); /* 计算当前页面索引 */ int page = scroll_y / SCREEN_HEIGHT; /* 动态加载该页面的内容 */ load_page(page); }方式 5:使用列表 (lv_list) --- 适合无限滚动
用于显示可无限滚动的列表项:
<lv_list width="100%" height="100%"> <lv_list_button label="Item 1" image="icon1.png"/> <lv_list_button label="Item 2" image="icon2.png"/> <lv_list_button label="Item 3" image="icon3.png"/> <!-- 可继续添加更多项 --> </lv_list>特点
- 内置滚动条,支持虚拟化(只渲染可见项)
- 内存高效,适合长列表
- 每项可点击,触发事件
滑动页面对比总结
组件 用途 适合场景 滑动方向 Tileview 多页面容器 多屏幕应用、仪表盘 上下、左右、四向 Tabview 标签页视图 有导航栏的页面切换 水平 Roller 滚轮选择器 数值/日期选择 竖向循环 List 列表视图 长列表、动态数据 竖向无限滚动 自定义容器 完全自定义 特殊交互逻辑 任意方向 推荐实践
- 大多数滑动页面用 Tileview(轻量、高效)
- 需要清晰导航的用 Tabview
- 列表式内容用 List
- 特殊需求才用自定义
实际应用示例
示例 1:手机主屏幕样式
<lv_tileview dir="lv_dir_all" anim_time="350"> <!-- 主页 --> <lv_tile row="0" col="0"> <lv_container layout="grid" grid_columns="2"> <lv_btn width="50%" height="25%"> <lv_label text="App 1"/> </lv_btn> <lv_btn width="50%" height="25%"> <lv_label text="App 2"/> </lv_btn> <!-- 更多应用图标 --> </lv_container> </lv_tile> <!-- 右页 --> <lv_tile row="0" col="1"> <lv_label text="Widgets Page"/> <lv_list> <lv_list_button label="Weather"/> <lv_list_button label="News"/> </lv_list> </lv_tile> </lv_tileview>示例 2:音乐播放器应用
<lv_screen> <!-- 标签页导航 --> <lv_tabview> <!-- 播放页 --> <lv_tab name="Now Playing"> <lv_container layout="flex" flex_direction="column" padding="20"> <!-- 专辑封面 --> <lv_image src="album_cover.png" width="200" height="200" align="center"/> <!-- 歌曲信息 --> <lv_label text="${currentTrack.title}" text_align="center" font_size="18" margin_top="20"/> <lv_label text="${currentTrack.artist}" text_align="center" font_size="14" opacity="200"/> <!-- 进度条 --> <lv_slider value="${playback.progress}" min="0" max="100" margin_top="20" on_value_changed="handleSeek"/> <!-- 播放控制 --> <lv_container layout="flex" justify_content="center" margin_top="20"> <lv_btn id="prev_btn" width="50" height="50"> <lv_label text="⏮"/> </lv_btn> <lv_btn id="play_btn" width="60" height="60" margin_left="20" margin_right="20"> <lv_label text="${playback.isPlaying ? '⏸' : '▶'}"/> </lv_btn> <lv_btn id="next_btn" width="50" height="50"> <lv_label text="⏭"/> </lv_btn> </lv_container> </lv_container> </lv_tab> <!-- 播放列表页 --> <lv_tab name="Playlist"> <lv_list width="100%" height="100%"> <!-- 动态生成列表项 --> <lv_list_button label="${track.title}" repeat="${playlist.tracks}" on_click="playTrack"/> </lv_list> </lv_tab> <!-- 设置页 --> <lv_tab name="Settings"> <lv_container layout="flex" flex_direction="column" padding="20"> <lv_label text="音量"/> <lv_slider value="${settings.volume}" min="0" max="100" on_value_changed="handleVolumeChange"/> <lv_label text="主题"/> <lv_dropdown options="Light,Dark" value="${settings.theme}" on_value_changed="handleThemeChange"/> </lv_container> </lv_tab> </lv_tabview> </lv_screen>示例 3:使用自定义组件的仪表盘
<!-- 定义可复用的信息卡片组件 --> <component name="InfoCard"> <parameter name="title" type="string"/> <parameter name="value" type="string"/> <parameter name="unit" type="string"/> <parameter name="icon" type="string" default=""/> <lv_container layout="flex" flex_direction="column" width="150" height="150" bg_color="#F0F0F0" border_radius="10" padding="10"> <lv_label text="${icon}" font_size="24"/> <lv_label text="${title}" font_size="12" opacity="150" margin_top="5"/> <lv_container layout="flex" margin_top="10"> <lv_label text="${value}" font_size="24" font_weight="bold"/> <lv_label text="${unit}" font_size="12" opacity="150" margin_left="5"/> </lv_container> </lv_container> </component> <!-- 仪表盘页面 --> <lv_screen> <lv_container layout="grid" grid_columns="2" padding="10"> <!-- 温度卡片 --> <InfoCard title="温度" value="${sensor.temperature}" unit="°C" icon="🌡"/> <!-- 湿度卡片 --> <InfoCard title="湿度" value="${sensor.humidity}" unit="%" icon="💧"/> <!-- 气压卡片 --> <InfoCard title="气压" value="${sensor.pressure}" unit="hPa" icon="📊"/> <!-- PM2.5 卡片 --> <InfoCard title="空气质量" value="${sensor.pm25}" unit="μg/m³" icon="💨"/> </lv_container> </lv_screen>
总结
LVGL Pro XML 的核心价值
LVGL Pro XML 是一套完整的嵌入式 UI 工程解决方案,其核心价值包括:
- 降低开发难度:从复杂的 C 代码编写转变为直观的 XML 声明
- 加速开发流程:从编译+烧录的分钟级反馈变为秒级预览
- 提升代码质量:支持版本控制、自动化测试、CI/CD 集成
- 促进团队协作:设计与开发分离,支持多人并行开发
- 灵活的部署方式:支持静态编译和运行时动态加载两种模式
- 完整的工程工具链:编辑器、预览器、CLI、测试框架、Figma 集成等
何时使用 LVGL Pro XML
适合使用:
- 中等复杂度的嵌入式 UI 应用
- 需要频繁迭代和 UI 更新的项目
- 多人团队开发的项目
- 需要支持多语言的应用
- 对开发效率有较高要求的项目
不适合使用:
- 极简单的单屏幕应用(直接写 C 可能更快)
- 需要自由像素级绘制的应用
- 对性能要求极高、空间极为受限的设备(需要用静态导出)
XML 在嵌入式 UI 开发中的地位
XML 不是"银弹",但它是一个非常好的权衡点------兼顾了轻量性、可读性、易用性和强大功能,使得嵌入式 UI 开发从一个"苦活"变成了一项"可工程化的专业工作"。
- 当