第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的事件系统——怎么让按钮按下去有反应。到时候见!