当前位置: 首页 > news >正文

LVGL:基础对象

一:对象

在 LVGL 中,⽤⼾界⾯的 基本组成部分是对象(控件),也称为 Widgets 。

例如,⼀个 按钮、标签、图像、 列表、图表或者⽂本区域。

所有的对象都使⽤ lv_obj_t 指针作为句柄进⾏引⽤。之后可以使⽤该指针来设置或获取对象的属性。

二:基础对象( lv_obj )

基础对象 ( 控件 ) 实现了屏幕上控件的基本属性,例如:

坐标 ,⽗对象 ,基于⽗对象的后代 ,包含样式 ,诸如 Clickable 、 Scrollable 等属性

在⾯向对象的思想中,基础对象 是 LVGL 中所有其他对象都继承的基类。

基础对象的功能可以与其他控件⼀起使⽤。

例如 lv_obj_set_width(slider, 100) 基础对象可以直接⽤作⼀个简单的控件:它只不过是⼀个矩形。

屏幕是没有⽗对象的基础对象

lv_screen_active()(void); // 活动屏幕(screen_active)

lv_layer_top(void); // 顶层(top layer)

lv_layer_sys(void); // 系统层(system layer)

// 可以在不同层上创建对象(控件)

lv_obj_create(lv_screen_active());

lv_obj_create(lv_layer_top());

lv_obj_create(lv_layer_sys());

学会创建对象 lv_obj_create();

三:基础对象的大小

1. 设置大小(Set Size)

设置宽度: lv_obj_set_width(obj, new_width);

设置⾼度: lv_obj_set_height(obj, new_height);

同时设置宽度、⾼度: lv_obj_set_size(obj, new_width, new_height);

2.获取大小(Get Size)

获取实际宽度: lv_obj_get_width(obj);

获取实际⾼度: lv_obj_get_height(obj);

获取实际可⽤的宽度: lv_obj_get_content_width(obj);

获取实际可⽤的⾼度: lv_obj_get_content_height(obj);

获取实际可⽤的宽⾼区域: lv_obj_get_content_coords(obj);

在 LVGL(嵌入式 GUI 库)中,"实际可用的宽度 / 高度 / 区域" 指的是组件(对象)内部真正可以用来显示内容的空间大小

实际可用的也就是,中间的绿色部分。(盒子模型)

四:LVGL 对象的位置

LVGL 屏幕的坐标系和我们熟悉的坐标系不⼀样, LVGL 的坐标系是我们⼀般称之为 “LCD 坐标系 ” ,他的原点位 置和直⻆坐标系的不⼀样(坐标原点在左上⻆,⽔平向右为 x 轴正⽅向,竖直向下为 y 轴正⽅向 ** )

其实我们只需要记住⼀点: LVGL 的原点位置在左上⻆。

屏幕区域的表⽰(假设屏幕是 1024*600 分辨率):

1.设置位置 (Set Position)

设置x轴方向的坐标位置:lv_obj_set_x(obj,new_x);

设置y轴方向的坐标位置:lv_obj_set_y(obj,new_y);

同时设置x,y坐标位置:lv_obj_set_pos(obj,new_x,new_y);

2. 设置对齐(Align)

参照父对象对齐:lv_obj_set_align(obj, LV_ALIGN_CENTER);//这是中心对齐

第二个参数为对齐类型,根据图标进行选择

参照父对象对齐后再设置坐标位置:lv_obj_align(obj, LV_ALIGN_CENTER, x, y);

  1. obj:要对齐的目标组件(对象)
  2. LV_ALIGN_CENTER:对齐参考点(枚举值)
    • LV_ALIGN_CENTER 表示 "以父对象的中心为参考点"
    • 其他常用参考点:LV_ALIGN_TOP_LEFT(父对象左上角)、LV_ALIGN_BOTTOM_RIGHT(父对象右下角)等
  3. x:在参考点基础上的 X 轴偏移量(像素),正数向右,负数向左
  4. y:在参考点基础上的 Y 轴偏移量(像素),正数向下,负数向上

