【Lvgl】lvgl的相关概念
本文最后更新于21 天前,其中的信息可能已经过时,如有错误请发送邮件到tudougin@163.com

1. LVGL 的核心架构总览

LVGL 架构可分为 5 个主要模块:

  1. 应用层:创建控件、注册事件、编写逻辑代码
  2. 对象系统(Object Tree):控件组织成树结构,样式、布局、事件处理等
  3. 渲染 & 布局引擎(Rendering + Layout):计算控件尺寸、位置、样式、绘制到缓存
  4. 驱动抽象层(Display/Input)
    • 显示:flush_cb(刷屏)
    • 输入:read_cb(读取触摸)
  5. 底层硬件 / RTOS 定时器:LVGL 需要周期性调用 lv_tick_inc + lv_timer

2.运转机制 — 技术详细解读

2.1 引擎架构

关键点:LVGL 引擎运转只靠三个核心条件:输入、输出、时间这三者是整个 LVGL 的“生命线”。


2.1.1 输入接口(Input)

解释:LVGL 需要从你那里获取用户的操作,比如触摸、按键、鼠标、编码器旋钮等。

要做的事情:注册一个 read_cb() 函数
这个函数会在每一轮 lv_timer_handler() 调用时被执行,LVGL 会去查询:

  • 用户有没有触摸?
  • 触摸了哪里?
  • 当前是按下状态还是释放状态?
void touchpad_read_cb(lv_indev_drv_t *drv, lv_indev_data_t *data) {
    if (is_touched()) {
        data->point.x = get_touch_x();
        data->point.y = get_touch_y();
        data->state = LV_INDEV_STATE_PRESSED;
    } else {
        data->state = LV_INDEV_STATE_RELEASED;
    }
}

类比为:“用户在说话”,LVGL 是“听众”,靠你搭建的这根麦克风(read_cb)听懂用户说了什么。


2.1.2 显示接口(Display)

解释:LVGL 并不直接控制 LCD,而是在内存中先画好内容(framebuffer),然后调用驱动程序“把这块内容刷出去”。

要做的事情:实现 flush_cb() 函数,把渲染缓冲区里的像素数据传到实际屏幕。

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p){
    if(disp_flush_enabled) {
        int32_t w = area->x2 - area->x1 + 1;
        int32_t h = area->y2 - area->y1 + 1;
        LCD_Address_Set(area->x1,area->y1,area->x2,area->y2);
        LCD_WritePixels_DMA((uint16_t *)color_p, w*h);
    }
    lv_disp_flush_ready(disp_drv);
}

类比为:“LVGL 是设计师,画好了图纸”,你是粉刷工人,负责按照图纸去刷墙。


2.1.3 时间驱动(Tick)

解释:LVGL 不会自动运转,它是一个“被动调度”的引擎。你必须定时告诉它:“时间过去了”、“检查一下有没有事要干”。

有两个关键函数:

lv_tick_inc(1); // 每 1 毫秒调用一次(通常在系统定时器中)
lv_timer_handler(); // 每帧调用一次(建议每 5~10ms)
  • lv_tick_inc():推进全局“时钟”
  • lv_timer_handler():核心主循环,处理所有更新逻辑

类比为:“你给系统上发条”,否则动画不会动、事件不处理、UI 不更新。

2.2 内部模块

2.2.1 控件树(Widget Tree)

解释:LVGL 的所有控件都是从 lv_obj_t 派生的,构成一棵树状结构,层层嵌套,像 HTML DOM 树。

lv_obj_t *btn = lv_btn_create(parent);
lv_obj_t *label = lv_label_create(btn);
  • 父子关系决定了位置、样式继承、事件冒泡等
  • 所有控件从 lv_scr_act()(当前屏幕对象)开始

2.2.2 事件与动画系统

解释

  • 用户点击按钮时触发 LV_EVENT_CLICKED
  • 控件支持内置的动画(滑动、缩放、颜色渐变等)
  • 所有的事件和动画都会在 lv_timer_handler() 中自动调度
lv_obj_add_event_cb(btn, my_btn_cb, LV_EVENT_CLICKED, NULL);

LVGL 会在合适的时候自动触发 my_btn_cb() 回调。

2.3 lv_timer_handler循环剖析

