3、LVGL核心对象与样式系统:lv_obj基本操作、样式(lv_style_t)的创建与应用、本地样式与继承样式、状态样式
好,咱们进入第三章。这一章可以说是LVGL的「地基」——对象和样式。你想想看,屏幕上任何一个按钮、滑块、标签,本质上都是一个 lv_obj_t。而它长什么样、什么颜色、什么大小,全由样式说了算。
我个人习惯,拿到一个新项目,第一件事不是画UI,而是先把对象树搭清楚。为什么?因为对象之间的父子关系,直接决定了样式怎么继承、事件怎么传递。搞反了,后面改起来能让你怀疑人生。
3.1 lv_obj基本操作——你屏幕上的每一个像素都归它管
在LVGL里,万物皆对象。创建一个对象很简单:
lv_obj_t * btn = lv_btn_create(lv_scr_act()); // 在活动屏幕上创建一个按钮
但这里有个坑——创建对象时必须指定父对象。父对象可以是屏幕(lv_scr_act()),也可以是另一个容器。我见过新手直接把所有控件都挂在屏幕下,结果想做弹出菜单时,发现层级全乱了。
常用的对象操作就那么几个,我列个表,你一看就明白:
| 操作 | 函数 | 说明 |
|---|---|---|
| 创建 | lv_obj_create(parent) |
创建一个基础对象,作为容器或自定义控件 |
| 设置大小 | lv_obj_set_size(obj, w, h) |
宽高单位是像素 |
| 设置位置 | lv_obj_set_pos(obj, x, y) |
相对于父对象的坐标 |
| 对齐 | lv_obj_align(obj, ref, LV_ALIGN_CENTER, 0, 0) |
相对于参考对象对齐,省去手动算坐标 |
| 添加子对象 | 创建时指定parent即可 | 子对象会显示在父对象之上 |
| 删除 | lv_obj_del(obj) |
会连带删除所有子对象 |
lv_obj_dump_tree(lv_scr_act()) 打印整个对象树。哪个对象挂在谁下面,一目了然。这招在排查显示异常时特别管用。
3.2 样式(lv_style_t)的创建与应用——给对象穿上衣服
对象是骨架,样式就是皮肤。在LVGL 8.x里,样式用 lv_style_t 表示。你得先初始化它,再设置各种属性,最后应用到对象上。
看个完整例子:
/* 1. 定义一个样式变量(通常声明为静态或全局) */
static lv_style_t style_btn;
/* 2. 初始化样式 */
lv_style_init(&style_btn);
/* 3. 设置样式属性 */
lv_style_set_bg_color(&style_btn, lv_color_hex(0x2196F3)); // 背景蓝色
lv_style_set_bg_opa(&style_btn, LV_OPA_COVER); // 不透明
lv_style_set_radius(&style_btn, 8); // 圆角8像素
lv_style_set_border_width(&style_btn, 2); // 边框宽度
lv_style_set_border_color(&style_btn, lv_color_hex(0x1976D2));// 边框深蓝
/* 4. 应用到对象 */
lv_obj_add_style(btn, &style_btn, 0); // 0 表示默认状态
这里有个关键点:样式属性不是随便设的。每个控件类型支持的样式属性不同。比如按钮支持背景、边框、阴影,但文本类控件还支持字体、文字颜色。设错了不会报错,但没效果。
lv_style_t,函数退出后样式就被销毁了,对象会变得「六亲不认」——显示全乱套。所以,样式变量请用 static 或动态分配。
3.3 本地样式与继承样式——谁说了算?
LVGL的样式系统有一个「就近原则」:对象自己的样式 > 父对象的样式 > 屏幕的默认样式。但这里说的「继承」不是CSS那种完全继承,而是有选择地继承。
具体规则是这样的:
- 本地样式:直接通过
lv_obj_add_style()加到对象上的样式。优先级最高。 - 继承样式:父对象设置了
LV_STATE_DEFAULT的样式,且属性是可继承的(比如文字颜色、字体),子对象如果没有覆盖,就会沿用父对象的。
哪些属性可以继承?我直接给你答案:
| 可继承属性 | 不可继承属性 |
|---|---|
文字颜色 (text_color) |
背景颜色、背景透明度 |
字体 (text_font) |
边框、轮廓、阴影 |
文字对齐 (text_align) |
大小、位置、圆角 |
透明度 (opa) |
内边距 (pad 系列) |
举个例子:你给一个容器设置了红色文字,容器里的所有标签默认都会是红色,除非你单独给某个标签设置了蓝色文字。这就是继承的威力——一次设置,全局生效。
实战建议: 我通常把主题色、默认字体、默认文字颜色放在屏幕级别的样式里。这样整个应用风格统一,改起来也快——改一处,全盘变。
3.4 状态样式——按钮按下去是什么样?
一个按钮,平时是一种颜色,按下去变深,松开又恢复。这就是状态样式干的事。
LVGL预定义了几种状态:
LV_STATE_DEFAULT(0):默认状态LV_STATE_PRESSED(1):按下状态LV_STATE_FOCUSED(2):聚焦状态(键盘/遥控器操作时)LV_STATE_DISABLED(4):禁用状态LV_STATE_CHECKED(8):选中状态(复选框、开关等)
用法很简单,就是在 lv_obj_add_style() 的第三个参数指定状态:
/* 默认样式 */
lv_obj_add_style(btn, &style_btn_default, LV_STATE_DEFAULT);
/* 按下样式 */
lv_obj_add_style(btn, &style_btn_pressed, LV_STATE_PRESSED);
/* 禁用样式 */
lv_obj_add_style(btn, &style_btn_disabled, LV_STATE_DISABLED);
当按钮被按下时,LVGL自动应用 LV_STATE_PRESSED 的样式。你不需要写任何事件处理代码——框架帮你做了。
但注意:状态样式是叠加的,不是替换的。什么意思?如果你在按下样式里只设置了背景颜色,那么边框、圆角等属性会沿用默认样式。只有你显式设置的属性才会覆盖。
3.5 知识体系总览
说了这么多,咱们用一张图把整个样式系统的脉络理清楚。这张图我画了好几次才满意,你仔细看看:
这张图把整个体系串起来了。你从对象出发,找到样式,再根据场景选择本地、继承还是状态样式。记住这个流程,写代码时思路会清晰很多。
3.6 实战避坑指南
最后,我把自己这些年踩过的坑总结一下,你遇到了能少走弯路:
- 样式不生效? 先检查
lv_style_init()有没有调用。我至少有三次熬夜调试,最后发现是忘了初始化。 - 颜色不对? 确认你用的是
lv_color_hex()还是lv_color_hex32()。前者是16位色深,后者是32位。混用会导致颜色偏差。 - 继承没起作用? 检查父对象是否设置了
LV_STATE_DEFAULT的样式。只有默认状态的样式才会被子对象继承。 - 状态切换卡顿? 不要在状态样式里设置太多属性。LVGL每次状态切换都会重新计算样式,属性越多越慢。
嗯,这一章的内容就到这。对象和样式是LVGL的根基,你花时间把它吃透,后面学控件、学动画都会轻松很多。别急,慢慢来,写几个demo试试,手感就有了。