参照另一个对象(无父子关系)对齐后设置坐标:lv_obj_align_to(obj_to_align, obj_reference, LV_ALIGN_..., x, y);

  1. obj_to_align:需要被对齐的目标对象(要移动的组件)
  2. obj_reference:作为参考的对象(基准组件)
  3. LV_ALIGN_...:对齐方式(枚举值),指定两个对象的对齐点
    • 例如 LV_ALIGN_RIGHT_MID 表示 "目标对象的左中对齐参考对象的右中"
    • 其他常用值:LV_ALIGN_BOTTOM_LEFT(目标对象上左对齐参考对象下左)、LV_ALIGN_TOP_RIGHT(目标对象下右对齐参考对象上右)等
  4. x:对齐后的 X 轴额外偏移量(像素)
  5. y:对齐后的 Y 轴额外偏移量(像素)

3. 获取位置 (Get Position)

获取 x 轴坐标位置 ( 在⽗对象之内 ) :

lv_obj_get_x(obj);

lv_obj_get_x2(obj);

获取 y 轴坐标位置 ( 在⽗对象之内 ) :

lv_obj_get_y(obj);

lv_obj_get_y2(obj);

获取 x 轴坐标位置 ( 对⻬之后 ) : lv_obj_get_x_aligned(obj);

获取 y 轴坐标位置 ( 对⻬之后 ) : lv_obj_get_y_aligned(obj);

五:LVGL盒子模型

LVGL 遵循 CSS 的 border-box 模型。

对象的 “ 盒⼦ ” 由以下部分构成:

边界 (bounding) :元素的宽度 / ⾼度围起来的区域 ( 整个盒⼦ ) 。

轮廓 (outline) :盒⼦之间的距离,确认代之的是轮廓 (outline) 。它是绘制于元素 ( 盒⼦ ) 周围的⼀条线,它不 占据空间,位于边框边缘的外围,可起到突出元素 ( 盒⼦ ) 的作⽤。在浏览器⾥,当⿏标点击或使⽤ Tab 键让⼀ 个选项或者⼀个图⽚获得焦点的时候,这个元素就会多了⼀个轮廓框围绕。轮廓 (outline) 。

边框 (border) :边框有⼤⼩和颜⾊等属性 ( 相当于盒⼦的厚度和它的颜⾊ ) 。

填充 (padding) :对象两侧与其⼦对象之间的空间 ( 盒⼦的填充物 ) 。

内容 (content) :如果边界框按边框宽度和填充的⼤⼩缩⼩,则显⽰其⼤⼩的内容区域 ( 盒⼦实际装东西的区 域 ) 。

LVGL 的盒⼦模型是我们理解对象 ( 部件 ) 的组成,修改对象的样式,实现对对象的布局、处理对象排列等等的关键。

  • 当你设置对象的宽⾼(如 lv_obj_set_size(obj, 100, 50))时,这个值包含了边框和填充,⽽不仅是内容区域。
  • 内容区域的实际⼤⼩ = 边界宽⾼ - 边框宽度 ×2 - 填充 ×2。

假设⼀个对象设置为:

  • 宽⾼:100px × 50px(边界⼤⼩)
  • 边框宽度:2px(左右 / 上下各 2px)
  • 填充:左右各 10px,上下各 5px
  • 内容区域宽度 = 100px - 2px×2(边框) - 10px×2(填充) = 76px
  • 内容区域⾼度 = 50px - 2px×2(边框) - 5px×2(填充) = 36px

六:对象的样式( Styles )

在 lvgl 中 styles ⽤于设置对象的外观:

样式是⼀个 lv_style_t 变量,它可以保存边框宽度、⽂本颜⾊等属性。

将样式 ( 变量 ) 分配给对象就可以改变其外观。在赋值过程中,可以指定⽬标部分和⽬标状态。

⼀个样式可以给多个对象使⽤(正常样式)。

样式可以级联,也就是可以将多个样式分配给⼀个对象。所以,我们不⽤将所有属性都在⼀个样式中指定, 可以通过多个样式组合的形式指定。

LVGL 会优先使⽤我们定义的样式,如果没有就会使⽤默认值。 后来添加的样式具有更⾼的优先级。也就是说如果在两种样式中指定了同⼀个属性,则将使⽤最后添加的样 式。

如果对象中未指定某些属性(例如⽂本颜⾊),就会从⽗级继承。