建议每 10 ~ 20 毫秒调用一次(即每秒运行 100~200 次)太慢,动画卡顿、事件响应延迟;太快,浪费资源(LCD 一般刷不出更高帧率)。

  1. 读取输入:调用输入设备的 read_cb(),获取触摸或按键状态。
  2. 处理动画:推进动画播放,更新控件位置、颜色等属性。
  3. 分发事件:触发控件的事件回调函数,如点击、长按等。
  4. 更新布局:根据控件变化,重新计算位置和尺寸。
  5. 标记重绘区域:找出哪些控件需要重绘,记录为“脏区域”。
  6. 重绘并刷新:在内存中绘制界面,并调用 flush_cb() 刷到 LCD。
  7. 调度定时器: 运行你注册的 lv_timer_t 类型定时任务。

lv_timer_handler() 负责驱动 LVGL 内部所有运行机制,让界面能自动响应用户、更新动画、刷新控件、执行事件与定时任务。是 LVGL 的主循环(大脑)和心跳(节拍器)

2.4 开发范式

开发者要真正让 LVGL 跑起来,其实流程非常清晰,只需掌握三个核心步骤:

  1. 初始化:先初始化 LVGL 本体,并接好以下几个关键模块:

    • 显示驱动:注册 flush_cb 回调函数,让 LVGL 能把图像刷新到实际的 LCD;
    • 输入设备:注册 read_cb 回调函数,让 LVGL 能读取触摸屏或按键状态;
    • 时间驱动:定期调用 lv_tick_inc() 推进时钟,并定时调用 lv_timer_handler() 启动主循环。
  2. 构建界面:使用 LVGL 提供的控件系统搭建 UI 界面:

    • 创建按钮、标签、图标等控件对象;
    • 使用布局系统(如 Flex、Grid)组织控件;
    • 设置控件样式、属性与层级结构。
  3. 注册事件回调:为控件绑定事件处理函数,例如:

    • 点击按钮后执行某个功能;
    • 拖动滑块时更新显示数值;
    • 长按触发菜单等交互逻辑。

界面构建后,其余流程(包括界面重绘、动画播放、事件分发、控件管理等)都由 LVGL 内部自动完成。开发者只需专注于控件创建与逻辑设计,不需要手动管理绘图和更新流程。但请注意:

  • 控件对象的销毁需显式调用 lv_obj_del()
  • 除非使用 lv_scr_load() 切换屏幕,系统才会自动销毁旧界面对象。

3. 相关概念

3.1 显示器和显示对象

  1. 显示器(Display Panel):显示器(或显示面板)指的是物理硬件层面的 LCD、OLED、TFT 屏幕,是你实际看的那个屏幕。

