位置、尺寸和布局¶
概述¶
与 LVGL 的许多其他部分类似,设置坐标的概念受到了 CSS 的启发。LVGL 并未完全实现 CSS,而是实现了一个可比较的子集(有时会进行一些调整)。
简而言之,这意味着:
显式设置的坐标存储在样式中(尺寸、位置、布局等)。
支持最小宽度、最大宽度、最小高度、最大高度。
支持像素、百分比和“内容”单位。
x=0;y=0 坐标表示父对象的左上角加上左/上内边距和边框宽度。
宽度/高度表示完整尺寸,“内容区域”因内边距和边框宽度而较小。
支持 Flexbox 和 Grid 布局的一个子集。
单位¶
像素:简单地以像素为单位表示位置。整数始终表示像素。例如:
lv_obj_set_x(btn, 10)。百分比:对象或其父对象尺寸的百分比(取决于属性)。
lv_pct(value)将值转换为百分比。例如:lv_obj_set_width(btn, lv_pct(50))。LV_SIZE_CONTENT:一种特殊值,用于将对象的宽度/高度设置为包含所有子对象。类似于 CSS 中的auto。例如:lv_obj_set_width(btn, LV_SIZE_CONTENT)。
盒模型¶
LVGL 遵循 CSS 的 border-box 模型。 对象的“盒子”由以下部分组成:
边界框:元素的宽度/高度。
边框宽度:边框的宽度。
内边距:对象边缘与其子对象之间的空间。
内容:内容区域是边界框减去边框宽度和内边距后的大小。

边框绘制在边界框内。在边框内,LVGL 在放置对象的子对象时保留了“内边距”。
轮廓绘制在边界框外。
重要说明¶
本节描述了 LVGL 行为可能出乎意料的特殊情况。
延迟坐标计算¶
LVGL 不会立即重新计算所有坐标更改。这是为了提高性能。 相反,对象被标记为“脏”,在重新绘制屏幕之前,LVGL 会检查是否有“脏”对象。如果有,它会刷新它们的位置、大小和布局。
换句话说,如果您需要获取对象的坐标,而坐标刚刚发生了变化,则需要强制 LVGL 重新计算坐标。
为此,请调用 lv_obj_update_layout(obj)。
大小和位置可能取决于父对象或布局。因此,lv_obj_update_layout 会重新计算 obj 所在屏幕上所有对象的坐标。
移除样式¶
如使用样式部分所述,坐标也可以通过样式属性设置。
更准确地说,底层每个样式坐标相关属性都存储为样式属性。如果您使用 lv_obj_set_x(obj, 20),LVGL 会将 x=20 保存到对象的本地样式中。
这是一个内部机制,使用 LVGL 时并不重要。然而,有一种情况需要注意实现。如果通过以下方式移除对象的样式:
lv_obj_remove_style_all(obj)
或
lv_obj_remove_style(obj, NULL, LV_PART_MAIN);
先前设置的坐标也将被移除。
例如:
/*最终 obj1 的大小将恢复为默认值*/
lv_obj_set_size(obj1, 200, 100); /*现在 obj1 的大小为 200;100*/
lv_obj_remove_style_all(obj1); /*移除设置的大小*/
/*最终 obj2 的大小为 200;100 */
lv_obj_remove_style_all(obj2);
lv_obj_set_size(obj2, 200, 100);
位置¶
简单方法¶
要简单地设置对象的 x 和 y 坐标,请使用:
lv_obj_set_x(obj, 10); //分别设置...
lv_obj_set_y(obj, 20);
lv_obj_set_pos(obj, 10, 20); //或在一个函数中设置
默认情况下,x 和 y 坐标是从父对象内容区域的左上角测量的。
例如,如果父对象在每一侧都有五个像素的内边距,则上述代码会将 obj 放置在 (15, 25),因为内容区域从内边距之后开始。
百分比值是根据父对象内容区域的大小计算的。
lv_obj_set_x(btn, lv_pct(10)); //x = 父对象内容区域宽度的 10%
对齐¶
在某些情况下,将定位的原点从默认的左上角更改为其他位置会很方便。如果将原点更改为例如右下角,则 (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() 无法在对象的坐标或参考对象的坐标发生变化时重新对齐对象。
尺寸¶
简单方法¶
对象的宽度和高度也可以轻松设置:
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 + 底部内边距
可以使用以下函数获取边界框和内容区域的大小:
lv_coord_t w = lv_obj_get_width(obj);
lv_coord_t h = lv_obj_get_height(obj);
lv_coord_t content_w = lv_obj_get_content_width(obj);
lv_coord_t content_h = lv_obj_get_content_height(obj);
使用样式¶
在底层,位置、尺寸和对齐属性是样式属性。 上述描述的“简单函数”隐藏了样式相关代码,以简化操作,并在对象的本地样式中设置位置、尺寸和对齐属性。
然而,使用样式设置坐标具有一些很大的优势:
可以轻松地为多个对象一起设置宽度/高度等。例如,使所有滑块的大小为 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_btn_create(lv_scr_act());
lv_obj_add_style(btn, &style, LV_PART_MAIN);
正如您将在下面看到的,尺寸和位置设置还有一些其他很棒的功能。 然而,为了保持 LVGL API 的简洁,仅最常见的坐标设置功能具有“简单”版本,而更复杂的功能可以通过样式使用。
平移¶
假设有 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 大小的对象对位置更改做出反应。
变换¶
与位置类似,对象的大小也可以相对于当前大小进行更改。 变换的宽度和高度会添加到对象的两侧。这意味着 10 像素的变换宽度会使对象宽度增加 2x10 像素。
与位置平移不同,尺寸变换不会使对象“真正”变大。换句话说,滚动条、布局和 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);
最小和最大尺寸¶
与 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); //将高度限制为父对象高度的一半
布局¶
概述¶
布局可以更新对象子对象的位置和大小。它们可用于自动将子对象排列成一行或一列,或以更复杂的形式排列。
布局设置的位置和大小会覆盖“正常”的 x、y、宽度和高度设置。
每种布局只有一个相同的函数:lv_obj_set_layout(obj, <LAYOUT_NAME>) 在对象上设置布局。
有关父对象和子对象的进一步设置,请参阅给定布局的文档。
标志¶
有一些标志可用于对象,以影响它们在布局中的行为:
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/clear_flag(obj, FLAG); 添加/移除这些标志。
添加新布局¶
LVGL 可以通过自定义布局自由扩展,如下所示:
uint32_t MY_LAYOUT;
...
MY_LAYOUT = lv_layout_register(my_layout_update, &user_data);
...
void my_layout_update(lv_obj_t * obj, void * user_data)
{
/*如果需要重新定位/调整“obj”的子对象的大小,将自动调用此函数*/
}
可以添加自定义样式属性,这些属性可以在更新回调中检索和使用。例如:
uint32_t MY_PROP;
...
LV_STYLE_MY_PROP = lv_style_register_prop();
...
static inline void lv_style_set_my_prop(lv_style_t * style, uint32_t value)
{
lv_style_value_t v = {
.num = (int32_t)value
};
lv_style_set_prop(style, LV_STYLE_MY_PROP, v);
}