LVGL 控件之基础对象(lv_obj)
管理其他部件的基本属性;作为背景装饰;辅助布局;界面切换。坐标父对象基于父对象的后代包含样式诸如ClickableScrollable等属性。在面向对象的思想中,基础对象就是LVGL 中所有其他对象都继承自的基类。基础对象的功能可以与其他控件一起使用。例如基础对象可以直接用作一个简单的控件:它只不过是一个矩形。在 HTML 术语中,将其视为 。
目录
一、 概述
1、基本概念
基础对象的作用有四个:
- 管理其他部件的基本属性;
- 作为背景装饰;
- 辅助布局;
- 界面切换。
基础对象 实现了屏幕上控件的基本属性,例如:
- 坐标
- 父对象
- 基于父对象的后代
- 包含样式
- 诸如
Clickable
、Scrollable
等属性。
在面向对象的思想中,基础对象就是 LVGL 中所有其他对象都继承自的基类。
基础对象的功能可以与其他控件一起使用。 例如 lv_obj_set_width(slider, 100)
基础对象可以直接用作一个简单的控件:它只不过是一个矩形。 在 HTML 术语中,将其视为 <div>。
在设计较为复杂的 GUI 界面时,不同功能的模块之间需要清晰地划分区域,此时,我们可以使用基础对象作为背景,对不同的区域进行划分。
父对象可以被看作是其子对象的容器,每个对象只有一个父对象(screen 对象没有父对象),父对象可以有无限数量的子对象,同时父对象的类型是没有限制。父对象和子对象之间具有如下两点特性:
- 一起移动
如果父对象的位置更改,则子对象将随父对象一起移动,因此子对象的坐标位置是以父对象的左上角而言的,而不是以屏幕的左上角。 - 子对象只能在父对象的区域内显示
如果子对象的一部分在父对象的外面,那么子对象的这一部分将不会被显示出来。
2、lv_obj_t
lv_obj_t
定义在目录 lvgl\src\core\lv_obj.h
中:
struct _lv_obj_t {
const lv_obj_class_t * class_p; // 描述每个对象的通用方法
lv_obj_t * parent; // 指向父对象
_lv_obj_spec_attr_t * spec_attr; // 特殊的、很少使用的属性
_lv_obj_style_t * styles; // 样式的描述符(属性和值的集合)
#if LV_OBJ_STYLE_CACHE
uint32_t style_main_prop_is_set;
uint32_t style_other_prop_is_set;
#endif
void * user_data;
#if LV_USE_OBJ_ID
void * id;
#endif
lv_area_t coords; // 表示该对象屏幕的哪个区域
lv_obj_flag_t flags; // 为 uint32_t 类型,用来存放宏标志
lv_state_t state; // 为 uint16_t 类型,用来表示状态
uint16_t layout_inv : 1;
uint16_t readjust_scroll_after_layout : 1;
uint16_t scr_layout_inv : 1;
uint16_t skip_trans : 1;
uint16_t style_cnt : 6;
uint16_t h_layout : 1;
uint16_t w_layout : 1;
uint16_t is_deleting : 1;
};
二、 基础对象的 API 函数
1、Coordinates(坐标)
LVGL 的坐标设置的概念受到 CSS 的启发,LVGL 并不完全实现了 CSS,但实现了一个类似的子集(有的地方进行了微小调整)。简而言之:
- 显式设置的坐标存储在样式中(大小、位置、布局等)
- 支持最小宽度、最大宽度、最小高度、最大高度
- 有像素、百分比和“内容(content)”单位
- x=0;y=0 坐标表示父对象的 左上角 + 左或上内边距 + 边框的宽度
- 宽/高(width/height)表示完整的尺寸,“内容区域”较小,带有填充和边框宽度
- 支持
flexbox
和grid
布局的部分功能(子集)
1.1 单位
- 像素(pixel):简单地说就是一个以像素为单位的位置。整数总是指像素。
例如lv_obj_set_x(btn, 10)
(设置按钮的横(x)坐标为 10 个像素) - 百分比(percentage):对象或其父对象大小的百分比。
lv_pct(value)
将一个值转换为百分比。
例如lv_obj_set_width(btn, lv_pct(50))
(将按钮的宽度设置为父级宽度的 50%) LV_SIZE_CONTENT
:设置对象宽度/高度的特殊值,将会根据子对象所需的大小自动调整自身大小。类似于 CSS 中的 auto。
例如:lv_obj_set_width(btn, LV_SIZE_CONTENT)
(将按钮的宽度设置为自适应内容宽度)
1.2 盒子模型
LVGL 遵循 CSS 的 border-box
模型。一个对象的“盒子”由以下部分构成:
- 边界框:元素的宽度/高度。
- 边框宽度:边框的宽度。
- 填充:对象与其子元素之间的间距。
- 外边距:对象外部的间距(仅由某些布局考虑)
- 内容:内容区域,即边界框减去边框宽度和内边距的大小。
注意:LVGL不会立即重新计算所有坐标变化,这样做是为了提高性能。相反,对象会被标记为"脏(dirty)",在重新绘制屏幕之前,LVGL会检查是否有任何"dirty"对象。如果有,则会刷新它们的位置、大小和布局。
换句话说,如果您需要获取对象的坐标,并且坐标刚刚发生了变化,LVGL需要强制重新计算坐标。要做到这一点,请调用lv_obj_update_layout()
。
大小和位置可能取决于父级或布局。因此,lv_obj_update_layout()
会重新计算 obj 屏幕上所有对象的坐标。
2、Size(大小)
函数 | 含义 |
---|---|
lv_obj_set_width(obj, new_width) | 修改对象的宽度 |
lv_obj_set_height(obj, new_height) | 修改对象的高度 |
lv_obj_set_size(obj, new_width, new_height) | 同时修改对象的宽度和高度 |
3、Position(位置)
函数 | 含义 |
---|---|
lv_obj_set_x(obj, new_x) | 设置相对于父级的 x 轴的位置 |
lv_obj_set_y(obj, new_y) | 设置相对于父级的 y 轴的位置 |
lv_obj_set_pos(obj, new_x, new_y) | 同时设置相对于父级的 x 轴和 y 轴的位置 |
4、Alignment(对齐)
函数 | 含义 |
---|---|
lv_obj_set_align(obj, LV_ALIGN_…) | 将对象参照其父对象对齐 |
调用该函数后,每个 x 和 y 设置都将适用于设置对齐模式。例如,这会将对象从其父对象的中心移动 10(x),20(y)
像素:
lv_obj_set_align(obj, LV_ALIGN_CENTER);
lv_obj_set_pos(obj, 10, 20);
//Or in one function
lv_obj_align(obj, LV_ALIGN_CENTER, 10, 20);
函数 | 含义 |
---|---|
lv_obj_align_to(obj_to_align, obj_reference, LV_ALIGN_…, x, y) | 将一个对象参照另一个对象对齐 |
例如,让图片下方的文本参照图片对齐:
lv_obj_align_to(text, image, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);
所有的对齐方式如下:
5、Parents and children
函数 | 含义 |
---|---|
lv_obj_set_parent(obj, new_parent) | 为对象设置新的父级 |
lv_obj_get_parent(obj) | 获取当前父级 |
函数 | 含义 |
---|---|
lv_obj_get_child(parent, idx) | 获取父母的特定孩子 |
下面是 idx 的一些示例:
- 0 获取创建的第一个子项
- 1 获取创建的第二个子项
- -1 获取最后创建的子项
父级的孩子们可以这样迭代:
uint32_t i;
for(i = 0; i < lv_obj_get_child_count(parent); i++) {
lv_obj_t * child = lv_obj_get_child(parent, i);
/*Do something with child*/
}
函数 | 含义 |
---|---|
lv_obj_get_index(obj) | 返回对象在其父对象中的索引 |
lv_obj_move_foreground(obj) | 对象带到前台 |
lv_obj_move_background(obj) | 将对象移到后台 |
lv_obj_move_to_index(obj, index) | 更改对象在其父对象中的索引 |
lv_obj_swap(obj1, obj2) | 交换两个对象的位置 (列表框中,它可用于对列表框项目进行排序。) |
6、Display and Screens(显示和屏幕)
在 LVGL 中,对象层次结构的最高级别是 display,它代表显示设备(物理显示器或模拟器)的驱动程序。 一个显示器可以有一个或多个与其相关联的屏幕。 每个屏幕都包含图形控件的对象层次结构,代表覆盖整个显示的布局。
函数 | 含义 |
---|---|
lv_screen_load(screen) | 激活屏幕对象 |
lv_screen_active() | 提供指向活动屏幕的指针 |
lv_display_set_default() | 明确指定屏幕 |
lv_obj_get_screen(obj) | 获取对象的屏幕 |
当你创建了一个像 lv_obj_t * screen = lv_obj_create(NULL)
这样的屏幕时,你可以用 lv_screen_load(screen)
激活它。
或者直接使用:lv_screen_load(screen)
。
7、Events(事件)
函数 | 含义 |
---|---|
lv_obj_add_event_cb(obj, event_cb, LV_EVENT_…, user_data) | 为对象设置事件回调 |
lv_event_send(obj, LV_EVENT_…, param) | 手动向对象发送事件 |
更多关于事件的讲解会在后续的内容提到。
8、Styles(样式)
函数 | 含义 |
---|---|
lv_obj_add_style(obj, &new_style, selector) | 向对象添加新样式 |
selector
可以组合使用。 例如 LV_PART_SCROLLBAR | LV_STATE_PRESSED
。
基本对象使用 LV_PART_MAIN
样式属性和带有典型背景样式属性的 LV_PART_SCROLLBAR
。
9、Flags(宏开关)
函数 | 含义 |
---|---|
lv_obj_add/remove_flag(obj, LV_OBJ_FLAG_…) | 为对象添加/移除宏标志 |
lv_obj_set_flag(obj, LV_OBJ_FLAG_…, true/false) | 为对象设置宏 |
LV_OBJ_FLAG_HIDDEN
隐藏对象。 (就像它根本不存在一样)LV_OBJ_FLAG_CLICKABLE
使输入设备可点击对象LV_OBJ_FLAG_CLICK_FOCUSABLE
单击时将焦点状态添加到对象LV_OBJ_FLAG_CHECKABLE
对象被点击时切换选中状态LV_OBJ_FLAG_SCROLLABLE
使对象可滚动LV_OBJ_FLAG_SCROLL_ELASTIC
允许在内部滚动但速度较慢LV_OBJ_FLAG_SCROLL_MOMENTUM
在“抛出”时使对象滚动得更远LV_OBJ_FLAG_SCROLL_ONE
只允许滚动一个可捕捉的子项LV_OBJ_FLAG_SCROLL_CHAIN_HOR
允许将水平滚动传播到父级LV_OBJ_FLAG_SCROLL_CHAIN_VER
允许将垂直滚动传播到父级LV_OBJ_FLAG_SCROLL_CHAIN
简单的包装 (LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER)LV_OBJ_FLAG_SCROLL_ON_FOCUS
自动滚动对象以使其在聚焦时可见LV_OBJ_FLAG_SCROLL_WITH_ARROW
允许使用箭头键滚动聚焦对象LV_OBJ_FLAG_SNAPPABLE
如果在父对象上启用了滚动捕捉,它可以捕捉到这个对象LV_OBJ_FLAG_PRESS_LOCK
保持对象被按下,即使按下从对象上滑动LV_OBJ_FLAG_EVENT_BUBBLE
也将事件传播给父级LV_OBJ_FLAG_GESTURE_BUBBLE
将手势传播给父级LV_OBJ_FLAG_ADV_HITTEST
允许执行更准确的命中(点击)测试。例如考虑圆角。LV_OBJ_FLAG_IGNORE_LAYOUT
使对象可以通过布局定位LV_OBJ_FLAG_FLOATING
当父级滚动时不要滚动对象并忽略布局LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS
启用 LV_EVENT_DRAW_TASK_ADDED 发送事件LV_OBJ_FLAG_OVERFLOW_VISIBLE
不要将子项的内容剪裁到父项的边界LV_OBJ_FLAG_FLEX_IN_NEW_TRACK
在此项目上启动新的弹性轨道LV_OBJ_FLAG_LAYOUT_1
自定义标志,可供布局免费使用LV_OBJ_FLAG_LAYOUT_2
自定义标志,可供布局免费使用LV_OBJ_FLAG_WIDGET_1
自定义标志,组件免费使用LV_OBJ_FLAG_WIDGET_2
自定义标志,组件免费使用LV_OBJ_FLAG_USER_1
自定义标志,用户免费使用LV_OBJ_FLAG_USER_2
自定义标志,用户免费使用LV_OBJ_FLAG_USER_3
自定义标志,用户免费使用LV_OBJ_FLAG_USER_4
自定义标志,用户免费使用
使用实例:
/*Hide on object*/
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
/*Make an object non-clickable*/
lv_obj_remove_flag(obj, LV_OBJ_FLAG_CLICKABLE);
10、Groups(组)
函数 | 含义 |
---|---|
lv_group_add_obj(group, obj) | 将对象添加到组 |
lv_obj_get_group(obj) | 查看对象属于哪个组 |
lv_obj_is_focused(obj) | 返回对象当前是否聚焦在其组上。 如果对象未添加到组中,则将返回 false |
11、通用函数
函数 | 描述 |
---|---|
lv_obj_create () | 创建基础对象(矩形) |
lv_obj_set_user_data() | 设置对象的 user_data 字段 |
lv_obj_has_flag() | 检查是否在对象上设置了指定的标志 |
lv_obj_has_flag_any() | 检查是否在对象上设置了任何标志 |
lv_obj_get_state() | 获取对象的状态 |
lv_obj_has_state() | 检查对象是否处于指定状态 |
lv_obj_get_group() | 获取对象的组 |
lv_obj_get_user_data() | 获取对象的用户数据 |
lv_obj_allocate_spec_attr() | 为对象分配特殊数据(还未分配时) |
lv_obj_check_type() | 检查 obj 的类型 |
lv_obj_has_class() | 检查是否有任何对象具有指定的类 |
lv_obj_get_class() | 获取对象的类 |
lv_obj_is_valid() | 检查是否有任何对象在活动 |
三、实例
这里定义了一个按钮,并简单添加了一些样式,并为它添加了一个点击事件。点击按钮后,其颜色会变成灰色。
static lv_style_t style_btn; // 样式对象
static lv_obj_t *btn;
static void obj_event_cb(lv_event_t *e)
{
lv_style_set_bg_color(&style_btn, lv_palette_main(LV_PALETTE_GREY)); // 将按钮背景颜色变成灰色
lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT); // 将样式对象添加到button
}
void my_gui(void)
{
btn = lv_btn_create(lv_scr_act()); // 创建一个button对象
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0); // 居中
lv_obj_set_height(btn, 30); // 设置button高度
lv_obj_t *label;
label = lv_label_create(btn); // 为button添加一个label子对象
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); // 居中
lv_label_set_text(label, "Hello World"); // 设置label文字
lv_style_init(&style_btn); // 初始化
lv_style_set_radius(&style_btn, 10); // 设置圆角
lv_style_set_border_color(&style_btn, lv_color_white()); // 设置边框颜色
lv_style_set_border_opa(&style_btn, LV_OPA_30); // 设置样式边框过滤颜色
lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT); // 将样式对象添加到button
lv_obj_add_event_cb(btn, obj_event_cb, LV_EVENT_PRESSED, NULL); // 添加点击事件
}
在模拟器上运行为:
点击按钮后:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)