上⾯说的是 “ 正常 ” 样式,对象还有本地样式,它⽐ “ 正常 ” 样式具有更⾼的优先级。 可以定义有过渡效果的样式。

默认有⼀个样式主题,我们也可以⾃⼰定义样式主题,作为默认的样式主题使⽤。

1. 样式的使⽤

初始化样式样式存储在 lv_style_t 变量中。

样式变量应该是 静态 、全局或动态分配 的。 也就是它们不能是函数中的局部变量,因为当函数结束时它们会被销毁。样式初始化⽰例:

static lv_style_t style_obj;

lv_style_init(&style_obj);

2.设置样式属性

初始化后,就可以设置属性

lv_style_set_<property_name>(&style, <value>);

  1. lv_style_set_<property_name>:函数名的前缀固定为 lv_style_set_,后面接具体的样式属性名(如 bg_color 表示背景色,border_width 表示边框宽度)。
  2. &style:指向 lv_style_t 类型变量的指针,即要修改的样式对象。
  3. <value>:该样式属性的具体值(根据属性类型不同而变化,如颜色值、数值、枚举等)。

示例:

lv_style_set_bg_color(&style_obj, lv_color_hex(0x000000));      // 设置背景色lv_style_set_text_color(&style_obj, lv_color_hex(0xc43e1c));    // 设置文字颜色

3. 添加(应用)样式到对象

lv_obj_add_style(obj, &style,<selector> )

  1. obj:要应用样式的目标对象(如按钮、容器等)。
  2. &style:指向已初始化的 lv_style_t 样式变量的指针,即要应用的样式。
  3. <selector>:样式选择器,用于指定样式生效的对象状态部件,常用值包括:
    • LV_STATE_DEFAULT:默认状态(未被点击、未选中时)
    • LV_STATE_PRESSED:被按下时的状态
    • LV_STATE_CHECKED:被选中时的状态(如复选框勾选后)
    • LV_STATE_FOCUSED:获得焦点时的状态(如通过键盘选中)
    • LV_PART_MAIN:对象的主要部分(默认值,可省略)
    • 其他部件(如 LV_PART_INDICATOR 用于滑块的指示器部分)

示例:

lv_obj_add_style(obj, &style_obj, 0);                   // 默认状态: LV_STATE_DEFAULT

lv_obj_add_style(obj, &style_obj, LV_STATE_PRESSED);  // 按下状态,当对象被按下的时候应用该样式

4. 获取样式属性

我们可以获取属性的最终值(考虑级联、继承、本地样式和转换),接⼝函数是这样的格式: lv_obj_get_style_<property_name>(obj, <part>);

  1. lv_obj_get_style_<property_name>:函数名前缀固定为 lv_obj_get_style_,后面接具体的样式属性名(如 bg_color 表示背景色,border_width 表示边框宽度)。
  2. obj:要查询的目标对象(如按钮、标签等)。
  3. <part>:指定对象的部件(如 LV_PART_MAIN 表示主部件),复杂对象可能包含多个部件(如滑块的轨道、指示器等)。
  4. 返回值:该样式属性的当前生效值(类型与属性对应,如颜色值、数值等)

函数使⽤对象的当前状态,如果没有更好的候选对象,则返回默认值。 

示例:

lv_color_t color = lv_obj_get_style_bg_color(obj, LV_PART_MAIN);//获取对象主部件当前生效的背景色

5. 删除样式

删除对象的所有样式: lv_obj_remove_style_all(obj);

删除对象的特定样式: lv_obj_remove_style(obj, &style_obj, selector);

6. 本地样式

除了 “ 普通 ” 样式外,对象还可以存储 本地样式 ( 私有样式 ) 。

本地样式与普通样式类似,但是它不能在其他对象之间共享。如果使⽤本地样式,将⾃动分配局部样式,并在删除对象时释放。

本地样式对于向对象添加本地⾃定义很有⽤。 本地样式的接⼝函数是这样的格式: 

lv_obj_set_style_<property_name>(lv_obj_t *obj, <value>, lv_part_t part);

示例:

lv_obj_set_style_bg_color(parent, lv_color_hex(0xc43e1c), 0);   // 通过本地样式(私有样式)设置背景色

删除本地样式的时候我们只删除某⼀个样式:

