LVGL UI 设计基础
LVGL UI 设计基础
本节介绍使用 LVGL 设计智能家居界面 UI 的基础知识。学习完成后,您将能够:
- 理解 LVGL 的对象系统和层级结构
- 创建按钮、标签、滑块、开关等基础控件
- 应用样式和动画美化界面
- 设计适合智能家居场景的页面布局
在开始本节之前,请确保:
- 已完成 LVGL 安装和配置
- 了解基本的 UI/UX 设计概念
- ESP32 + 屏幕硬件已正常工作
LVGL Object System
Section titled “LVGL Object System”Creating and Managing Objects
Section titled “Creating and Managing Objects”所有 LVGL 控件都是 lv_obj_t 类型的对象,通过父子关系组织:
// 创建控件的基本模式lv_obj_t* obj = lv_obj_create(parent); // 创建lv_obj_set_xxx(obj, value); // 设置属性lv_obj_set_style_xxx(obj, value, part); // 设置样式lv_obj_add_event_cb(obj, callback, code, NULL); // 绑定事件Object Lifecycle
Section titled “Object Lifecycle”// 创建lv_obj_t* screen = lv_obj_create(NULL); // 创建屏幕(顶级对象)lv_obj_t* btn = lv_btn_create(screen); // 创建按钮(屏幕子对象)lv_obj_t* label = lv_label_create(btn); // 创建标签(按钮子对象)
// 修改lv_obj_set_pos(btn, 50, 100); // 设置位置lv_obj_set_size(btn, 120, 50); // 设置大小
// 销毁lv_obj_del(btn); // 删除对象及其子对象lv_obj_clean(screen); // 清空所有子对象Basic Widgets for Smart Home
Section titled “Basic Widgets for Smart Home”Label (标签)
Section titled “Label (标签)”用于显示文字信息:
// 温度标签lv_obj_t* tempLabel = lv_label_create(screen);lv_label_set_text(tempLabel, "24.5°C");lv_obj_set_style_text_font(tempLabel, &lv_font_montserrat_32, 0);lv_obj_set_style_text_color(tempLabel, lv_color_hex(0xFFFFFF), 0);lv_obj_align(tempLabel, LV_ALIGN_TOP_LEFT, 20, 20);
// 房间名称标签lv_obj_t* roomLabel = lv_label_create(screen);lv_label_set_text(roomLabel, "客厅");lv_obj_set_style_text_font(roomLabel, &lv_font_montserrat_16, 0);lv_obj_align_to(roomLabel, tempLabel, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 10);Button (按钮)
Section titled “Button (按钮)”用于触发操作:
// 创建按钮lv_obj_t* lightBtn = lv_btn_create(screen);lv_obj_set_size(lightBtn, 120, 60);lv_obj_set_pos(lightBtn, 40, 100);
// 按钮样式lv_obj_set_style_bg_color(lightBtn, lv_color_hex(0x2196F3), 0);lv_obj_set_style_radius(lightBtn, 12, 0); // 圆角lv_obj_set_style_shadow_width(lightBtn, 10, 0); // 阴影
// 按钮文字lv_obj_t* btnLabel = lv_label_create(lightBtn);lv_label_set_text(btnLabel, "开灯");lv_obj_center(btnLabel);
// 点击事件lv_obj_add_event_cb(lightBtn, light_btn_cb, LV_EVENT_CLICKED, NULL);
// 事件回调void light_btn_cb(lv_event_t* e) { Serial.println("灯光按钮点击"); // 发布 MQTT 消息}Switch (开关)
Section titled “Switch (开关)”用于开/关状态控制:
lv_obj_t* switchCtrl = lv_switch_create(screen);lv_obj_set_pos(switchCtrl, 200, 100);lv_obj_set_size(switchCtrl, 60, 30);
// 开关样式(开启状态)lv_obj_set_style_bg_color(switchCtrl, lv_palette_main(LV_PALETTE_GREEN), LV_STATE_CHECKED);// 开关样式(关闭状态)lv_obj_set_style_bg_color(switchCtrl, lv_color_hex(0x555555), 0);
// 值变化事件lv_obj_add_event_cb(switchCtrl, switch_cb, LV_EVENT_VALUE_CHANGED, NULL);
void switch_cb(lv_event_t* e) { lv_obj_t* sw = lv_event_get_target(e); bool isOn = lv_obj_has_state(sw, LV_STATE_CHECKED); Serial.printf("开关: %s\n", isOn ? "ON" : "OFF");}Slider (滑块)
Section titled “Slider (滑块)”用于调节数值(如灯光亮度、温度设定):
lv_obj_t* slider = lv_slider_create(screen);lv_obj_set_pos(slider, 40, 200);lv_obj_set_size(slider, 200, 20);
// 设置范围lv_slider_set_range(slider, 0, 100);lv_slider_set_value(slider, 50, LV_ANIM_ON);
// 数值显示标签lv_obj_t* sliderValue = lv_label_create(screen);lv_label_set_text_fmt(sliderValue, "%d%%", 50);lv_obj_align_to(sliderValue, slider, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
// 值变化事件lv_obj_add_event_cb(slider, slider_cb, LV_EVENT_VALUE_CHANGED, sliderValue);
void slider_cb(lv_event_t* e) { lv_obj_t* slider = lv_event_get_target(e); lv_obj_t* label = (lv_obj_t*)lv_event_get_user_data(e); int value = lv_slider_get_value(slider); lv_label_set_text_fmt(label, "%d%%", value);}Layout Management
Section titled “Layout Management”Flex Layout (弹性布局)
Section titled “Flex Layout (弹性布局)”// 创建容器lv_obj_t* container = lv_obj_create(screen);lv_obj_set_size(container, 280, 200);lv_obj_center(container);
// 启用 Flex 布局lv_obj_set_flex_flow(container, LV_FLEX_FLOW_ROW_WRAP);lv_obj_set_flex_align(container, LV_FLEX_ALIGN_SPACE_EVENLY, // 主轴 LV_FLEX_ALIGN_CENTER, // 交叉轴 LV_FLEX_ALIGN_SPACE_EVENLY); // 交叉轴(多行)
// 在容器中添加子控件(自动排列)for (int i = 0; i < 6; i++) { lv_obj_t* btn = lv_btn_create(container); lv_obj_set_size(btn, 70, 50);
lv_obj_t* label = lv_label_create(btn); lv_label_set_text_fmt(label, "Btn %d", i + 1); lv_obj_center(label);}Grid Layout (网格布局)
Section titled “Grid Layout (网格布局)”// 创建网格容器lv_obj_t* grid = lv_obj_create(screen);lv_obj_set_size(grid, 280, 240);lv_obj_center(grid);
// 定义网格列和行static lv_coord_t col_dsc[] = {80, 80, 80, LV_GRID_TEMPLATE_LAST};static lv_coord_t row_dsc[] = {50, 50, 50, 50, LV_GRID_TEMPLATE_LAST};
lv_obj_set_grid_dsc_array(grid, col_dsc, row_dsc);
// 在网格中放置控件for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { lv_obj_t* btn = lv_btn_create(grid); lv_obj_set_grid_cell(btn, LV_GRID_ALIGN_STRETCH, col, 1, LV_GRID_ALIGN_STRETCH, row, 1);
lv_obj_t* label = lv_label_create(btn); lv_label_set_text_fmt(label, "R%dC%d", row, col); lv_obj_center(label); }}Styling
Section titled “Styling”Style Properties
Section titled “Style Properties”// 创建样式static lv_style_t style;lv_style_init(&style);lv_style_set_bg_color(&style, lv_color_hex(0x1E88E5));lv_style_set_bg_grad_color(&style, lv_color_hex(0x1565C0)); // 渐变色lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER); // 垂直渐变lv_style_set_radius(&style, 16); // 圆角lv_style_set_border_width(&style, 2);lv_style_set_border_color(&style, lv_color_hex(0x0D47A1));lv_style_set_shadow_width(&style, 8);lv_style_set_shadow_color(&style, lv_color_hex(0x000000));lv_style_set_shadow_ofs_x(&style, 2);lv_style_set_shadow_ofs_y(&style, 4);
// 应用样式到控件lv_obj_add_style(btn, &style, 0); // 默认状态lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED); // 按下状态Animations
Section titled “Animations”Basic Animations
Section titled “Basic Animations”// 淡入动画void fade_in(lv_obj_t* obj) { lv_anim_t a; lv_anim_init(&a); lv_anim_set_var(&a, obj); lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_style_opa); lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_COVER); lv_anim_set_time(&a, 300); // 300ms lv_anim_set_path_cb(&a, lv_anim_path_ease_out); lv_anim_start(&a);}
// 位移动画void slide_in(lv_obj_t* obj) { lv_anim_t a; lv_anim_init(&a); lv_anim_set_var(&a, obj); lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_x); lv_anim_set_values(&a, -200, 0); // 从左侧滑入 lv_anim_set_time(&a, 400); lv_anim_set_path_cb(&a, lv_anim_path_bounce); lv_anim_start(&a);}
// 页面切换动画void page_transition(lv_obj_t* oldPage, lv_obj_t* newPage) { // 旧页淡出 lv_anim_t a_fade_out; lv_anim_init(&a_fade_out); lv_anim_set_var(&a_fade_out, oldPage); lv_anim_set_exec_cb(&a_fade_out, (lv_anim_exec_xcb_t)lv_obj_set_style_opa); lv_anim_set_values(&a_fade_out, LV_OPA_COVER, LV_OPA_TRANSP); lv_anim_set_time(&a_fade_out, 200); lv_anim_start(&a_fade_out);
// 新页滑入 lv_anim_t a_slide_in; lv_anim_init(&a_slide_in); lv_anim_set_var(&a_slide_in, newPage); lv_anim_set_exec_cb(&a_slide_in, (lv_anim_exec_xcb_t)lv_obj_set_x); lv_anim_set_values(&a_slide_in, 320, 0); lv_anim_set_time(&a_slide_in, 300); lv_anim_set_path_cb(&a_slide_in, lv_anim_path_ease_out); lv_anim_start(&a_slide_in);}Smart Home UI Example
Section titled “Smart Home UI Example”Main Dashboard
Section titled “Main Dashboard”// 创建主仪表板void create_main_dashboard() { lv_obj_t* screen = lv_scr_act(); lv_obj_set_style_bg_color(screen, lv_color_hex(0x1a1a2e), 0);
// 状态标题栏 lv_obj_t* title = lv_label_create(screen); lv_label_set_text(title, "🏠 智能家居"); lv_obj_set_style_text_font(title, &lv_font_montserrat_24, 0); lv_obj_set_style_text_color(title, lv_color_hex(0xFFFFFF), 0); lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 15);
// 温度显示 lv_obj_t* temp_card = lv_obj_create(screen); lv_obj_set_size(temp_card, 130, 100); lv_obj_set_pos(temp_card, 10, 60); lv_obj_set_style_bg_color(temp_card, lv_color_hex(0x16213e), 0); lv_obj_set_style_radius(temp_card, 12, 0);
lv_obj_t* temp_icon = lv_label_create(temp_card); lv_label_set_text(temp_icon, "🌡️"); lv_obj_align(temp_icon, LV_ALIGN_TOP_LEFT, 10, 5);
lv_obj_t* temp_value = lv_label_create(temp_card); lv_label_set_text(temp_value, "24.5°C"); lv_obj_set_style_text_font(temp_value, &lv_font_montserrat_24, 0); lv_obj_set_style_text_color(temp_value, lv_color_hex(0x00E5FF), 0); lv_obj_align(temp_value, LV_ALIGN_BOTTOM_MID, 0, -10);
// 湿度卡片 lv_obj_t* hum_card = lv_obj_create(screen); lv_obj_set_size(hum_card, 130, 100); lv_obj_set_pos(hum_card, 150, 60); lv_obj_set_style_bg_color(hum_card, lv_color_hex(0x16213e), 0); lv_obj_set_style_radius(hum_card, 12, 0);
lv_obj_t* hum_icon = lv_label_create(hum_card); lv_label_set_text(hum_icon, "💧"); lv_obj_align(hum_icon, LV_ALIGN_TOP_LEFT, 10, 5);
lv_obj_t* hum_value = lv_label_create(hum_card); lv_label_set_text(hum_value, "62%"); lv_obj_set_style_text_font(hum_value, &lv_font_montserrat_24, 0); lv_obj_set_style_text_color(hum_value, lv_color_hex(0x76FF03), 0); lv_obj_align(hum_value, LV_ALIGN_BOTTOM_MID, 0, -10);
// 灯光控制 lv_obj_t* light_switch = lv_switch_create(screen); lv_obj_set_pos(light_switch, 30, 190);
lv_obj_t* light_label = lv_label_create(screen); lv_label_set_text(light_label, "💡 客厅灯"); lv_obj_align_to(light_label, light_switch, LV_ALIGN_OUT_LEFT_MID, -10, 0);
// 场景按钮 create_scene_button(screen, "🌙 睡眠", 10, 240, LV_PALETTE_INDIGO); create_scene_button(screen, "🏃 离家", 110, 240, LV_PALETTE_TEAL); create_scene_button(screen, "🏠 回家", 210, 240, LV_PALETTE_GREEN);}
void create_scene_button(lv_obj_t* parent, const char* text, int x, int y, lv_palette_t color) { lv_obj_t* btn = lv_btn_create(parent); lv_obj_set_size(btn, 90, 45); lv_obj_set_pos(btn, x, y); lv_obj_set_style_bg_color(btn, lv_palette_main(color), 0); lv_obj_set_style_radius(btn, 22, 0);
lv_obj_t* label = lv_label_create(btn); lv_label_set_text(label, text); lv_obj_center(label);}问题 1: 控件显示不全
Section titled “问题 1: 控件显示不全”原因: 控件尺寸或位置超出了屏幕边界
解决方案: 使用 lv_obj_align() 或 lv_obj_align_to() 代替绝对位置
问题 2: 样式不生效
Section titled “问题 2: 样式不生效”原因: 样式设置了错误的部件或状态
解决方案:
// 正确:添加到控件主体(part = 0)lv_obj_add_style(btn, &style, 0);// 或者指定部件lv_obj_add_style(slider, &style_track, LV_PART_INDICATOR);Summary
Section titled “Summary”本节介绍了 LVGL UI 设计基础:
- 核心控件:Label、Button、Switch、Slider 等智能家居常用控件
- 布局管理:Flex 弹性布局和 Grid 网格布局
- 样式系统:颜色、圆角、渐变、阴影、边框
- 动画系统:位移动画、淡入淡出、页面切换过渡
- 仪表板设计:温度卡片、灯光控制、场景按钮的布局