一、 概述

1、基本概念

基础对象的作用有四个:

  1. 管理其他部件的基本属性;
  2. 作为背景装饰;
  3. 辅助布局;
  4. 界面切换。

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

  • 坐标
  • 父对象
  • 基于父对象的后代
  • 包含样式
  • 诸如 ClickableScrollable 等属性。

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

基础对象的功能可以与其他控件一起使用。 例如 lv_obj_set_width(slider, 100)

基础对象可以直接用作一个简单的控件:它只不过是一个矩形。 在 HTML 术语中,将其视为 <div>。

在这里插入图片描述
在设计较为复杂的 GUI 界面时,不同功能的模块之间需要清晰地划分区域,此时,我们可以使用基础对象作为背景,对不同的区域进行划分。

父对象可以被看作是其子对象的容器,每个对象只有一个父对象(screen 对象没有父对象),父对象可以有无限数量的子对象,同时父对象的类型是没有限制。父对象和子对象之间具有如下两点特性:

  1. 一起移动
    如果父对象的位置更改,则子对象将随父对象一起移动,因此子对象的坐标位置是以父对象的左上角而言的,而不是以屏幕的左上角。
  2. 子对象只能在父对象的区域内显示
    如果子对象的一部分在父对象的外面,那么子对象的这一部分将不会被显示出来。

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)表示完整的尺寸,“内容区域”较小,带有填充和边框宽度
  • 支持 flexboxgrid 布局的部分功能(子集)

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); // 添加点击事件
}

在模拟器上运行为:

点击按钮后:

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