本笔记以项目中的 obj/myexample.c 系列函数作为核心代码示例进行讲解
配置好跨平台的项目链接:跨平台 LVGL 项目模板
1. LVGL 的对象与盒子模型
在 LVGL 中,所有图形元素(部件/Widgets)都是基于对象的。这种面向对象的思想,使得图形界面的构建模块化且易于管理。
核心理念
- 父子结构: 每个被创建的对象都有一个父对象。屏幕本身也是一个对象,通常作为最顶层的父对象。子对象的位置是相对于父对象的,并且其可见范围不会超出父对象的边界。
- 获取当前活动的屏幕:
lv_scr_act() - 创建一个新的对象:
lv_obj_t *lv_obj_create(lv_obj_t *parent);
- 获取当前活动的屏幕:
- 继承与派生: 所有不同类型的部件(如按钮、标签)都继承自一个基础的
lv_obj_t结构体,并在此之上进行功能的扩展与具体化。
代码示例:创建对象
1 | void demo_create_basic_objects(void) { |
常用接口 (API)
以下是一些用于操作对象基本属性的常用函数:
- 尺寸设置与获取:
lv_obj_set_width(obj, new_width);lv_obj_set_height(obj, new_height);lv_obj_set_size(obj, width, height);lv_obj_get_width(obj);lv_obj_get_height(obj);
- 位置设置与获取:
lv_obj_set_x(obj, new_x);lv_obj_set_y(obj, new_y);lv_obj_set_pos(obj, x, y);lv_obj_get_x(obj);lv_obj_get_y(obj);
- 对齐:
lv_obj_align(obj, LV_ALIGN_..., x_offset, y_offset);lv_obj_align_to(obj, base_obj, LV_ALIGN_..., x_offset, y_offset);
代码示例:设置位置和尺寸
1 | void demo_set_position_and_size(void) { |
对象的盒子模型
LVGL 遵循类似 CSS 的 border-box 模型,这对于理解对象的布局与样式至关重要。一个对象的“盒子”由内到外包含以下几个部分:
- 内容 (Content): 盒子实际容纳其子对象的区域。
- 填充 (Padding): 内容区域与边框之间的空间。
- 边框 (Border): 包围在填充区外围的线条,拥有厚度、颜色等属性。
- 轮廓 (Outline): 绘制在边框之外的线条,用于突出元素,但不占据实际的布局空间。LVGL 中没有外边距 (margin) 的概念,轮廓起到了类似的作用。
- 边界 (Bounding Box): 指的是由对象的宽度和高度定义的整个区域。
2. 样式 (Style)
样式用于定义对象的外观。LVGL 的样式系统类似网页三大件中的 CSS,具有灵活、高效的特点。
样式的组成:局部与状态
样式可以被应用于对象的特定部分 (Part) 和在特定的状态 (State) 下。
- 常见局部:
LV_PART_MAIN(主体),LV_PART_SCROLLBAR(滚动条),LV_PART_INDICATOR(指示器),LV_PART_KNOB(旋钮) 等。 - 常见状态:
LV_STATE_DEFAULT(默认),LV_STATE_PRESSED(按下),LV_STATE_FOCUSED(聚焦),LV_STATE_DISABLED(禁用) 等。
样式特点
- 复用与级联: 一个样式可以被多个对象使用。一个对象也可以添加多个样式,后添加的样式优先级更高。
- 继承: 如果对象没有指定某个属性(如文本颜色),它会从父对象继承。
- 本地样式: 对象可以拥有高优先级的“本地样式”,用于进行个别的、独特的样式设置。
- 过渡效果: 样式支持过渡动画,可以在状态改变时产生平滑的视觉效果。
使用样式三部曲
- 初始化,向系统申请一个空间用来存样式
- 设置样式
- 将样式添加到对象
1 | void demo_create_and_apply_style(void) { |
样式应用举例:状态过渡动画
通过为不同状态设置不同的样式,并定义过渡描述,可以实现平滑的动画效果。
1 | // 定义一个过渡描述,指定哪些属性(props)参与动画 |
3. 事件 (Event) 处理机制
当对象的状态发生改变时(如被点击、数值变化等),LVGL 会触发相应的事件。我们可以通过为对象添加回调函数来响应这些事件。
事件特点
- 一个回调函数可以处理多种不同的事件。
- 一个对象可以根据不同的事件,绑定不同的回调函数。
- 对象的事件可以传递给其父对象,这一机制被称为事件冒泡。
事件种类
事件被分为几大类,每一类都包含具体的事件代码(宏定义在 lv_event.h 中):
- 输入设备事件: 如
LV_EVENT_PRESSED(按下),LV_EVENT_CLICKED(单击),LV_EVENT_LONG_PRESSED(长按)。 - 绘图事件
- 其他特殊事件: 如
LV_EVENT_VALUE_CHANGED(数值改变)。 - 自定义事件
事件处理流程
编写事件回调函数: 定义一个符合
lv_event_cb_t原型的函数。1
2
3
4
5
6
7
8
9
10
11
12
13// 事件回调函数
static void button_event_handler(lv_event_t *e) {
// 获取事件代码
lv_event_code_t code = lv_event_get_code(e);
// 获取通过 lv_obj_add_event_cb 传递的用户数据
lv_obj_t *label = (lv_obj_t *)lv_event_get_user_data(e);
// 判断是否是点击事件
if (code == LV_EVENT_CLICKED) {
// 修改标签的文本
lv_label_set_text(label, "Hello, World!");
}
}为对象添加事件: 使用
lv_obj_add_event_cb函数将回调函数与特定的事件绑定。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void demo_add_event_handler(void) {
lv_obj_t* panel = lv_obj_create(lv_scr_act());
lv_obj_set_size(panel, 200, 150);
lv_obj_center(panel);
// 创建一个标签用于显示结果
lv_obj_t* info_label = lv_label_create(panel);
lv_label_set_text(info_label, "Please click the button");
lv_obj_align(info_label, LV_ALIGN_TOP_MID, 0, 10);
// 创建一个按钮
lv_obj_t* btn = lv_btn_create(panel);
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 20);
lv_obj_t* btn_label = lv_label_create(btn);
lv_label_set_text(btn_label, "Click Me");
lv_obj_center(btn_label);
// 关键步骤:为按钮添加事件回调
// 将 info_label 作为用户数据传递给回调函数
lv_obj_add_event_cb(btn, my_event_handler, LV_EVENT_CLICKED, info_label);
}
事件冒泡
当一个子对象的事件被触发后,如果开启了事件冒泡,该事件会像气泡一样“向上”传递给它的父对象,父对象也可以对这个事件进行响应。
- 如何开启: 使用
lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE);为子对象添加标志。 - 如何判断: 在回调函数中,可以通过
lv_event_get_target()获取最初触发事件的对象,通过lv_event_get_current_target()获取当前正在处理事件的对象。如果两者不一致,则说明事件是冒泡上来的。
代码示例
下面的代码创建了一个按钮(子)和一个面板(父)。点击按钮后,上方的标签文本会先显示事件在按钮上触发,短暂延迟后会显示事件冒泡到了父面板上
1 | // 事件冒泡的回调函数 |