它可能是:

  • 一块 128×160 的 ST7735 LCD 面板
  • 一块 480×320 的 RGB 屏
  • 或者是模拟器上的虚拟屏幕(如 PC 上的模拟仿真窗口)

  1. 显示对象(lv_display_tlv_display_t 是 LVGL 用来管理一块“显示面板”的核心结构。它包含驱动信息、缓冲区、屏幕列表、当前显示状态等。

每一块独立的显示器(物理或虚拟),都会在内部用一个 lv_display_t 对象来表示。它内部会:

  • 保存你注册的显示驱动(flush_cb 等)
  • 管理多个屏幕对象(screen list)
  • 管理当前正在显示的 screen
  • 管理 draw buffer(图像缓冲)

  1. 屏幕(Screen):LVGL 中的“屏幕”指的是一个 lv_obj_t 类型的根对象,它代表一个完整的页面或界面容器。

每个 screen:

  • 是控件的容器,可以创建按钮、标签、图标等子控件
  • 没有父对象,它直接挂在 lv_display_tscreens 列表下
  • 是用户可见的“画面”,可以切换或销毁

三者之间的关系(类比说明)

概念类比在 LVGL 中
显示器(面板)一块幕布(物理屏幕)lv_display_t 表示
显示对象放映设备管理 draw buffer + 驱动接口
屏幕(Screen)当前播放的影片画面lv_obj_t 的根节点,可切换

3.2 控件

控件(Widget) 是构建图形用户界面的核心组成部分。你可以把它们看作是「按钮、标签、图标、滑块」等 可视化组件,类似于 Web 的 DOM 元素、Qt 的 QWidget、安卓的 View。

  1. LVGL 中控件的本质

​ 在 LVGL 中,每一个控件都是一个 lv_obj_t 对象,所有控件都是从它派生出来的。控件之间可以嵌套,形成控件树(Object Tree),方便组织结构和层级关系。

  1. 常见控件分类与用途
控件类型函数用途
基础控件
标签(Label)lv_label_create()显示文字
按钮(Button)lv_btn_create()用于点击交互
图像(Image)lv_img_create()显示图片(支持 JPG/PNG)
线条(Line)lv_line_create()绘制一条线
块(Object)lv_obj_create()通用容器控件(可用作背景、面板)
输入控件
滑块(Slider)lv_slider_create()选择数值范围
切换按钮(Switch)lv_switch_create()开关型组件
滚轮(Roller)lv_roller_create()类似滚动选择器
文本框(TextArea)lv_textarea_create()文本输入框
键盘(Keyboard)lv_keyboard_create()屏幕输入键盘
高级控件
列表(List)lv_list_create()滚动式项目列表
表格(Table)lv_table_create()网格化的数据表格
图表(Chart)lv_chart_create()绘制折线图、柱状图等
页面(Tabview)lv_tabview_create()标签页切换式界面
下拉框(Dropdown)lv_dropdown_create()下拉选择菜单
滚动容器(Tileview)lv_tileview_create()多个页面滑动切换
消息框(Msgbox)lv_msgbox_create()弹出式消息窗口
日历(Calendar)lv_calendar_create()日期选择控件
颜色选择器(Colorwheel)lv_colorwheel_create()颜色拾取控件
布局容器
Flex 容器lv_obj_set_flex_flow()弹性盒布局
Grid 网格lv_obj_set_grid_dsc_array()网格布局容器

3. 属性支持

  • 大小、位置(lv_obj_set_size() / lv_obj_align()
  • 布局与对齐(flex/grid)
  • 样式(颜色、边框、圆角、字体)
  • 状态(隐藏、禁用、焦点等)
  • 动画效果
  • 子对象嵌套
  • 事件回调(点击、滑动、值变、焦点等)

3.3 控件树

在 LVGL 中,控件(lv_obj_t)之间的嵌套关系,是整个 GUI 系统的基础。你可以将其理解为一个 “控件树”,也就是一种典型的 父子对象结构,非常类似于网页中的 DOM 结构,或 Qt 中的控件层级。


  1. 控件嵌套是怎么回事?

所有 LVGL 控件都可以有“父对象”和“子对象”。

  • 子控件的坐标、大小、布局是相对于父控件来计算的;
  • 父控件销毁时,子控件会一并被销毁;
  • 父控件控制了子控件的裁剪区域显示顺序

所以,屏幕(screen)其实也是一个控件(lv_obj_t),它是所有控件的“根”。


  1. 控件树的结构(示意)
lv_scr_act()   // 当前屏幕对象
├── Panel(面板)
│    ├── Label(文字)
│    └── Slider(滑块)
└── Button(按钮)
     └── Label(按钮文字)

这个结构是通过以下代码创建的:

lv_obj_t *scr = lv_scr_act(); // 当前屏幕

lv_obj_t *panel = lv_obj_create(scr);     // panel 是屏幕的子对象
lv_obj_t *label = lv_label_create(panel); // label 是 panel 的子对象
lv_obj_t *slider = lv_slider_create(panel); // slider 也是 panel 的子对象

lv_obj_t *btn = lv_btn_create(scr);       // 按钮直接挂在屏幕上
lv_obj_t *btn_label = lv_label_create(btn); // 按钮里的文字是它的子对象

  1. 父子关系 API

创建控件时指定父对象:

lv_obj_t *child = lv_btn_create(parent);  // parent 可以是屏幕、面板、按钮等

获取关系:

lv_obj_t *parent = lv_obj_get_parent(child);
lv_obj_t *first_child = lv_obj_get_child(parent, 0);
lv_obj_t *next_sibling = lv_obj_get_next(child);

4. 事件系统

事件(Event) 是控件响应交互的核心机制,类似于 “按下按钮执行某功能” 或 “滑块值变化时更新界面”——所有用户交互和控件状态变化,最终都会以事件的形式传递出来。LVGL 中,任何控件(lv_obj_t)都可以注册一个或多个事件回调函数,用于响应特定事件,比如:

  • 被点击(LV_EVENT_CLICKED
  • 被按下(LV_EVENT_PRESSED
  • 值发生变化(LV_EVENT_VALUE_CHANGED
  • 被长按(LV_EVENT_LONG_PRESSED
  • 获取焦点、被隐藏、拖动、滚动……

4.1 事件回调及使用

  1. 添加事件回调:
// 回调函数
void my_event_cb(lv_event_t *e) {
    lv_obj_t *target = lv_event_get_target(e);   // 被触发事件的控件
    lv_event_code_t code = lv_event_get_code(e); // 事件类型
    if(code == LV_EVENT_CLICKED) {
        LV_LOG_USER("Button clicked!");
    }
}

// 添加事件
lv_obj_add_event_cb(obj, my_event_cb, LV_EVENT_ALL, NULL);
  1. 常见的事件类型:
事件宏含义说明
LV_EVENT_PRESSED按下手指/鼠标按下但未释放
LV_EVENT_RELEASED释放手指/鼠标松开
LV_EVENT_CLICKED点击按下 + 松开后触发
LV_EVENT_LONG_PRESSED长按长时间按住
LV_EVENT_VALUE_CHANGED值变滑块/开关等控件数值变更
LV_EVENT_DRAW_MAIN绘制控件重绘时触发(可自定义样式)
LV_EVENT_FOCUSED / DEFOCUSED获取/失去焦点键盘或导航模式下常用
LV_EVENT_READY / CANCEL操作完成/取消文本框、消息框
LV_EVENT_SCROLL滚动中滚动容器正在移动
LV_EVENT_DELETE控件即将销毁常用于释放资源

注意:不是每个控件都会触发所有事件,具体取决于控件的功能类型。

  1. lv_timer_handler() 的关系
  • 所有事件都不会立刻执行,而是 lv_timer_handler() 被调用时触发
  • 所以事件处理依赖于你的主循环定期调用 lv_timer_handler()(通常每 5~10ms);
  • 没有心跳,事件就不会“冒泡”上来。

4.2 实用示例

按钮点击时改变标签内容:

void btn_cb(lv_event_t *e) {
    lv_obj_t *label = lv_event_get_user_data(e);
    lv_label_set_text(label, "按钮被点击!");
}

lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Hello");

lv_obj_add_event_cb(btn, btn_cb, LV_EVENT_CLICKED, label);

滑块值变动更新数值显示:

void slider_cb(lv_event_t *e) {
    lv_obj_t *slider = lv_event_get_target(e);
    int val = lv_slider_get_value(slider);
    printf("当前值: %d\n", val);
}

lv_obj_add_event_cb(slider, slider_cb, LV_EVENT_VALUE_CHANGED, NULL);

5. 部件与状态

部件(Part)状态(State) 是构建丰富交互 UI 的核心机制。它们控制着控件的样式变化、行为反馈和视觉效果,是 LVGL 样式系统的基础。

5.1 部件

Part(部件) 指的是控件的不同组成区域。LVGL 的控件大多不只是一个“整体”,而是可以分成若干部分来分别设置样式。

控件部件(Part)含义
lv_btn(按钮)LV_PART_MAIN整个按钮的背景
lv_slider(滑块)LV_PART_MAIN背景轨道
LV_PART_INDICATOR滑块已选中的部分
LV_PART_KNOB可拖动的圆点
lv_labelLV_PART_MAIN文本内容区域
lv_bar(进度条)LV_PART_MAIN底部轨道
LV_PART_INDICATOR进度条的填充部分

设置样式时指定部件:

lv_obj_set_style_bg_color(slider, lv_color_blue(), LV_PART_INDICATOR);
lv_obj_set_style_radius(slider, 10, LV_PART_KNOB);

每个控件至少有一个 LV_PART_MAIN,部分控件有多个可细分的部件。

5.2 状态

State(状态) 是控件在运行中可能出现的交互或视觉状态,常见状态枚举(可组合):

宏定义含义
LV_STATE_DEFAULT默认状态
LV_STATE_PRESSED正在按下
LV_STATE_CHECKED已选中(用于开关、复选框等)
LV_STATE_FOCUSED获得焦点(键盘/遥控)
LV_STATE_DISABLED被禁用,灰色不可交互
LV_STATE_EDITED正在编辑
LV_STATE_HOVERED鼠标悬停(桌面环境)
LV_STATE_USER_1 ~ 4用户自定义状态(高级用途)

LVGL 内部会自动检测并切换状态,例如:

  • 按下按钮 → 自动进入 LV_STATE_PRESSED
  • lv_obj_add_state(obj, LV_STATE_DISABLED) → 手动设置为禁用
  • 焦点系统/滑块切换时自动设置 FOCUSED

也可以手动设置状态:

lv_obj_add_state(obj, LV_STATE_DISABLED);
lv_obj_clear_state(obj, LV_STATE_DISABLED);

5.3 部件 + 状态 = 精准样式控制

LVGL 的样式系统允许你根据 不同部件、不同状态 来设置样式,实现复杂 UI 效果。

示例:按钮的样式分层控制

// 默认背景颜色
lv_obj_set_style_bg_color(btn, lv_color_white(), LV_PART_MAIN | LV_STATE_DEFAULT);

// 被按下时背景变绿
lv_obj_set_style_bg_color(btn, lv_palette_main(LV_PALETTE_GREEN), LV_PART_MAIN | LV_STATE_PRESSED);

// 禁用时背景变灰
lv_obj_set_style_bg_color(btn, lv_color_hex(0xAAAAAA), LV_PART_MAIN | LV_STATE_DISABLED);

6. 样式

样式(Style)系统 是构建精美 UI 的核心机制。你可以通过样式来控制每个控件的外观,比如颜色、圆角、边框、阴影、字体、透明度、对齐方式等。LVGL 的样式系统非常灵活,可以通过 lv_style_t 自定义样式,也可以通过简化函数快速设置单个属性。

类型样式属性说明
背景bg_color, bg_grad_color, bg_opa控件背景色
边框border_width, border_color控件边框
圆角radius控件边缘圆角半径
字体text_font, text_color文本样式
阴影shadow_color, shadow_width控件的投影
填充pad_top, pad_left内边距
布局width, height, align尺寸和对齐方式

6.1 样式使用方式

  1. 快速设置某个属性
lv_obj_set_style_bg_color(btn, lv_palette_main(LV_PALETTE_BLUE), LV_PART_MAIN);
lv_obj_set_style_radius(btn, 10, LV_PART_MAIN);

这种方式适合简单的样式修改,不需要声明 lv_style_t 对象


  1. 使用 lv_style_t 样式对象(推荐)
lv_style_t style;
lv_style_init(&style);

lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_RED));
lv_style_set_radius(&style, 8);
lv_style_set_text_color(&style, lv_color_white());

lv_obj_add_style(btn, &style, LV_PART_MAIN);

可以将同一个样式重复用于多个控件,样式复用性强

6.2 常用样式属性(函数接口)

样式类别设置函数示例
背景颜色lv_style_set_bg_color设置背景色
背景不透明度lv_style_set_bg_opa设置透明度
边框颜色lv_style_set_border_color设置边框颜色
边框宽度lv_style_set_border_width0 表示无边框
圆角lv_style_set_radiusLV_RADIUS_CIRCLE 表示圆形
字体lv_style_set_text_font设置字体(如 &lv_font_montserrat_16
字体颜色lv_style_set_text_color设置文本颜色
内边距lv_style_set_pad_*控件内部的上下左右边距
阴影lv_style_set_shadow_color / shadow_width控件阴影样式

6.3 实用示例

自定义按钮样式

lv_style_t style_btn;
lv_style_init(&style_btn);

// 默认状态
lv_style_set_bg_color(&style_btn, lv_color_hex(0x3498db));
lv_style_set_radius(&style_btn, 10);
lv_style_set_text_color(&style_btn, lv_color_white());

// 按下状态
lv_style_set_bg_color(&style_btn, lv_color_hex(0x2ecc71));

// 应用到按钮
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_add_style(btn, &style_btn, LV_PART_MAIN);

7. 主题

主题(Theme) 是对多个控件的样式统一管理与复用机制是 LVGL 样式系统的自动化高级封装,不用为每个控件都手动设置样式,只要启用一个主题,它会自动为你创建的每个控件设置默认样式。

lv_disp_t *disp = lv_disp_get_default();
lv_theme_t *theme = lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE),
                                           lv_palette_main(LV_PALETTE_RED),
                                           LV_THEME_DEFAULT_DARK, &lv_font_montserrat_14);

lv_disp_set_theme(disp, theme);

设置后,创建的按钮、标签、滑块等,都会自动带有默认风格,比如蓝底白字、统一字体、圆角大小等。

7.1 默认主题使用方式

设置默认主题:

lv_theme_t *theme = lv_theme_default_init(
    lv_disp_get_default(),             // 绑定显示器
    lv_palette_main(LV_PALETTE_BLUE), // 主色
    lv_palette_main(LV_PALETTE_RED),  // 次色
    false,                             // false=亮色模式, true=暗色
    &lv_font_montserrat_14);          // 默认字体

lv_disp_set_theme(lv_disp_get_default(), theme);

常用调色板:

  • LV_PALETTE_RED
  • LV_PALETTE_BLUE
  • LV_PALETTE_GREEN
  • LV_PALETTE_GREY
  • LV_PALETTE_ORANGE

7.2 自定义主题(高级用法)

可以写自己的主题模块,并在初始化阶段绑定:

lv_theme_t *my_theme = my_custom_theme_init(...);
lv_disp_set_theme(disp, my_theme);

自定义主题结构示意:

lv_theme_t *my_custom_theme_init(...) {
    static lv_theme_t theme;
    lv_theme_init(&theme, ...);

    theme.apply_cb = my_theme_apply;
    return &theme;
}

void my_theme_apply(lv_theme_t *th, lv_obj_t *obj) {
    if (lv_obj_check_type(obj, &lv_btn_class)) {
        // 设置按钮默认样式
        lv_obj_add_style(obj, &my_btn_style, LV_PART_MAIN);
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