(二)LVGL的对象、位置、大小和布局
Widget 是 LVGL 用户界面的基本构建块。
Widget 的例子有:Base Widget(和 Screen)、 Button、Label、 Image、List、 Chart 和 Text Area。
参见 Widgets(控件) 以查看所有类型的 Widget。
所有 Widget 都使用一个 lv_obj_t 指针作为句柄进行引用。 此指针稍后可用于读取或更改 Widget 的属性。
基础 Widget
所有 Widgets 中最基本的是基础 Widget,所有其他 Widgets 都基于它构建。从面向对象的角度来看,可以将基础 Widget 视作所有其他 Widgets 继承的 Widget 类。
基础 Widget 的函数和功能也可以与其他 Widgets 一起使用。例如 lv_obj_set_width(slider, 100)。
基础 Widget 可以直接用作一个简单的 Widget。虽然它只是一个简单的矩形,但它具有与所有 Widgets 共享的大量特性,这些将在下文及后续页面中详细介绍。用 HTML 的术语来说,可以把它看作是一个
所有 Widget 类型共享一些基本属性:位置、尺寸、父级、样式、它发出的事件、标志,如 Clickable, Scollable 等
您可以使用
lv_obj_set_... 和 lv_obj_get_... 函数来设置/获取这些属性。
/* 设置基本 Widget 属性 */
lv_obj_set_size(btn1, 100, 50); /* 设置按钮的尺寸 */
lv_obj_set_pos(btn1, 20, 30); /* 设置按钮的位置 */
Widget 类型还具有特殊的属性。例如,滑块(slider)有最小值和最大值、当前值。
LVGL 的许多其他部分,设置坐标的概念受到 CSS 的启发。LVGL 并没有完整实现 CSS,但实现了一个相似的子集。
简而言之,这意味着:
显式设置的坐标存储在样式中(位置、大小、布局等)
支持最小宽度、最大宽度、最小高度、最大高度
支持像素、百分比和“内容”单位
x=0;y=0 坐标表示父级的左上角加上左/上边距加上边框宽度
宽度/高度表示完整的大小,“内容区域”较小,包含了边距和边框宽度
支持子集的 flexbox 和 grid 布局
Widget的父级
Widget 的父级是在创建 Widget 时设置的——父级被传递给创建函数。
要获取 Widget 的当前父级,使用 lv_obj_get_parent(widget)。
你可以使用 lv_obj_set_parent(widget, new_parent) 将 Widget 移动到一个新的父级。
要获取父级的特定子 Widget,使用 lv_obj_get_child(parent, idx)。 idx 的一些例子如下:
0 获取最先创建的子 Widget
1 获取第二个创建的子 Widget
-1 获取最后创建的子 Widget
你可以像这样迭代一个父级 Widget 的所有子 Widget:
uint32_t i;
for(i = 0; i < lv_obj_get_child_count(parent); i++) {
lv_obj_t * child = lv_obj_get_child(parent, i);
/* 对子 Widget 执行某些操作。*/
}
lv_obj_get_index(widget) 返回 Widget 在其父级中的索引。它相当于在父级中比它早创建的子 Widget 数量。
你可以使用 lv_obj_move_foreground(widget) 和 lv_obj_move_background(widget) 将 Widget 移动到前景或发送到背景。
你可以使用 lv_obj_move_to_index(widget, index) 改变 Widget 在其父级中的索引。
你可以使用 lv_obj_swap(widget1, widget2) 交换两个 Widget 的位置。
要获取 Widget 的 Screen(最高级别的父级),使用 lv_obj_get_screen(widget)。
Parent-child 结构
一个父级 Widget 可以被视为其子 Widget 的容器。每个 Widget 恰好有一个父级 Widget(除了 Screens 之外), 但是一个父级 Widget 可以拥有任意数量的子 Widget。 对于父级的类型没有限制,但有一些通常是作为父级的 Widgets(例如按钮 button)或作为子级的 Widgets(例如标签 label)。
如果父级的位置发生变化,子 Widget 将会随之移动。因此,所有位置都是相对于父级的。
lv_obj_t * parent = lv_obj_create(lv_screen_active()); /* 在当前屏幕上创建一个父级 Widget */
lv_obj_set_size(parent, 100, 80); /* 设置父级的尺寸 */
lv_obj_t * obj1 = lv_obj_create(parent); /* 在先前创建的父级 Widget 上创建一个 Widget */
lv_obj_set_pos(widget1, 10, 10); /* 设置新 Widget 的位置 */
修改父级的位置:
lv_obj_set_pos(parent, 50, 50); /* 移动父级。子级将随之移动。 */
如果子级部分或完全位于其父级之外,则外部的部分将不会被显示。
lv_obj_set_x(widget1, -30); /* 将子级稍微移出父级 */
这种行为可以通过 lv_obj_add_flag(widget, LV_OBJ_FLAG_OVERFLOW_VISIBLE) 来覆盖,它允许子级在父级之外绘制。除此之外,你必须注册以下事件回调(这在之前的版本中是不需要的)。注意: ext_width 应该是子级将被绘制在内的最大绝对宽度。
static void ext_draw_size_event_cb(lv_event_t * e)
{
lv_event_set_ext_draw_size(e, 30); /* 设置 widget 周围 30px 的额外绘制区域 */
}
在 LVGL 中,Widgets 可以在运行时动态创建和删除。这意味着只有当前创建(存在的)Widgets 会消耗 RAM。这允许在一个按钮被点击打开一个屏幕时创建该屏幕,并在加载新屏幕时删除旧屏幕。用户界面可以根据设备的当前环境创建。例如,可以基于当前连接的传感器创建仪表、图表、条形图和滑块。
每个 widget 都有自己的 create 函数,其原型如下:
lv_obj_t * lv_<widget>_create(lv_obj_t * parent, <other parameters if any>);
通常,创建函数仅有一个 parent 参数,用于告知在哪个 Widget 上创建新的 Widget。返回值是指向创建的 Widget 的指针,类型为 lv_obj_t *。所有 Widget 类型都有一个通用的 delete 函数。它会删除 Widget 及其所有子级。
void lv_obj_delete(lv_obj_t * widget);
lv_obj_delete() 将立即删除 Widget。如果由于任何原因不能立即删除 Widget,你可以使用 lv_obj_delete_async(widget),它将在下一次调用 lv_timer_handler() 时执行删除操作。这在你想要在子级的 LV_EVENT_DELETE 处理程序中删除父级 Widget 时非常有用。
你可以通过 lv_obj_clean(widget) 移除 Widget 的所有子级(但不包括 Widget 本身)。
你可以使用 lv_obj_delete_delayed(widget, 1000) 在一段时间后删除 Widget。延迟是以毫秒为单位表示的。
有时你不确定 Widget 是否已被删除,并且需要某种方法来检查它是否仍然“存活”。在 Widget 被删除之前任何时候,你可以使用 cpp:expr:lv_obj_null_on_delete(&widget),以便在 Widget 被删除时将其指针设置为 NULL。
确保指针变量本身保持有效直到 Widget 被删除。以下是一个示例:
void some_timer_callback(lv_timer_t * t)
{
static lv_obj_t * my_label;
if(my_label == NULL) {
my_label = lv_label_create(lv_screen_active());
lv_obj_delete_delayed(my_label, 1000);
lv_obj_null_on_delete(&my_label);
}
else {
lv_obj_set_x(my_label, lv_obj_get_x(my_label) + 1);
}
}
Screens(屏幕)
不要与 Display (lv_display) 混淆,Screens 仅仅是创建时不带父级(即在创建时为 parent 参数传递 NULL)的任何 Widget。因此,它们构成了 Widget 树的“根”。通常情况下,基础 Widget 用于此目的,因为它具备大多数 Screens 所需的所有特性。但是,也可以使用 Image (图象)(lv_image) Widget 来为 Widget 树创建壁纸背景。
所有 Screens:
在创建 Screen 时自动附加到 default_display;
自动占用关联显示的整个区域;
不能被移动,即不能在屏幕上使用诸如 lv_obj_set_pos() 和 lv_obj_set_size() 等函数。
每个 Display (lv_display) 对象可以有多个与之关联的 Screens,但反之则不行。因此关系如下:
Display
|
--- (一个或多个)
/|\
Screen Widgets (Widget 树的根)
|
O (零个或多个)
/|\
Child Widgets
Screens 是这样创建的:
lv_obj_t * scr1 = lv_obj_create(NULL);
可以使用 lv_obj_delete(scr) 删除 Screens,但请确保不要删除 Active Screen。
Active Screen
虽然每个 Display (lv_display) 对象可以有关联的任意数量的 Screen Widgets,但在任何给定时间只考虑其中一个 Screen 是“Active”的。该 Screen 被称为 Display 的“Active Screen”。因此,一次只有一个 Screen 及其子 Widgets 会在一个显示上显示。
当每个 Display (lv_display) 对象被创建时,会与之一起创建一个默认屏幕,并设置为其“Active Screen”。
要获取指向“Active Screen”的指针,请调用 lv_screen_active()。
要将一个 Screen 设置为“Active Screen”,请调用 lv_screen_load() 或 lv_screen_load_anim()。
Loading Screens
要加载新屏幕,请使用:cpp:expr:lv_screen_load(scr1)。这将 scr1设置为 活动屏幕。
可以通过使用 lv_screen_load_anim(scr, transition_type, time, delay, auto_del) 带动画加载新的屏幕。存在以下过渡类型:LV_SCR_LOAD_ANIM_NONE:在 delay 毫秒后立即切换LV_SCR_LOAD_ANIM_OVER_LEFT,LV_SCR_LOAD_ANIM_OVER_RIGHT,LV_SCR_LOAD_ANIM_OVER_TOP 和 LV_SCR_LOAD_ANIM_OVER_BOTTOM:将新屏幕沿给定方向移动到当前屏幕上LV_SCR_LOAD_ANIM_OUT_LEFT,LV_SCR_LOAD_ANIM_OUT_RIGHT,LV_SCR_LOAD_ANIM_OUT_TOP 和 LV_SCR_LOAD_ANIM_OUT_BOTTOM:将旧屏幕沿给定方向移出当前屏幕LV_SCR_LOAD_ANIM_MOVE_LEFT,LV_SCR_LOAD_ANIM_MOVE_RIGHT,LV_SCR_LOAD_ANIM_MOVE_TOP 和 LV_SCR_LOAD_ANIM_MOVE_BOTTOM:将当前和新屏幕都沿给定方向移动LV_SCR_LOAD_ANIM_FADE_IN 和 LV_SCR_LOAD_ANIM_FADE_OUT:新屏幕覆盖旧屏幕或反之渐显
将 auto_del 设置为 true 会在动画结束后自动删除旧的屏幕。
新屏幕将在 delay 时间过后,当动画开始时变为活动状态(由 lv_screen_active() 返回)。在屏幕动画期间,所有输入都将被禁用
Transparent Screens
通常,Screen 的不透明度为 LV_OPA_COVER 以为其子级提供一个实心背景。如果不是这种情况(不透明度 < 100%),则 display 的 bottom_layer 将会可见。如果 bottom layer 的不透明度也非 LV_OPA_COVER,LVGL 将没有实心背景来绘制。
这种配置(透明 Screen)对于创建例如屏幕显示(OSD)菜单非常有用,在这种情况下,视频在一个不同的硬件层播放,而菜单则覆盖在显示器面板的更高层上。
为了正确地在透明屏幕上渲染 UI,display 的颜色格式需要设置为带有 alpha 通道的一种(LV_COLOR_FORMAT_ARGB8888)。
总而言之,要启用适用于类似 OSD 菜单 UI 的透明屏幕和显示:
将屏幕的 bg_opa 设置为透明: lv_obj_set_style_bg_opa(lv_screen_active(), LV_OPA_TRANSP, LV_PART_MAIN)
将 bottom layer 的 bg_opa 设置为透明: lv_obj_set_style_bg_opa(lv_layer_bottom(), LV_OPA_TRANSP, LV_PART_MAIN)
设置带有 alpha 通道的颜色格式。例如: lv_display_set_color_format(disp, LV_COLOR_FORMAT_ARGB8888)
Layers
当创建一个 lv_display_t 对象时,会创建4个 Screens(层)并附着到它上面。
Bottom Layer
Active Screen
Top Layer
System Layer
1、3 和 4 层独立于 Active Screen,如果它们包含任何可见的内容,则无论哪个屏幕是 Active Screen 都会显示。
Widgets的属性
Widgets 是由多个部分构建而成的。例如,Base Widget 使用主部分和滚动条部分,而 Slider 则使用主部分、指示器部分和旋钮部分。这些部分类似于 CSS 中的 伪元素。
LVGL 中存在以下预定义的部分:LV_PART_MAIN:类似背景的矩形LV_PART_SCROLLBAR:滚动条LV_PART_INDICATOR:指示器,例如用于滑块、进度条、开关或复选框的勾选框LV_PART_KNOB:如同一个可以抓取来调整值的手柄LV_PART_SELECTED:指示当前选定的选项或部分LV_PART_ITEMS:如果 Widget 包含多个相似元素时使用(例如表格单元格)LV_PART_CURSOR:标记特定位置,例如文本区域或图表的光标LV_PART_CUSTOM_FIRST:可以从这里添加自定义部分。
部分的主要目的是允许对 Widgets 的“组件”进行样式设置。
States
Widget 可以处于以下状态的组合中:LV_STATE_DEFAULT:正常、释放状态LV_STATE_CHECKED:切换或选中状态LV_STATE_FOCUSED:通过键盘或编码器聚焦,或者通过触摸板/鼠标点击聚焦LV_STATE_FOCUS_KEY:仅通过键盘或编码器聚焦,而不是通过触摸板/鼠标LV_STATE_EDITED:通过编码器编辑LV_STATE_HOVERED:通过鼠标悬停(目前不支持)LV_STATE_PRESSED:被按下LV_STATE_SCROLLED:正在滚动LV_STATE_DISABLED:禁用状态LV_STATE_USER_1:自定义状态LV_STATE_USER_2:自定义状态LV_STATE_USER_3:自定义状态LV_STATE_USER_4:自定义状态
这些状态通常由库根据用户与 Widget 的交互(按压、释放、聚焦等)自动更改。然而,也可以手动更改状态。要设置或清除给定状态(但保留其他状态不变),请使用 lv_obj_add_state(widget, LV_STATE_...) 和 lv_obj_remove_state(widget, LV_STATE_...)。在这两种情况下都可以使用 OR 连接的状态值。例如: lv_obj_add_state(widget, part, LV_STATE_PRESSED | LV_STATE_CHECKED)。
Flags
有一些 Widget 属性可以通过 lv_obj_add_flag(widget, LV_OBJ_FLAG_…) 和 lv_obj_remove_flag(widget, LV_OBJ_FLAG_…) 来启用或禁用。LV_OBJ_FLAG_HIDDEN 使 Widget 隐藏。(就像它完全不存在一样)LV_OBJ_FLAG_CLICKABLE 使 Widget 可以通过输入设备点击LV_OBJ_FLAG_CLICK_FOCUSABLE 点击时为 Widget 添加聚焦状态LV_OBJ_FLAG_CHECKABLE 当 Widget 被点击时切换选中状态LV_OBJ_FLAG_SCROLLABLE 使 Widget 可滚动LV_OBJ_FLAG_SCROLL_ELASTIC 允许内部滚动但速度较慢LV_OBJ_FLAG_SCROLL_MOMENTUM 当“抛出”Widget 时使其滚动更远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 聚焦时自动滚动 Widget 以使其可见LV_OBJ_FLAG_SCROLL_WITH_ARROW 允许使用箭头键滚动聚焦的 WidgetLV_OBJ_FLAG_SNAPPABLE 如果父级启用了滚动停靠,则可以停靠到此 WidgetLV_OBJ_FLAG_PRESS_LOCK 即使按压滑出 Widget 也保持按压状态LV_OBJ_FLAG_EVENT_BUBBLE 向父级传播事件LV_OBJ_FLAG_GESTURE_BUBBLE 向父级传播手势LV_OBJ_FLAG_ADV_HITTEST 允许执行更准确的点击测试。例如,考虑圆角LV_OBJ_FLAG_IGNORE_LAYOUT 使 Widget 不受布局定位影响LV_OBJ_FLAG_FLOATING 当父级滚动时不滚动该 Widget,并忽略布局LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS 启用发送 LV_EVENT_DRAW_TASK_ADDED 事件LV_OBJ_FLAG_OVERFLOW_VISIBLE 不裁剪子元素的内容至父级边界LV_OBJ_FLAG_FLEX_IN_NEW_TRACK 在此项目上开始一个新的 flex 轨道LV_OBJ_FLAG_LAYOUT_1 自定义标志,由布局自由使用LV_OBJ_FLAG_LAYOUT_2 自定义标志,由布局自由使用LV_OBJ_FLAG_WIDGET_1 自定义标志,由 widget 自由使用LV_OBJ_FLAG_WIDGET_2 自定义标志,由 widget 自由使用LV_OBJ_FLAG_USER_1 自定义标志,由用户自由使用LV_OBJ_FLAG_USER_2 自定义标志,由用户自由使用LV_OBJ_FLAG_USER_3 自定义标志,由用户自由使用LV_OBJ_FLAG_USER_4 自定义标志,由用户自由使用
一些例子:
/* 隐藏 Widget */
lv_obj_add_flag(widget, LV_OBJ_FLAG_HIDDEN);
/* 使 Widget 不可点击 */
lv_obj_remove_flag(widget, LV_OBJ_FLAG_CLICKABLE);
Base-Widget EventsLV_EVENT_PRESSED Widget 已被按下。LV_EVENT_PRESSING Widget 正在被按下(在按压期间持续发送)。LV_EVENT_PRESS_LOST Widget 仍然被按下,但滑动的光标/手指离开了 Widget。LV_EVENT_SHORT_CLICKED Widget 被短时间按下,然后释放。如果滚动则不发送。LV_EVENT_SINGLE_CLICKED 在小距离和短时间内第一次短点击时发送。LV_EVENT_DOUBLE_CLICKED 在小距离和短时间内第二次短点击时发送。LV_EVENT_TRIPLE_CLICKED 在小距离和短时间内第三次短点击时发送。LV_EVENT_LONG_PRESSED 对象已被按下至少 long_press_time。如果滚动则不发送。LV_EVENT_LONG_PRESSED_REPEAT 在 long_press_time 后每 long_press_repeat_time 毫秒发送一次。如果滚动则不发送。LV_EVENT_CLICKED 如果没有滚动(不论长按与否),在释放时发送。LV_EVENT_RELEASED 在 Widget 被释放的所有情况下发送。LV_EVENT_SCROLL_BEGIN 滚动开始。事件参数是指向滚动动画的指针。可以修改。LV_EVENT_SCROLL_THROW_BEGIN 当滚动开始时接收。LV_EVENT_SCROLL_END 滚动结束。LV_EVENT_SCROLL 滚动中LV_EVENT_GESTURE 检测到手势。通过 lv_indev_get_gesture_dir(lv_indev_active()); 获取手势。LV_EVENT_KEY 键被发送到 Widget。通过 lv_indev_get_key(lv_indev_active()); 获取键。LV_EVENT_FOCUSED Widget 收到焦点,LV_EVENT_DEFOCUSED Widget 失去了焦点。LV_EVENT_LEAVE Widget 失去了焦点但仍然被选中。LV_EVENT_HIT_TEST 执行高级命中测试。
Positions, sizes, and layouts(位置、大小和布局)
类似于 LVGL 的许多其他部分,设置坐标的概念受到 CSS 的启发。LVGL 并没有完整实现 CSS,但实现了一个相似的子集(有时做了些微调)。
简而言之,这意味着:
显式设置的坐标存储在样式中(位置、大小、布局等)
支持最小宽度、最大宽度、最小高度、最大高度
支持像素、百分比和“内容”单位
x=0;y=0 坐标表示父级的左上角加上左/上边距加上边框宽度
宽度/高度表示完整的大小,“内容区域”较小,包含了边距和边框宽度
支持子集的 flexbox 和 grid 布局
Units(单位)pixel: 像素位置。整数始终表示像素。
例如lv_obj_set_x(btn, 10)percentage: 控件或其父控件大小的百分比
(取决于属性)。lv_pct(value) 将一个值转换为百分比。 例如:lv_obj_set_width(btn, lv_pct(50))LV_SIZE_CONTENT: 设置控件宽度/高度为包含所有子控件的特殊值。类似于CSS中的 auto。
例如lv_obj_set_width(btn, LV_SIZE_CONTENT)
Boxing Model(盒子模型)
LVGL遵循CSS的 border-box 模型。一个对象的“盒子”由以下部分构成:
边界框:元素的宽度/高度。
边框宽度:边框的宽度。
内边距:对象与其子元素之间的间距。
外边距:对象外部的间距(仅由某些布局考虑)
内容:内容区域,即边界框减去边框宽度和内边距的大小。
边界绘制在边界框内。在边界内部,LVGL在放置对象的子对象时会保留一个“内边距边距”。轮廓绘制在边界框之外。
坐标会被延迟计算
LVGL不会立即重新计算所有坐标变化,这样做是为了提高性能。相反,对象会被标记为"脏(dirty)",在重新绘制屏幕之前,LVGL会检查是否有任何"dirty"对象。如果有,则会刷新它们的位置、大小和布局。换句话说,如果您需要获取一个对象的坐标,并且坐标刚刚发生了变化,需要强制LVGL重新计算坐标。要做到这一点,请调用 lv_obj_update_layout()。大小和位置可能取决于父级或布局。因此,lv_obj_update_layout() 会重新计算 obj 屏幕上所有对象的坐标。
删除样式
正如在 coord_using_styles部分中所描述的,坐标也可以通过样式属性进行设置。更准确地说,实际上每个与坐标相关的样式属性都会作为样式属性进行存储。如果你使用:lv_obj_set_x(widget, 20),LVGL会在小部件的本地样式中保存 x=20。
这是一个内部机制,对使用LVGL的过程影响不大。然而,有一种情况需要你注意实现方式。如果小部件的样式被移除:
lv_obj_remove_style_all(widget)
Widgets的位置
lv_obj_set_x(obj, 10); //单独设置...
lv_obj_set_y(obj, 20);
lv_obj_set_pos(obj, 10, 20); //或者使用一个函数
默认情况下,x和y坐标是从父对象的内容区域的左上角开始计算的。例如,如果父对象的每一边都有五个像素的内边距(padding),那么上面的代码会把 obj 放置在(15, 25),因为内容区域在填充之后开始计算。
百分比值是通过父对象的内容(content)区域的大小来计算的。
lv_obj_set_x(btn, lv_pct(10)); //x = 父元素内容区域宽度的10%
Alignment(对齐)
在某些情况下,可以方便地从对象默认的左上角更改其定位原点。如果改变了原点,比如改成右下角,那么(0,0)位置的意思是:与右下角对齐。要改变原点,使用如下代码:
lv_obj_set_align(obj, align);
改变对齐方式并设置新的坐标:
lv_obj_align(obj, align, x, y);
有以下对齐选项可用:LV_ALIGN_TOP_LEFTLV_ALIGN_TOP_MIDLV_ALIGN_TOP_RIGHTLV_ALIGN_BOTTOM_LEFTLV_ALIGN_BOTTOM_MIDLV_ALIGN_BOTTOM_RIGHTLV_ALIGN_LEFT_MIDLV_ALIGN_RIGHT_MIDLV_ALIGN_CENTER
将子对象对齐到其父对象的中心是非常常见的操作,因此存在专门的函数:
lv_obj_center(obj);
//有相同的效果
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
如果父对象的大小改变,则子对象的设置对齐和位置会根据父对象的变化自动调整更新。上述介绍的功能使对象与其父对象对象。然而,也可以将对象与任意参考对象对其。
lv_obj_align_to(obj_to_align, reference_obj, align, x, y);
除了上述的对齐选项外,还可以使用以下选项将对象对齐到参考对象外部:LV_ALIGN_OUT_TOP_LEFTLV_ALIGN_OUT_TOP_MIDLV_ALIGN_OUT_TOP_RIGHTLV_ALIGN_OUT_BOTTOM_LEFTLV_ALIGN_OUT_BOTTOM_MIDLV_ALIGN_OUT_BOTTOM_RIGHTLV_ALIGN_OUT_LEFT_TOPLV_ALIGN_OUT_LEFT_MIDLV_ALIGN_OUT_LEFT_BOTTOMLV_ALIGN_OUT_RIGHT_TOPLV_ALIGN_OUT_RIGHT_MIDLV_ALIGN_OUT_RIGHT_BOTTOM
例如,将标签对齐到按钮上方并使标签水平居中:
lv_obj_align_to(label, btn, LV_ALIGN_OUT_TOP_MID, 0, -10);
请注意,与 lv_obj_align() 不同,lv_obj_align_to() 不能在对象的坐标或参考对象的坐标发生变化时重新对齐对象。
Size(大小)
一个对象的宽度和高度也可以很容易地进行设置:
lv_obj_set_width(obj, 200); //分别设置...
lv_obj_set_height(obj, 100);
lv_obj_set_size(obj, 200, 100); //或者使用一个函数
百分比值是基于父对象的内容区域大小进行计算的。例如,要将对象的高度设置为屏幕高度:
lv_obj_set_height(obj, lv_pct(100));
大小设置支持特殊值:LV_SIZE_CONTENT。这意味着对象在相应方向上的大小将被设置为其子对象恰好所需的大小。请注意,只有右侧和底部的子对象才会被考虑,而顶部和左侧的子对象仍会被裁剪。这种限制使行为更可预测
具有 LV_OBJ_FLAG_HIDDEN 或 LV_OBJ_FLAG_FLOATING 的对象将被 LV_SIZE_CONTENT 计算忽略。
上述函数设置对象边界框的大小,但内容区域的大小也可以设置。这也就是说,对象的边界框会根据内边距的增加而扩大。
lv_obj_set_content_width(obj, 50); //实际宽度:左内边距 + 50 + 右内边距
lv_obj_set_content_height(obj, 30); //实际高度:顶部内边距 + 30 + 底部内边距
可以使用以下函数获取边界框和内容区域的大小:
int32_t w = lv_obj_get_width(obj);
int32_t h = lv_obj_get_height(obj);
int32_t content_w = lv_obj_get_content_width(obj);
int32_t content_h = lv_obj_get_content_height(obj);
Using styles(使用样式)
在内部实现中,位置、大小和对齐属性是样式属性。上述描述的“简单函数”隐藏了与样式相关的代码来简化操作,实质上已经在对象的本地样式中设置位置、大小和对齐等属性。
然而,使用样式来设置坐标具有一些很大的优势:
使得可以很容易的为多个对象同时设置宽度/高度等。比如,使所有滑块的尺寸为 100x10 像素。
还可以在一个位置修改值。
这些数值可以部分地被其他样式覆盖。例如, style_btn 默认将对象的宽度设置为 100x50,但添加 style_full_width 仅覆盖对象的宽度。
对象的位置或大小可以根据状态而有所不同。例如,在 LV_STATE_DEFAULT 状态下宽度为100像素,在 LV_STATE_PRESSED 状态下为120像素。
可以使用样式转换使坐标变化更加平滑。
以下是使用样式设置对象大小的一些示例代码:
static lv_style_t style;
lv_style_init(&style);
lv_style_set_width(&style, 100);
lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_add_style(btn, &style, LV_PART_MAIN);
Translation(位置转换)
现在假设有3个相邻的按钮。它们的位置如上所述。现在你想要在按下按钮时将按钮上移一点。实现这一目标的一种方法是为其按下状态设置一个新的Y坐标:
static lv_style_t style_normal;
lv_style_init(&style_normal);
lv_style_set_y(&style_normal, 100);
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_y(&style_pressed, 80);
lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);
这种方法有效,但不够灵活,因为按下时的坐标是硬编码的。如果按钮不在y=100处, style_pressed 就不会如预期般工作。可以使用平移来解决这个问题:
static lv_style_t style_normal;
lv_style_init(&style_normal);
lv_style_set_y(&style_normal, 100);
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_translate_y(&style_pressed, -20);
lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn2, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn2, &style_pressed, LV_STATE_PRESSED);
lv_obj_add_style(btn3, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn3, &style_pressed, LV_STATE_PRESSED);
平移是相对于对象当前位置进行应用的。
百分比值也可以在平移中使用。百分比是相对于对象的大小(而不是相对于父对象的大小)。例如 lv_pct(50) 将使对象移动其宽度/高度的一半。
平移是在布局计算之后应用。因此,即使对象的位置总是被布局计算,也可以进行平移。
平移实际上移动了对象。这意味着它会使滚动条和 LV_SIZE_CONTENT 大小的对象对位置变化做出反应。
Transformation(大小转换)
与位置类似,对象的大小也可以相对于当前大小进行改变。变换后的宽度和高度会分别加在对象的两侧。这意味着,一个 10 像素的变换宽度会使对象宽 20 像素(两侧各增加 10 像素)。
与位置平移不同,大小转换并不会使对象“真正”变大。换句话说,滚动条、布局和 LV_SIZE_CONTENT 不会对变换后的大小做出反应。因此,大小转换 “只是” 一种视觉效果。
下面的代码会在按钮被按下时放大:
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_transform_width(&style_pressed, 10);
lv_style_set_transform_height(&style_pressed, 10);
lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);
Min and Max size(最小和最大尺寸)
与CSS类似,LVGL也支持 min-width、 max-width、 min-height和 max-height。这些限制了对象的大小,防止其变得比这些值更小/更大。如果通过百分比或 LV_SIZE_CONTENT 来设置大小,
static lv_style_t style_max_height;
lv_style_init(&style_max_height);
lv_style_set_y(&style_max_height, 200);
lv_obj_set_height(obj, lv_pct(100));
lv_obj_add_style(obj, &style_max_height, LV_STATE_DEFAULT); //将高度限制为200像素
也可以使用百分比值,相对于父对象的内容区域的大小。
static lv_style_t style_max_height;
lv_style_init(&style_max_height);
lv_style_set_y(&style_max_height, lv_pct(50));
lv_obj_set_height(obj, lv_pct(100));
lv_obj_add_style(obj, &style_max_height, LV_STATE_DEFAULT); //将高度限制为父容器高度的一半
Layout(布局)
布局可以更新对象子对象的位置和大小。它们可以用于自动排列子对象成一行或一列,或者以更复杂的形式排列
布局设置的位置和大小会覆盖“正常”的x、y、宽度和高度设置
每个布局都有一个相同的函数: lv_obj_set_layout() (obj, <布局名称>) 用于在对象上设置布局
LVGL带有两种非常强大的布局:
Flexbox:将对象排列成行或列,支持换行和扩展项目。
Grid:在二维表中将对象排列成固定位置。
这两种布局都受到了同名的CSS布局的启发。
Flags(标志)
有一些标志可以用于对象,以影响它们与布局的行为:LV_OBJ_FLAG_HIDDEN 隐藏的对象在布局计算中被忽略。LV_OBJ_FLAG_IGNORE_LAYOUT 该对象被布局简单地忽略。它的坐标可以像常规那样设置。LV_OBJ_FLAG_FLOATING 与 LV_OBJ_FLAG_IGNORE_LAYOUT 相同,但具有 LV_OBJ_FLAG_FLOATING 标志的对象将在 LV_SIZE_CONTENT 计算中被忽略。
这些标志可以使用 lv_obj_add_flag(obj, FLAG) 和 lv_obj_remove_flag(obj, FLAG) 添加/移除。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐



所有评论(0)