3、LVGL基础对象(lv_obj):对象的创建与删除、父子层级关系、大小与位置、对齐方式、样式基础
好,咱们今天来啃LVGL最核心的一块——基础对象,也就是 lv_obj。你想想看,整个LVGL的界面,说白了就是一堆对象堆出来的。按钮是对象,标签是对象,滑块也是对象。它们都继承自同一个祖宗——lv_obj。
我刚开始学LVGL的时候,觉得这玩意儿不就是new一个对象嘛,有啥好讲的?结果真上手做项目,发现对象创建了不显示、父子关系搞乱了、对齐方式选错了……各种坑踩了个遍。所以这一章,咱们把基础对象彻底捋清楚。
3.1 对象的创建与删除
创建对象,就一个函数:
lv_obj_t * lv_obj_create(lv_obj_t * parent);
这个 parent 参数,就是对象的父对象。传 NULL 的话,它会挂到屏幕的根对象上。我个人习惯,从来不传 NULL,哪怕暂时不知道放哪,也会先创建一个容器对象当父级。为什么?因为后面调整布局会方便很多。
删除对象更简单:
void lv_obj_del(lv_obj_t * obj);
注意,这个函数会递归删除所有子对象。我曾经在项目里犯过一个错——删了一个父对象,结果它下面的子对象全没了,而我还拿着子对象的指针到处用,直接硬核死机。嗯,这里要注意:对象删除后,所有指向它的指针都变成野指针。
NULL。
如果你想延迟删除,可以用:
void lv_obj_del_async(lv_obj_t * obj);
这个函数会在下一个事件循环中执行删除,适合在中断或回调里调用。
3.2 父子层级关系
LVGL的对象组织方式,就是一棵树。每个对象可以有多个子对象,但只有一个父对象。根节点是 lv_scr_act(),也就是当前活动屏幕。
为什么要搞父子关系?说白了,就是继承和联动。父对象移动,子对象跟着动;父对象隐藏,子对象全隐藏;父对象被删除,子对象全完蛋。
常用的父子操作函数:
| 功能 | 函数 |
|---|---|
| 获取父对象 | lv_obj_get_parent(obj) |
| 获取子对象(按索引) | lv_obj_get_child(obj, idx) |
| 获取子对象数量 | lv_obj_get_child_cnt(obj) |
| 将对象移到顶层 | lv_obj_move_foreground(obj) |
| 将对象移到底层 | lv_obj_move_background(obj) |
我记得有一次做仪表盘界面,需要把报警弹窗显示在最前面。当时没调层级,弹窗被其他控件挡住了。后来用了 lv_obj_move_foreground,问题就解决了。你想想看,这种小细节,不知道的话能折腾半天。
lv_obj_move_foreground,或者在创建时把它放在最后创建——后创建的对象默认在更上层。
3.3 大小与位置
每个对象都有两个核心属性:大小(width, height)和位置(x, y)。
设置大小:
lv_obj_set_width(obj, 100);
lv_obj_set_height(obj, 50);
lv_obj_set_size(obj, 100, 50); // 一次性设置宽高
设置位置:
lv_obj_set_x(obj, 10);
lv_obj_set_y(obj, 20);
lv_obj_set_pos(obj, 10, 20); // 一次性设置x和y
这里有个关键点:位置是相对于父对象的。如果你把父对象移动了,子对象的位置(相对于父对象)不变,但它在屏幕上的绝对位置会变。
我刚开始做项目时,经常搞混这个。明明设置了坐标,对象却跑到了奇怪的地方。后来才意识到,是父对象的坐标变了。所以,调试位置问题时,先检查父对象。
lv_obj_get_x(obj) 和 lv_obj_get_y(obj) 获取,但这两个函数返回的其实是相对于父对象的坐标。要获取屏幕绝对坐标,需要用 lv_obj_get_x_aligned(obj) 或自己递归计算。
3.4 对齐方式
手动算坐标太累了,LVGL提供了对齐功能。说白了,就是让对象自动摆好位置。
最常用的对齐函数:
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
这个函数的意思:把 obj 相对于它的父对象居中对齐,偏移量为 (0, 0)。
常用的对齐常量:
| 常量 | 含义 |
|---|---|
LV_ALIGN_CENTER |
居中 |
LV_ALIGN_TOP_LEFT |
左上角 |
LV_ALIGN_TOP_MID |
顶部居中 |
LV_ALIGN_TOP_RIGHT |
右上角 |
LV_ALIGN_BOTTOM_LEFT |
左下角 |
LV_ALIGN_BOTTOM_MID |
底部居中 |
LV_ALIGN_BOTTOM_RIGHT |
右下角 |
LV_ALIGN_LEFT_MID |
左侧居中 |
LV_ALIGN_RIGHT_MID |
右侧居中 |
还有一个更灵活的函数:
lv_obj_align_to(obj, base_obj, LV_ALIGN_OUT_RIGHT_TOP, 10, 0);
这个是把 obj 对齐到 base_obj 的右侧顶部,再偏移 (10, 0)。非常适合做“在某个控件旁边显示”的效果。
我记得做菜单界面时,需要让子菜单出现在父菜单项的右侧。用 lv_obj_align_to 配合 LV_ALIGN_OUT_RIGHT_TOP,一行代码搞定。如果手动算坐标,每个菜单项的位置都得重新算,太麻烦了。
LV_ALIGN_CENTER 偏移 (10, 0),就是居中后再向右移10像素。这个偏移量可以是负数,向左或向上移动。
3.5 样式基础
样式,就是对象的外观。LVGL的样式系统非常强大,但咱们先从最基础的开始。
设置样式,需要先创建一个样式:
lv_style_t style;
lv_style_init(&style);
lv_style_set_bg_color(&style, lv_color_hex(0x3498db));
lv_style_set_radius(&style, 5);
然后把这个样式添加到对象上:
lv_obj_add_style(obj, &style, LV_STATE_DEFAULT);
第三个参数是状态,比如 LV_STATE_PRESSED(按下状态)、LV_STATE_DISABLED(禁用状态)。同一个对象可以有不同的样式对应不同的状态。
常用的样式属性:
| 属性 | 设置函数 | 说明 |
|---|---|---|
| 背景颜色 | lv_style_set_bg_color |
设置背景色 |
| 圆角半径 | lv_style_set_radius |
设置圆角大小 |
| 边框宽度 | lv_style_set_border_width |
设置边框粗细 |
| 边框颜色 | lv_style_set_border_color |
设置边框颜色 |
| 内边距 | lv_style_set_pad_all |
设置内边距 |
| 透明度 | lv_style_set_opa |
设置透明度(0-255) |
这里有个坑:样式是全局共享的。如果你修改了一个样式对象,所有使用这个样式的对象都会跟着变。我曾经在项目里犯过这个错——改了一个按钮的样式,结果所有按钮都变了样。嗯,后来我学会了,每个独立的对象最好用独立的样式实例,或者用 lv_style_copy 复制一份。
3.6 知识体系总览
好了,上面讲了这么多,咱们用一张图把本章的知识体系串起来:
这张图把本章的五个核心知识点串在了一起。你可以看到,lv_obj 是中心,向外辐射出创建删除、父子层级、大小位置、对齐方式和样式基础。这五个点,就是你在LVGL中操作任何控件的基础。
好了,这一章的内容就到这。记住,基础对象是所有控件的根,把这一章吃透了,后面学按钮、标签、滑块什么的,你会发现它们都差不多——只是多了几个专有属性和事件而已。