lv_obj_remove_local_style_prop(obj, LV_STYLE_..., selector);

示例:

lv_obj_remove_local_style_prop(parent, LV_STYLE_BG_COLOR, 0);   // 删除通过本地样式(私有样式)设置的背景色

七:LVGL 对象的事件

什么是事件?

当发⽣⽤⼾可能感兴趣的事情时, LVGL 中会触发事件并作出相应的处理(反馈),例如当⼀个对象:

被点击 ,滚动 ,数值改变 ,重绘 ,等等。

1. 使用事件

1). 添加事件: lv_obj_add_event_cb(obj, event_cb, event_code, user_data);

2). 发送事件 lv_obj_send_event(obj, event_code, param);

3). 删除事件 lv_obj_remove_event_cb(obj, event_cb);

lv_obj_remove_event_dsc(obj, event_dsc); //event_dsc 是 lv_obj_add_event_cb 返回的指针

示例:

lv_obj_add_event_cb(obj1, my_event_cb, LV_EVENT_ALL, label);//将给obj1添加事件回调函数,所有的事件类型都能触发该回调函数

2.事件类型 (event_code)

输⼊设备事件 (Input device events)

绘图事件 (Drawing events)

其他事件 (Special events)

特殊事件 (Other events)

⾃定义事件 (Custom events)

3.事件回调函数的 lv_event_t 参数

事件回调函数只有⼀个参数,函数原型如下: static void my_event_cb(lv_event_t * e);

这个参数包含了很多信息,其中我们常⽤的有 :

获取触发的事件代码: lv_event_code_t code = lv_event_get_code(e);

获取触发事件的对象: lv_obj_t * target = lv_event_get_target(e);

获取最初触发事件的对象 ( 事件冒泡 ) : lv_obj_t * target = lv_event_get_current_target(e);

获取事件传递的⽤⼾数据:

lv_event_get_user_data(e); 获取使⽤ lv_obj_add_event_cb 函数传递的⽤⼾数据 lv_event_get_param(e); 获取使⽤ lv_obj_send_event 函数传递的⽤⼾数据

4.事件与对象之间的关系

⼀个事件回调函数可给多个对象使⽤。

我们创建了⼀个事件处理函数之后是可以给不同的对象使⽤的。

⼀个对象可以使⽤多个事件回调函数

我们创建的对象可以绑定多个事件,⽐如⼀个事件是处理点击类型的事件,⼀个事件处理按下类型的 事件等等。

如果传⼊的⽤⼾数据不⼀样,⼀个对象可以绑定同⼀个事件回调函数多次,事件将按照添加的顺序调⽤。例如:

lv_obj_add_event_cb(obj, my_clicked_event_cb, LV_EVENT_CLICKED, &num1); lv_obj_add_event_cb(obj, my_clicked_event_cb, LV_EVENT_CLICKED, &num2);

5.事件冒泡

如果对象启⽤了 lv_obj_add_flag(obj, LV_OBJ_FLAG_EVENT_BUBBLE) ,该对象的所有事件将会发送到该对象的⽗级。如果⽗级也启⽤了 LV_OBJ_FLAG_EVENT_BUBBLE ,那么事件继续发送到他的⽗级,依此类推。

lv_event_get_target(e); 获取触发事件的当前对象。

lv_event_get_current_target(e); 获取事件冒泡的⽗对象

八:LVGL 定时器( lv_timer )

LVGL 的定时器就是会按照指定周期(单位:毫秒 ms )执⾏的函数。

LVGL 有⼀个内置的计时器系统。我们可以注册⼀个函数,让它定期被调⽤,这个函数我们可以称之为定时器处理任务。

这些定时器任务在 lv_task_handler() 中进⾏处理和调⽤,需要每隔 x 毫秒调⽤⼀次。

定时器是⾮抢占式的,这也就是说⼀个定时器不能中断另⼀个定时器。

因此,我们可以在定时器回调函数中调⽤任 何与 LVGL 相关或⽆关的函数。

1. 创建定时器:

lv_timer_t * timer = lv_timer_create(timer_cb, period_ms, user_data);

可以通过 lv_timer_create 返回的定时器句柄 ”timer“ 变量,修改定时器的参数。

