第3章 LVGL基础对象:lv_obj_t对象
好,咱们今天来聊聊LVGL里最基础的东西——lv_obj_t对象。说白了,你在屏幕上看到的每一个按钮、标签、滑块,本质上都是这个对象的“变种”。我刚开始学LVGL时,总觉得这玩意儿抽象,后来做项目多了才发现,搞懂它,后面所有控件都是手到擒来。
3.1 什么是lv_obj_t?
lv_obj_t是LVGL所有控件的“祖宗”。你可以把它想象成一块空白的画布,或者一个空盒子。所有复杂的控件,比如按钮、进度条,都是在它基础上“装修”出来的。
我个人习惯把lv_obj_t比作一个“空壳子”。它本身不显示任何东西,但你可以给它设置大小、位置、颜色,还能往里面塞别的对象。嗯,这里要注意:所有控件都是lv_obj_t的子类,但反过来不行。
核心要点:lv_obj_t是LVGL对象系统的基石。你写的每一行UI代码,最终都在操作这个结构体。
3.2 创建与删除对象
3.2.1 创建对象
创建对象很简单,就一个函数:
lv_obj_t * obj = lv_obj_create(parent);
这个parent参数,就是对象的父对象。如果不指定父对象,可以传NULL,但这样创建出来的对象会直接挂在屏幕根节点下。我在项目中遇到过,新手经常忘了指定父对象,结果控件显示不出来——其实不是没创建成功,而是没挂到正确的位置上。
举个例子,创建一个简单的对象:
lv_obj_t * scr = lv_obj_create(NULL); // 创建一个屏幕对象
lv_obj_t * btn = lv_obj_create(scr); // 在屏幕上创建一个按钮
3.2.2 删除对象
删除对象用lv_obj_del:
lv_obj_del(obj);
这里有个坑:删除父对象时,所有子对象也会被自动删除。我曾经在项目中犯过这个错——删了一个容器,结果里面的十几个按钮全没了,调试了半天才发现是父对象被提前释放了。
避坑指南:千万不要在回调函数里直接删除正在处理事件的对象。我曾经这么干过,结果程序直接崩溃。正确的做法是用lv_event_send延迟删除,或者用lv_obj_del_async。
3.3 对象属性:大小、位置、颜色
创建完对象,接下来就是给它“打扮”了。三个最基础的属性:大小、位置、颜色。
3.3.1 大小
设置大小用lv_obj_set_size:
lv_obj_set_size(obj, 200, 100); // 宽200,高100
你想想看,如果只创建对象不设置大小,它默认是0x0,屏幕上根本看不见。我刚开始学的时候就踩过这个坑,还以为代码写错了。
3.3.2 位置
位置设置用lv_obj_set_pos:
lv_obj_set_pos(obj, 50, 30); // x=50, y=30
注意,这个坐标是相对于父对象的。如果父对象移动了,子对象也会跟着动。这就是父子关系的妙处。
我个人习惯用lv_obj_align来对齐,更直观:
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0); // 居中
3.3.3 颜色
颜色设置稍微复杂一点,因为对象本身没有“颜色”属性,但你可以设置它的背景色:
lv_obj_set_style_bg_color(obj, lv_color_hex(0x2196F3), 0); // 蓝色背景
lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, 0); // 不透明度
这里0是状态值,表示默认状态。如果你想让按钮按下时变红,可以设置状态为LV_STATE_PRESSED。
小技巧:颜色值用lv_color_hex最方便,直接写16进制RGB值。我常用lv_palette_main(LV_PALETTE_BLUE)来取主题色,省得自己调色。
3.4 父子对象关系
父子关系是LVGL里最重要的概念之一。说白了,就是“谁管谁”的问题。
- 父对象移动,子对象跟着动——这是最直观的体现。
- 父对象被裁剪,子对象超出部分会被隐藏——默认情况下,子对象不能超出父对象边界。
- 父对象被删除,所有子对象自动删除——省心但也容易踩坑。
举个例子,你做一个仪表盘界面:
lv_obj_t * container = lv_obj_create(parent); // 容器
lv_obj_set_size(container, 300, 200);
lv_obj_set_pos(container, 10, 10);
lv_obj_t * label = lv_label_create(container); // 标签,父对象是容器
lv_label_set_text(label, "Hello LVGL");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
这样,当你移动container时,label会自动跟着移动。我在项目中做多语言切换时,就靠这种父子关系批量调整控件位置,省了不少事。
经验之谈:合理利用父子关系,可以大大简化布局代码。比如做一个设置页面,把相关控件放在同一个容器里,移动容器就等于移动整组控件。
3.5 实战小例子
最后,咱们写一个完整的例子,把今天学的串起来:
// 创建屏幕
lv_obj_t * scr = lv_obj_create(NULL);
lv_obj_set_style_bg_color(scr, lv_color_hex(0x333333), 0);
// 创建容器
lv_obj_t * cont = lv_obj_create(scr);
lv_obj_set_size(cont, 200, 150);
lv_obj_align(cont, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_bg_color(cont, lv_color_hex(0x2196F3), 0);
// 创建标签
lv_obj_t * label = lv_label_create(cont);
lv_label_set_text(label, "Hello LVGL!");
lv_obj_set_style_text_color(label, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
这段代码创建了一个深色背景的屏幕,中间放一个蓝色容器,容器里显示白色文字。嗯,这就是最基础的LVGL界面了。
课后练习:试着修改上面的代码,让容器移动到屏幕左上角,并把标签文字改成你的名字。看看父子关系是怎么工作的。
好了,这一章就到这里。下一章咱们聊聊LVGL的事件系统——怎么让按钮按下去有反应。到时候见!