2、LVGL基础对象(lv_obj):对象的创建与删除、父子层级关系、常用属性(大小、位置、对齐)、事件回调机制入门。

各位同学,欢迎来到第二章。这一章我们聊聊LVGL里最核心的东西——基础对象,也就是 lv_obj。

你想想看,LVGL里所有的控件,什么按钮、标签、滑块,本质上都是 lv_obj 的“子类”。搞懂了这个基础对象,后面学啥都快。我个人习惯把 lv_obj 比作一张白纸——你可以在上面画东西,也可以在上面贴其他纸片。嗯,就是这么个道理。

核心认知:LVGL中一切控件皆对象。掌握 lv_obj 就等于掌握了LVGL的“内功心法”。

lv_obj 基础对象知识体系 lv_obj 基础对象 创建与删除 父子层级关系 常用属性 事件回调机制 大小 (size) 位置 (pos) 对齐 (align) 💡 学习路径建议 先掌握创建与删除 → 理解父子层级 → 熟练设置属性 → 最后玩转事件回调

2.1 对象的创建与删除

先说说怎么“变”出一个对象来。LVGL里创建对象就一行代码:

lv_obj_t * btn = lv_obj_create(lv_scr_act());  // 在当前活动屏幕创建按钮

这里 lv_obj_create() 的参数是父对象。说白了,你告诉系统:“我要在这个父对象上面放一个新对象。” 返回值是一个指针,指向新创建的对象。我刚开始学的时候老忘记保存这个指针,结果后面想操作对象都找不到它——嗯,这个坑我踩过。

删除对象更简单:

lv_obj_del(btn);  // 删除btn对象

这里有个细节要注意:如果你删除了一个父对象,它所有的子对象也会被自动删除。我曾经在项目里不小心删了一个容器,结果里面的十几个按钮全没了,调试了半天才发现是父对象被误删了。

⚠️ 避坑指南:千万不要在事件回调里直接删除触发事件的对象本身,否则会导致野指针崩溃。正确的做法是用 lv_event_send() 延迟删除,或者使用 lv_obj_del_async()。

2.2 父子层级关系

LVGL的界面是一个树形结构。每个对象都有一个父对象,也可以有多个子对象。屏幕(Screen)是最顶层的父对象。

我举个例子你就明白了:

lv_obj_t * scr = lv_scr_act();              // 获取当前屏幕
lv_obj_t * cont = lv_obj_create(scr);       // 在屏幕上创建容器
lv_obj_t * btn = lv_obj_create(cont);       // 在容器里创建按钮

这个层级关系决定了三件事:

  • 显示顺序:子对象永远显示在父对象之上
  • 裁剪范围:子对象超出父对象的部分默认会被裁剪
  • 事件传递:点击子对象时,事件会冒泡到父对象

我记得有一次做智能家居面板,需要在一个卡片容器里放多个开关按钮。如果不设置父子关系,按钮就会跑到卡片外面去。后来我设置了正确的父子层级,一切就正常了。

💡 小技巧:用 lv_obj_get_parent() 获取父对象,用 lv_obj_get_child() 遍历子对象。这在调试复杂界面时特别有用。

2.3 常用属性设置

创建了对象,接下来就是给它“打扮打扮”。常用的属性就三个:大小、位置、对齐。

2.3.1 大小(Size)

lv_obj_set_size(btn, 100, 50);  // 设置宽度100,高度50

你也可以用 lv_obj_set_width() 和 lv_obj_set_height() 分别设置。我个人习惯用 lv_obj_set_size(),一行搞定,省事。

2.3.2 位置(Position)

lv_obj_set_pos(btn, 20, 30);  // 相对于父对象左上角,x=20, y=30

这里要注意:坐标是相对于父对象的。如果你把按钮放在容器里,坐标就是相对于容器的左上角。我见过有人把坐标设成相对于屏幕的,结果容器移动后按钮位置全乱了。

2.3.3 对齐(Alignment)

对齐是LVGL里非常强大的功能。它让你不用手动计算坐标:

lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);  // 在父对象中居中

第二个参数是对齐方式,常用的有:

对齐常量 说明
LV_ALIGN_CENTER 居中
LV_ALIGN_TOP_LEFT 左上角
LV_ALIGN_TOP_MID 顶部居中
LV_ALIGN_BOTTOM_RIGHT 右下角
LV_ALIGN_LEFT_MID 左侧居中

后两个参数是x和y的偏移量。比如你想让按钮在屏幕正中央,但偏右10像素:

lv_obj_align(btn, LV_ALIGN_CENTER, 10, 0);

实战经验:我建议你优先使用 lv_obj_align() 而不是手动设置坐标。因为当屏幕尺寸变化时,对齐会自动适应,而手动坐标会跑偏。这在做多分辨率适配时特别重要。

2.4 事件回调机制入门

对象创建好了,属性也设好了,但它还不会“动”。要让对象响应用户操作,就需要事件回调。

事件回调说白了就是:当某个事件发生时(比如点击、长按、拖动),系统会调用你写的一个函数。

注册事件回调的代码:

lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);

// 回调函数实现
static void btn_event_cb(lv_event_t * e) {
    lv_obj_t * obj = lv_event_get_target(e);  // 获取触发事件的对象
    LV_LOG_USER("按钮被点击了!");
}

这里 LV_EVENT_CLICKED 是事件类型。LVGL里有很多事件类型,常用的有:

事件类型 触发时机
LV_EVENT_CLICKED 点击完成(按下并释放)
LV_EVENT_PRESSED 按下瞬间
LV_EVENT_RELEASED 释放瞬间
LV_EVENT_LONG_PRESSED 长按触发
LV_EVENT_VALUE_CHANGED 值改变(如滑块拖动)

最后一个参数是用户自定义数据。你可以传一个指针进去,在回调里用 lv_event_get_user_data() 取出来。这个功能很实用,比如多个按钮共用一个回调函数时,通过用户数据区分是哪个按钮被点击。

⚠️ 注意:回调函数里不要做耗时操作,比如延时、文件读写等。LVGL的事件处理是在主循环里同步执行的,耗时操作会卡死界面。如果确实需要做耗时操作,请使用 lv_task 或 lv_timer 异步处理。

好了,这一章的内容就这些。你可能会觉得知识点有点多,但别急,这些都是最基础的东西。后面每一章都会反复用到这些概念。你先把对象的创建、父子关系、属性设置和事件回调这四个点吃透,后面的学习就会顺畅很多。