timer_cb :定时器回调函数原型: void (*lv_timer_cb_t)(lv_timer_t *)

period_ms :定时器执⾏周期,以毫秒 (ms) 为单位

user_data :⽤⼾数据, void * 类型的指针

示例:

 //定时器每 100 毫秒触发一次,定时器触发时会调用回调函数 my_timer1。

 timer1 = lv_timer_create(my_timer1, 100, &user_data);        

static void my_timer1(lv_timer_t * timer)

{

    int *user_data = lv_timer_get_user_data(timer);

    uint32_t idle = lv_timer_get_idle();

    LV_LOG_USER("my_timer1 user_data: %d, idle: %d", *user_data, idle);

}

2. 准备与重置定时器

lv_timer_ready(timer); //使计时器在下次调用lv_timer_handler()时运行。

lv_timer_reset(timer);//重置计时器的周期。在定义的毫秒周期过去后,它将再次被调用

示例:

先看默认情况(不调用这两个函数):

定时器创建后,会按照固定周期执行:

  • 0ms:创建定时器,开始计时
  • 1000ms:触发 timer_cb(第一次执行)
  • 2000ms:触发 timer_cb(第二次执行)
  • 3000ms:触发 timer_cb(第三次执行)
  • ... 以此类推,每 1 秒执行一次

1. lv_timer_ready(timer):立即执行一次回调,不打乱原有周期

作用:不管当前计时到了多少,立刻调用立即执行一次回调,之后仍然按原周期继续计时。

示例时间线:
  • 0ms:创建定时器,开始计时(原本应在 1000ms 第一次执行)
  • 500ms:调用 lv_timer_ready(timer) → 立即执行 timer_cb(提前执行)
  • 1000ms:按照原周期,再次执行 timer_cb(第二次执行)
  • 2000ms:第三次执行
  • ... 后续仍每 1 秒执行一次

2. lv_timer_reset(timer):重置计时,延后下一次执行

作用:不触发回调,但会把 “下一次执行时间” 从现在重新计算,相当于 “推迟” 下一次执行。

示例时间线:
  • 0ms:创建定时器,开始计时(原本应在 1000ms 第一次执行)
  • 800ms:此时距离下一次执行只剩 200ms,调用 lv_timer_reset(timer) → 重置计时
  • 1800ms:(从 800ms 开始再算 1000ms)第一次执行 timer_cb
  • 2800ms:第二次执行
  • ... 后续每 1 秒执行一次

3. 设置定时器的参数

lv_timer_set_cb(timer, new_cb); // 设置新的回调函数

lv_timer_set_period(timer, new_period); // 设置新的时间周期

lv_timer_set_user_data(timer, new_user_data); // 设置新的⽤⼾数据

4. 设置重复次数

使⽤ lv_timer_set_repeat_count(timer, count) 接⼝让指定的定时器指定重复的次数。

当定时器调⽤了定义的次数后,它会⾃动被删除。将计数设置为 -1 表⽰⽆限重复。

5. 启⽤和禁⽤定时器使⽤ lv_timer_enable(en) 来启⽤或禁⽤定时器。

6. 暂停和恢复

lv_timer_pause(timer); // 暂停指定的定时器。

lv_timer_resume(timer); // 恢复指定的定时器。

其他用法:

可以通过 lv_timer_create_basic() 在不指定任何参数的情况下创建⼀个新定时器

可以使⽤ lv_timer_get_idle 函数获取 lv_task_handler() 函数的空闲百分⽐时间。

请注意,它并不测量整个 系统的空闲时间,仅测量 lv_task_handler() 的空闲时间。 如果在操作系统中使⽤ lvgl 定时器调⽤ lv_task_handler() ,这可能会产⽣误导,因为它实际上不能测量操作系统在空闲线程中的消耗时间。

问题:

如何删除一个定时器?

void lv_timer_delete(lv_timer_t * timer)//删除指定定时器

如果创建了⼀个控件,⽐如 label ;还创建了⼀个 timer ,并且将前⾯创建的 label 作为⽤⼾数据传递给这个 timer ,然后在这个 timer 的回调函数中这个 label 进⾏了处理,⽐如刷新显⽰某些数据;那么如何合理、合法 ( 指针 ) 地删除这个 timer ?

