本文最后更新于48 天前,其中的信息可能已经过时,如有错误请发送邮件到tudougin@163.com
1. LVGL 对象系统(Object System)
LVGL 中几乎所有的 UI 元素都是从 lv_obj_t
派生的对象,遵循“组件 + 样式 +事件” 的思想,以下是官方的例程。
void lv_say_hello(void)
{
// 获取当前活动屏幕对象
lv_obj_t * scr = lv_scr_act(); // lv_scr_act 是 LVGL 8.x 的推荐用法
// 设置屏幕背景颜色为深蓝色
lv_obj_set_style_bg_color(scr, lv_color_hex(0x003a57), LV_PART_MAIN);
// 创建标签对象并设置基本属性
lv_obj_t * label = lv_label_create(scr); // 父对象为屏幕
lv_label_set_text(label, "Hello world"); // 设置文本内容
lv_obj_set_style_text_color(label, lv_color_hex(0xffffff), LV_PART_MAIN); // 设置文字颜色为白色
// 设置字体(可选,如果有自定义字体)
// lv_obj_set_style_text_font(label, &your_font, LV_PART_MAIN);
// 对齐标签到屏幕中心
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
}
1.1 对象的基本概念
lv_obj_t * label = lv_label_create(scr); //以屏幕为父对象创建对象
lv_label_set_text(label, "Hello world"); // 设置文本内容
- 每个控件都是一个对象,基类是
lv_obj_t
- 创建对象时用
lv_obj_create()
或lv_xxx_create()
(比如lv_label_create()
) - 对象树结构:对象之间有父子关系,便于分层管理和事件传播
1.2 对象的属性设置
lv_obj_set_style_text_color(label, lv_color_hex(0xffffff), LV_PART_MAIN); // 设置文字颜色为白色
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
- 设置大小:
lv_obj_set_size(obj, w, h)
- 设置位置:
lv_obj_set_pos(obj, x, y)
- 设置对齐:
lv_obj_align(obj, LV_ALIGN_CENTER, x_ofs, y_ofs)
- 设置样式:
lv_obj_add_style(obj, &style, part)
1.3 样式系统(Style)
lv_obj_set_style_text_color(label, lv_color_hex(0xffffff), LV_PART_MAIN); // 设置文字颜色为白色
// lv_obj_set_style_text_font(label, &your_font, LV_PART_MAIN);
lv_style_t
是样式的核心结构体,可以定义颜色、边框、阴影、字体等
2. 常用控件(Widgets)
LVGL 提供了丰富的控件,以下是一些最常用的控件:
控件 | 描述 | 常用函数 |
---|---|---|
Label | 显示文本 | lv_label_create() , lv_label_set_text() |
Button | 按钮控件 | lv_btn_create() , lv_obj_add_event_cb() |
Slider | 滑条控件 | lv_slider_create() , lv_slider_get_value() |
Switch | 开关 | lv_switch_create() |
Chart | 图表控件 | lv_chart_create() , lv_chart_set_next_value() |
Textarea | 文本输入框 | lv_textarea_create() , lv_textarea_set_text() |
Dropdown | 下拉框 | lv_dropdown_create() , lv_dropdown_set_options() |
Checkbox | 复选框 | lv_checkbox_create() , lv_checkbox_is_checked() |
2.1 按键
按钮(lv_btn
)是一个标准可交互控件,常用于执行点击行为、触发回调、配合标签显示文字等。
功能特性 | 描述 |
---|---|
点击交互 | 支持触控设备点击(触摸)、模拟点击(lv_event_send ) |
事件驱动 | 可绑定多个事件,如 LV_EVENT_CLICKED 、LV_EVENT_PRESSED 、LV_EVENT_RELEASED 等 |
状态响应 | 自动切换不同状态样式,如按下状态、不可用状态等 |
可嵌套控件 | 可放入标签、图标、图片、甚至其他按钮(嵌套) |
动画和过渡 | LVGL 可通过 style transition 实现点击过渡动画 |
测试代码:
// 全局变量保存按钮对象
static lv_obj_t * btn_global = NULL;
/**
* 按钮事件回调:点击后更新文字计数
*/
static void btn_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}
/**
* 定时器回调函数:模拟点击按钮
*/
static void timer_cb(lv_timer_t * timer)
{
ESP_LOGI(TAG, "定时器回调函数:模拟点击按钮");
if(btn_global) {
lv_event_send(btn_global, LV_EVENT_CLICKED, NULL); // 触发点击事件
}
}
/**
* 创建按钮及标签
*/
void lv_set_btn(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);
// ✅ 保存按钮指针,供定时器使用
btn_global = btn;
}
/**
* 主程序入口
*/
void app_main(void) {
ESP_LOGI(TAG, "程序启动");
lvgl_mutex = xSemaphoreCreateMutex();
assert(lvgl_mutex != NULL);
vTaskDelay(pdMS_TO_TICKS(1000));
spi_master_init();
lvgl_tick_timer_init();
lv_port_disp_init();
xTaskCreate(lvgl_task, "lvgl_task", 4096, NULL, 5, NULL);
vTaskDelay(pdMS_TO_TICKS(200));
lv_set_btn();
// ✅ 正确创建 LVGL 定时器(自动每 1000ms 执行一次 timer_cb)
lv_timer_create(timer_cb, 1000, NULL);
// 空循环,不再手动调用 timer_cb
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
示例代码中按钮支持的特性:
- 创建后自动显示
- 可以响应用户点击或程序模拟事件
- 内部嵌套一个标签,并动态更新其文本
- 样式跟随状态改变,如点击时变暗
2.2 滑条
滑条(又叫滑块、滑动条、进度条控件)是图形界面中一种可视化的数值调节控件,用户可以通过拖动滑块改变其数值。
特性 | 描述 |
---|---|
支持范围 | 可设置最小值和最大值(比如 0 ~ 100) |
显示当前值 | 可通过标签显示滑块的当前值 |
双向交互 | 用户可以拖动它,也可以由程序代码控制它 |
事件响应 | 每次用户拖动都会触发 LV_EVENT_VALUE_CHANGED 事件 |
可自定义样式 | 可以设置颜色、高度、圆角、指示条样式等 |
动画支持 | 可使用动画让滑块平滑地变化而不是跳变 |
测试代码:
static lv_obj_t * label_slider = NULL;
static lv_obj_t * slider_global = NULL; // 用于程序控制滑块
/**
* 滑块事件回调:当滑块值变化时更新标签
*/
static void slider_event_cb(lv_event_t * e)
{
lv_obj_t * slider = lv_event_get_target(e);
lv_label_set_text_fmt(label_slider, "%" LV_PRId32, lv_slider_get_value(slider));
lv_obj_align_to(label_slider, slider, LV_ALIGN_OUT_TOP_MID, 0, -15);
}
/**
* 创建滑块与标签
*/
void lv_set_slider(void)
{
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_set_width(slider, 120);
lv_obj_set_pos(slider, 20, 50);
lv_obj_add_event_cb(slider, slider_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
lv_slider_set_range(slider, 0, 100);
lv_slider_set_value(slider, 50, LV_ANIM_OFF);
// 保存到全局变量
slider_global = slider;
label_slider = lv_label_create(lv_scr_act());
lv_label_set_text(label_slider, "50");
lv_obj_align_to(label_slider, slider, LV_ALIGN_OUT_TOP_MID, 0, -15);
}
void update_slider_value(int32_t val)
{
if (slider_global) {
lv_slider_set_value(slider_global, val, LV_ANIM_ON);
lv_event_send(slider_global, LV_EVENT_VALUE_CHANGED, NULL);
}
}
static void slider_auto_inc_cb(lv_timer_t * timer)
{
static int32_t val = 50;
val++;
if(val > 100) val = 0;
update_slider_value(val);
}
示例代码中按钮支持的特性:
- 创建后立即显示在屏幕上
- 支持用户拖动改变数值
- 支持程序主动设置值,含动画效果
- 值变化时触发事件回调
- 可联动标签实时显示当前值
- 可通过定时器自动更新数值
- 支持灵活对齐布局与样式自定义
3. 事件机制
LVGL 在对象的生命周期或用户交互时,自动检测状态变化并发送对应事件。事件机制是Lvgl UI 交互的核心之一,它允许程序监听并响应各种用户操作(比如点击、拖动、值改变),或者对象状态变化(比如创建、删除等)。LVGL 中所有对象都可以接收事件,事件通过回调函数处理:
- 事件类型: 每种事件对应
lv_event_code_t
枚举,如LV_EVENT_CLICKED
,LV_EVENT_VALUE_CHANGED
等。 - 事件函数: 格式固定
void callback(lv_event_t * e)
- 事件对象: 可以通过
lv_event_get_target(e)
获取触发事件的对象。
3.1 常见的事件类型(lv_event_code_t)
完整事件列表见:lv_event.h
事件代码 | 触发时机 | 常见控件 |
---|---|---|
LV_EVENT_CLICKED | 被点击时 | 按钮、图片等 |
LV_EVENT_PRESSED | 按下(触摸) | 按钮、滑块 |
LV_EVENT_RELEASED | 释放(触摸结束) | 按钮 |
LV_EVENT_VALUE_CHANGED | 值改变(如 slider) | 滑块、开关、下拉框 |
LV_EVENT_FOCUSED / LV_EVENT_DEFOCUSED | 获取/失去焦点 | 文本输入框等 |
LV_EVENT_DRAW_MAIN_BEGIN/END | 绘制开始/结束 | 高级自定义绘图用 |
LV_EVENT_DELETE | 对象被删除时 | 所有对象 |
3.2 使用流程
以2.1按键
中的代码为例:
3.2.1 编写事件回调函数
void btn_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * btn = lv_event_get_target(e);
if(code == LV_EVENT_CLICKED) {
static uint8_t cnt = 0;
cnt++;
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "Button: %d", cnt);
}
}
- 通过
lv_event_get_code
判断事件类型,通过lv_event_get_target
知道是哪个对象触发了事件。 - 这段代码只对
LV_EVENT_CLICKED
类型作出响应,展示了事件机制的基本用法。
3.2.2 将事件回调绑定对象
这段代码创建了一个带标签的按钮,并将事件回调函数绑定到该按钮上,以响应所有类型的事件。
void lv_set_btn(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_set_pos(btn, 10, 10);
lv_obj_set_size(btn, 120, 50);
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "Button");
lv_obj_center(label);
// ✅ 保存按钮指针,供定时器使用
btn_global = btn;
}
绑定事件回调函数:
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);
这是事件机制的重点,含义如下:
btn
: 绑定事件的目标对象 —— 这个按钮。btn_event_cb
: 回调函数 —— 触发事件时调用的函数(你上面定义的)。LV_EVENT_ALL
: 指定接收所有事件类型(包括CLICKED
,PRESSED
等等)。(也可以换成具体事件类型,比如LV_EVENT_CLICKED
,只响应点击事件)NULL
: 用户数据(user_data
),此处没有使用。
3.3 模拟触发事件
这段代码通过定时器回调函数,使用 lv_event_send
主动触发按钮的 LV_EVENT_CLICKED
事件,实现了模拟点击行为,从而调用绑定的事件回调函数。
void timer_cb(lv_timer_t * timer)
{
ESP_LOGI(TAG, "定时器回调函数:模拟点击按钮");
if(btn_global) {
lv_event_send(btn_global, LV_EVENT_CLICKED, NULL); // 触发点击事件
}
}
- 使用
lv_event_send
向按钮手动发送一个LV_EVENT_CLICKED
事件。 - 这会使之前绑定到按钮的回调函数
btn_event_cb()
被调用,就好像用户实际点击了按钮一样。