关键原则:

  1. 删除顺序:必须先删除定时器,再删除被其引用的控件(如 label)。如果先删控件,定时器回调可能仍会访问已释放的内存(导致崩溃)。
  2. 指针置空:删除后将指针设为 NULL,后续代码可通过判断 NULL 避免操作野指针。
  3. 回调检查:在定时器回调中,先判断用户数据(如 label)是否为 NULL,再执行操作(防止删除过程中仍有回调触发)。

https://lvgl.100ask.net/9.1/overview/timer.html

百问网:学习记录


文章转载自:

http://NpATKd7A.nshhf.cn
http://827kC3O1.nshhf.cn
http://KDXChGtp.nshhf.cn
http://Y6CBwRBT.nshhf.cn
http://hnPzYqG2.nshhf.cn
http://9VXBqV22.nshhf.cn
http://DqJko9Zr.nshhf.cn
http://fqV7xBtL.nshhf.cn
http://513GXCN9.nshhf.cn
http://hr21CYdB.nshhf.cn
http://0IhSKZfc.nshhf.cn
http://n0wXzcrh.nshhf.cn
http://FDqU0awH.nshhf.cn
http://QeoUSNSX.nshhf.cn
http://hMBwgXeM.nshhf.cn
http://qZIsQdh1.nshhf.cn
http://K1IKUYN3.nshhf.cn
http://y2l07hQE.nshhf.cn
http://xFOL1BrS.nshhf.cn
http://8cBn1BIn.nshhf.cn
http://vp06oIjx.nshhf.cn
http://VIPBnCtv.nshhf.cn
http://zZDhE2N0.nshhf.cn
http://MvyQtXrs.nshhf.cn
http://ZZ3YYUpc.nshhf.cn
http://vblttndb.nshhf.cn
http://50I8gbu1.nshhf.cn
http://qcRGJi2v.nshhf.cn
http://GAdtCZ9U.nshhf.cn
http://L0Sa3L8X.nshhf.cn
http://www.dtcms.com/a/378163.html

相关文章:

  • 【LeetCode - 每日1题】将字符串中的元音字母排序
  • 签名、杂凑、MAC、HMAC
  • C++与QT高频面试问题(不定时更新)
  • 数据结构之跳表
  • 记录豆包的系统提示词
  • Docker 从入门到实践:容器化技术核心指南
  • 【Python-Day 43】告别依赖混乱:Python虚拟环境venv入门与实战
  • CF702E Analysis of Pathes in Functional Graph 题解
  • 元宇宙与智慧城市:数字孪生赋能的城市治理新范式
  • es通过分片迁移迁移解决磁盘不均匀问题
  • 深入浅出CRC校验:从数学原理到单周期硬件实现 (2)CRC数学多项式基础
  • 无人设备遥控器之控制指令发送技术篇
  • LinuxC++项目开发日志——高并发内存池(4-central cache框架开发)
  • 解决蓝牙耳机连win11电脑画质依托答辩问题
  • 农业养殖为何离不开温湿度传感器?
  • Android开发 AlarmManager set() 方法与WiFi忘记连接问题分析
  • CKA02-Ingress
  • JavaEE 初阶第二十一期:网络原理,底层框架的“通关密码”(一)
  • TOL-API 基于Token验证文件传输API安全工具
  • 构建一个优雅的待办事项应用:现代JavaScript实践
  • 计算机视觉进阶教学之图像投影(透视)变换
  • 计算机视觉与深度学习 | 基于MATLAB的AI图片识别系统研究
  • 计算机视觉----图像投影(透视)变换(小案例)
  • Docker 学习笔记(七):Docker Swarm 服务管理与 Containerd 实践
  • 3-10〔OSCP ◈ 研记〕❘ WEB应用攻击▸XSS攻击理论基础
  • 微信小程序开发笔记(01_小程序基础与配置文件)
  • ArcGIS JSAPI 高级教程 - ArcGIS Maps SDK for JavaScript - 自定义(GLSL)修改高亮图层样式
  • idea npm install 很慢(nodejs)
  • Elasticsearch 创建索引别名的正确姿势
  • Kite Compositor for Mac v2.1.2 安装教程|DMG文件安装步骤(Mac用户必看)