Positions, sizes, and layouts(位置、大小和布局)

发布于:2025-08-02 ⋅ 阅读:(17) ⋅ 点赞:(0)

Units(单位)
1、pixel:像素位置,整数始终表示像素。

lv_obj_set_x(btn, 10)

2、percentage: 控件或其父控件大小的百分比,lv_pct(value) 将一个值转换为百分比。

lv_obj_set_width(btn,lv_pct(50));

3、LV_SIZE_CONTENT: 设置控件宽度/高度为包含所有子控件的特殊值。

lv_obj_set_width(btn, LV_SIZE_CONTENT);

存在内边距的计算

lv_obj_set_x(obj, 10);       
lv_obj_set_y(obj, 20);

默认情况下,x和y坐标是从父对象的内容区域的左上角开始计算的。例如,如果父对象的每一边都有五个像素的内边距(padding),那么上面的代码会把 obj 放置在(15, 25),因为内容区域在填充之后开始计算。

百分比值是通过父对象的内容(content)区域的大小来计算的。
lv_obj_set_x(btn, lv_pct(10)); //x = 父元素内容区域宽度的10%

Alignment(对齐)

在这里插入图片描述

1、改变对齐方式(对象内部对齐)
lv_obj_set_align(obj, align);
如果父对象的大小改变,则子对象的设置对齐和位置会根据父对象的变化自动调整更新。

2、改变对齐方式(对象外部对齐)
lv_obj_align_to(label, btn, LV_ALIGN_OUT_TOP_MID, 0, -10);
lv_obj_align_to() 不能在对象的坐标或参考对象的坐标发生变化时重新对齐对象.

3.获取边界框的大小:
获取对象的外部宽度(包括边框、阴影等装饰部分的总宽度 )

int32_t w = lv_obj_get_width(obj);
int32_t h = lv_obj_get_height(obj);

4.获取内容区域的大小:
获取对象的内容区域宽度,即去除边框、外边距、内边距(padding ,若有设置)等装饰后,实际可用于显示内容(如文本、图片等)的水平空间宽度 。
int32_t content_w = lv_obj_get_content_width(obj);
int32_t content_h = lv_obj_get_content_height(obj);

Translation(位置转换)
在按下按钮时将按钮上移一点。推荐可以使用平移。

void translation(){
    // 样式初始化(确保只执行一次)
    static lv_style_t style_normal;
    static lv_style_t style_pressed;
    static lv_style_t style_pressed2;
    static bool styles_inited = false;
    if (!styles_inited) {
        lv_style_init(&style_normal);
        lv_style_set_y(&style_normal, 100);  // 正常状态:Y轴偏移100

        lv_style_init(&style_pressed);
        lv_style_set_translate_y(&style_pressed, -20);

        lv_style_init(&style_pressed2);
        lv_style_set_transform_width(&style_pressed2, 10);
        lv_style_set_transform_height(&style_pressed2, 10);

        styles_inited = true;
    }

    // 创建屏幕
    screen = lv_obj_create(NULL);
    lv_scr_load(screen);
    
    //位置转换(平移)
    // 创建 btn1(不设置固定Y坐标,由样式控制)
    btn1 = lv_btn_create(screen);
    lv_obj_set_height(btn1, 100);
    lv_obj_set_width(btn1, 150);
    lv_obj_set_x(btn1, 100);  // 只设置X坐标,Y坐标由样式控制
    lv_obj_set_style_bg_color(btn1, lv_color_hex(0xFFEEFF), LV_PART_MAIN);
    // 应用样式(关键:正常状态和按下状态的Y偏移)
    lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
    lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);

    //大小转换
    btn2 = lv_btn_create(screen);
    lv_obj_set_width(btn2, 100);
    lv_obj_set_height(btn2, 100);
    lv_obj_set_x(btn2, 300);
    lv_obj_set_y(btn2, 100);  
    lv_obj_set_style_bg_color(btn2, lv_color_hex(0x808000), LV_PART_MAIN);
    lv_obj_add_style(btn2,&style_pressed2,LV_STATE_PRESSED);
}

Transformation(大小转换)
1.变换后的宽度和高度会分别加在对象的两侧。这意味着,一个 10 像素的变换宽度会使对象宽 20 像素(两侧各增加 10 像素)。
2.与位置平移不同,大小转换并不会使对象“真正”变大。换句话说,滚动条、布局和 LV_SIZE_CONTENT 不会对变换后的大小做出反应。因此,大小转换 “只是” 一种视觉效果。

void translation(){
    // 样式初始化(确保只执行一次)
    static lv_style_t style_normal;
    static lv_style_t style_pressed;
    static lv_style_t style_pressed2;
    static bool styles_inited = false;
    if (!styles_inited) {
        lv_style_init(&style_normal);
        lv_style_set_y(&style_normal, 100);  // 正常状态:Y轴偏移100

        lv_style_init(&style_pressed);
        lv_style_set_translate_y(&style_pressed, -20);

        lv_style_init(&style_pressed2);
        lv_style_set_transform_width(&style_pressed2, 10);
        lv_style_set_transform_height(&style_pressed2, 10);

        styles_inited = true;
    }

    // 创建屏幕
    screen = lv_obj_create(NULL);
    lv_scr_load(screen);
    
    //位置转换(平移)
    // 创建 btn1(不设置固定Y坐标,由样式控制)
    btn1 = lv_btn_create(screen);
    lv_obj_set_height(btn1, 100);
    lv_obj_set_width(btn1, 150);
    lv_obj_set_x(btn1, 100);  // 只设置X坐标,Y坐标由样式控制
    lv_obj_set_style_bg_color(btn1, lv_color_hex(0xFFEEFF), LV_PART_MAIN);
    // 应用样式(关键:正常状态和按下状态的Y偏移)
    lv_obj_add_style(btn1, &style_normal, LV_STATE_DEFAULT);
    lv_obj_add_style(btn1, &style_pressed, LV_STATE_PRESSED);

    //大小转换
    btn2 = lv_btn_create(screen);
    lv_obj_set_width(btn2, 100);
    lv_obj_set_height(btn2, 100);
    lv_obj_set_x(btn2, 300);
    lv_obj_set_y(btn2, 100);  
    lv_obj_set_style_bg_color(btn2, lv_color_hex(0x808000), LV_PART_MAIN);
    lv_obj_add_style(btn2,&style_pressed2,LV_STATE_PRESSED);
}

Min and Max size(最小和最大尺寸)
1、限制了对象的大小,防止其变得比这些值更小/更大。

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像素

2、使用百分比值,相对于父对象的内容区域的大小。

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(布局)
1、布局可以更新对象子对象的位置和大小。它们可以用于自动排列子对象成一行或一列,或者以更复杂的形式排列。
2、布局设置的位置和大小会覆盖“正常”的x、y、宽度和高度设置。
3、每个布局都有一个相同的函数: lv_obj_set_layout(obj, <布局名称>) 用于在对象上设置布局。

内置布局
LVGL带有两种非常强大的布局:
1、Flexbox:将对象排列成行或列,支持换行和扩展项目。
2、Grid:在二维表中将对象排列成固定位置。

Flags(标志)
有一些标志可以用于对象,以影响它们与布局的行为:

LV_OBJ_FLAG_HIDDEN 隐藏的对象在布局计算中被忽略。

LV_OBJ_FLAG_IGNORE_LAYOUT 该对象被布局简单地忽略。它的坐标可以像常规那样设置。

LV_OBJ_FLAG_FLOATINGLV_OBJ_FLAG_IGNORE_LAYOUT 相同,但具有 LV_OBJ_FLAG_FLOATING 标志的对象将在 LV_SIZE_CONTENT 计算中被忽略。

这些标志可以使用 lv_obj_add_flag(obj, FLAG)lv_obj_remove_flag(obj, FLAG)添加/移除。
移除某个标志:lv_obj_clear_flag(obj,FLAG);

Order of Creation(图层顺序)
1、默认情况下,LVGL会将新的Widgets绘制在旧的Widgets之上。
例如,假设我们在名为button1的父Widget上添加一个按钮,然后再添加另一个名为button2的按钮。那么,button1(及其子Widget)将处于背景中,并可能被button2及其子Widget覆盖。
在这里插入图片描述

void OrderOfCreation(){
    /* 创建一个屏幕 */
    screen = lv_obj_create(NULL);
    lv_scr_load(screen);                          /* 加载屏幕 */

    /* 创建两个按钮 */
    lv_obj_t * btn1 = lv_btn_create(screen);      /* 在屏幕上创建一个按钮 */
    lv_obj_set_size(btn1, 100, 60);             
    lv_obj_set_pos(btn1, 80, 100);                /* 设置按钮的位置 */


    lv_obj_t * btn2 = lv_btn_create(screen);     
    lv_obj_set_size(btn2, 100, 60);              
    lv_obj_set_pos(btn2, 150, 100);                    

    /* 为按钮添加标签 */
    lv_obj_t * label1 = lv_label_create(btn1);   /* 在按钮1上创建标签 */
    lv_label_set_text(label1, "Button 1");       /* 设置标签文本 */

    lv_obj_t *label2 = lv_label_create(btn2);    /* 在按钮2上创建标签 */
    lv_label_set_text(label2, "Button 2");       /* 设置标签文本 */
}

Styles(风格样式)

1、样式是一个 lv_style_t 变量,它可以保存边框宽度、文本颜色等属性。它类似于 CSS 中的“类”。

2、可以将样式分配给对象以更改其外观。在赋值过程中,可以指定目标部分(CSS 中的pseudo element)和目标状态(pseudo class)。例如,当滑块处于按下状态时,可以将“style_blue”添加到滑块的旋钮。

3、任何数量的对象都可以使用相同的样式。

4、样式可以级联,这意味着可以将多个样式分配给一个对象,并且每个样式可以具有不同的属性。因此,并非所有属性都必须在样式中指定。 LVLG 将寻找一个属性,直到一个样式定义它,或者如果它没有被任何样式指定,则使用默认值。例如,style_btn 可以导致默认的灰色按钮,而style_btn_red 只能添加一个background-color=red 来覆盖背景颜色。

5、后来添加的样式具有更高的优先级。这意味着如果在两种样式中指定了一个属性,则将使用稍后添加的样式。

6、如果对象中未指定某些属性(例如文本颜色),则可以从父级继承。

7、对象可以具有比“正常”样式具有更高优先级的本地样式。具体解释如下:

  • “正常” 样式:通常指全局样式(通过 lv_style_create 创建,可被多个对象共享),例如为所有按钮设置统一的默认背景色。
  • 本地样式:直接绑定到单个对象的样式(通过 lv_obj_add_style给特定对象添加),仅对该对象生效。
  • 优先级规则:本地样式的优先级 高于 全局样式。当两者设置了相同属性(如背景色)时,会优先使用本地样式的配置。

8、当对象改变状态时可以应用转换。具体解释如下:
状态转换:指对象从一个状态切换到另一个状态时(如从默认状态 → 按下状态),样式变化可以通过动画过渡实现,而不是瞬间切换。

Status(状态)
对象可以处于以下状态的组合:

LV_STATE_DEFAULT (0x0000) 正常,释放状态

LV_STATE_CHECKED (0x0001) 切换或检查状态

LV_STATE_FOCUSED (0x0002) 通过键盘或编码器聚焦或通过触摸板/鼠标点击

LV_STATE_FOCUS_KEY (0x0004) 通过键盘或编码器聚焦,但不通过触摸板/鼠标聚焦

LV_STATE_EDITED (0x0008) 由编码器编辑

LV_STATE_HOVERED (0x0010) 鼠标悬停(现在不支持)

LV_STATE_PRESSED (0x0020) 被按下

LV_STATE_SCROLLED (0x0040) 正在滚动

LV_STATE_DISABLED (0x0080) 禁用状态

LV_STATE_USER_1 (0x1000) 自定义状态

该组合表示可以同时聚焦和按下对象。这表示为LV_STATE_FOCUSED | LV_STATE_PRESSED。
样式可以添加到任何状态和状态组合。 例如,为默认和按下状态设置不同的背景颜色。 如果属性未在状态中定义,则将使用 最佳匹配状态的属性 。通常这意味着使用带有 LV_STATE_DEFAULT 的属性。 如果是默认状态但是没有设置他的属性,那么将会使用默认值。
但是 “最佳匹配状态的属性” 到底是什么意思呢? 状态具有优先级,由它们的值显示(参见上面的列表)。更高的值意味着更高的优先级。 为了确定使用哪个状态的属性,让我们举个例子。想象一下,背景颜色是这样定义的:
LV_STATE_DEFAULT:白色
LV_STATE_PRESSED:灰色
LV_STATE_FOCUSED:红色

1.默认情况下,对象处于默认状态,所以这是一个简单的情况:属性在对象当前状态下完美定义为白色。
2. 当对象被按下时有2个相关属性:默认为白色(默认与每个状态相关)和按下为灰色。 按下状态的优先级为 0x0020,高于默认状态的 0x0000 优先级,因此将使用灰色。
3. 当物体聚焦时,发生与按下状态相同的事情,将使用红色。 (焦点状态比默认状态具有更高的优先级)。
4. 当物体聚焦并按下时,灰色和红色都可以工作,但按下状态的优先级高于聚焦状态,因此将使用灰色。
5. 可以为 LV_STATE_PRESSED | LV_STATE_FOCUSED 设置例如玫瑰色。 。 在这种情况下,此组合状态的优先级为 0x0020 + 0x0002 = 0x0022,高于按下状态的优先级,因此将使用玫瑰色。
6. 当对象处于选中状态时,没有设置此状态的背景颜色的属性。因此,由于缺乏更好的选择,对象从默认状态的属性中保持白色。

一些实用的注意事项:
1、状态的优先级(值)非常直观,这是用户自然期望的。例如。如果一个对象被聚焦,用户仍然希望查看它是否被按下,因此按下状态具有更高的优先级。 如果聚焦状态具有更高的优先级,它将覆盖按下的颜色。

效果
仅聚焦时:显示黄色(聚焦样式)。
聚焦 + 按下时:显示红色(按下样式覆盖聚焦,符合用户预期)。

    // 定义样式
    static lv_style_t style_default;    // 默认状态(无特殊状态)
    static lv_style_t style_focused;   // 聚焦状态
    static lv_style_t style_pressed;   // 按下状态

    lv_style_init(&style_default);
    lv_style_set_bg_color(&style_default, lv_color_hex(0xFFFFFF)); // 白色

    lv_style_init(&style_focused);
    lv_style_set_bg_color(&style_focused, lv_color_hex(0xFFFF00)); // 黄色(聚焦)

    lv_style_init(&style_pressed);
    lv_style_set_bg_color(&style_pressed, lv_color_hex(0xFF0000)); // 红色(按下)

    // 创建按钮并绑定状态
    lv_obj_t *btn = lv_btn_create(screen);
    lv_obj_set_size(btn,50,30);
    lv_obj_set_pos(btn,80,50);
    lv_obj_add_style(btn, &style_default, LV_STATE_DEFAULT);
    lv_obj_add_style(btn, &style_focused, LV_STATE_FOCUSED);  // 聚焦状态
    lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);   // 按下状态(优先级更高)

2、如果您想为所有状态设置一个属性(例如红色背景色),只需将其设置为默认状态即可。如果对象找不到当前状态的属性,它将回退到默认状态的属性。
效果
默认状态:白色背景 + 2px 边框。
按下状态:红色背景(自定义) + 2px 边框(继承默认状态,因为按下状态没有定义边框)。

    static lv_style_t style_default;
    static lv_style_t style_pressed;

    lv_style_init(&style_default);
    lv_style_set_bg_color(&style_default, lv_color_hex(0xFFFFFF)); // 白色(默认)
    lv_style_set_border_width(&style_default, 2);                  // 边框宽度2px(所有状态通用)

    lv_style_init(&style_pressed);
    lv_style_set_bg_color(&style_pressed, lv_color_hex(0xFF0000)); // 红色(仅定义背景色)

    lv_obj_t *btn = lv_btn_create(screen);
    lv_obj_set_size(btn,50,30);
    lv_obj_set_pos(btn,80,50);
    lv_obj_add_style(btn, &style_default, LV_STATE_DEFAULT);
    lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);

3、使用 ORed 状态来描述复杂情况的属性。 (例如按下 + 选中 + 聚焦)
效果
仅选中:白色(默认状态,无组合状态触发)。
选中 + 按下:绿色(组合状态样式生效)。

    static lv_style_t style_default;
    static lv_style_t style_checked; // 新增:仅选中状态样式  因为后面要手动设置来模拟选中状态,没有这个样式会显示选中状态默认的样式:红色
    static lv_style_t style_pressed_checked; // 按下 + 选中 组合状态

    lv_style_init(&style_default);
    lv_style_set_bg_color(&style_default, lv_color_hex(0xFFFFFF));

    lv_style_init(&style_checked);
    lv_style_set_bg_color(&style_checked, lv_color_hex(0xFFFFFF)); // 白色(仅选中,与默认状态统一)

    lv_style_init(&style_pressed_checked);
    lv_style_set_bg_color(&style_pressed_checked, lv_color_hex(0x00FF00)); // 绿色(按下且选中)

    lv_obj_t *btn = lv_btn_create(screen);
    lv_obj_set_size(btn,50,30);
    lv_obj_set_pos(btn,80,50);
    lv_obj_add_style(btn, &style_default, LV_STATE_DEFAULT);
    // 绑定仅选中状态(未按下)
    lv_obj_add_style(btn, &style_checked, LV_STATE_CHECKED);
    // 绑定组合状态:按下(LV_STATE_PRESSED)+ 选中(LV_STATE_CHECKED)
    lv_obj_add_style(btn, &style_pressed_checked, LV_STATE_PRESSED | LV_STATE_CHECKED);

    // 手动设置按钮为选中状态(模拟场景)
    lv_obj_add_state(btn, LV_STATE_CHECKED);

4、为不同的状态使用不同的样式元素可能是个好主意。 例如,为released、pressed、checked+pressed、focused、focused+pressed、focused+pressed+checked等状态寻找背景颜色是相当困难的。 相反,例如,对按下和选中状态使用背景颜色,并使用不同的边框颜色指示聚焦状态。

Inheritance(继承)
某些属性(通常与文本相关)可以从父对象的样式继承。 仅当未在对象的样式中设置给定属性时(即使在默认状态下),才应用继承。 在这种情况下,如果该属性是可继承的,则该属性的值也将在父项中搜索,直到一个对象为该属性指定了一个值。父母将使用自己的状态来确定该值。 因此,如果按下按钮,并且文本颜色来自此处,则将使用按下的文本颜色。

Parts
LVGL 中存在以下预定义部分:

LV_PART_MAIN 类似矩形的背景*/

LV_PART_SCROLLBAR 滚动条

LV_PART_INDICATOR 指标,例如用于滑块、条、开关或复选框的勾选框

LV_PART_KNOB 像手柄一样可以抓取调整值*/

LV_PART_SELECTED 表示当前选择的选项或部分

LV_PART_ITEMS 如果小部件具有多个相似元素(例如表格单元格)*/

LV_PART_TICKS 刻度上的刻度,例如对于图表或仪表

LV_PART_CURSOR 标记一个特定的地方,例如文本区域或图表的光标

LV_PART_CUSTOM_FIRST 可以从这里添加自定义部件。

滑条

// 定义三个部分的样式(背景、指标、旋钮)
static lv_style_t style_bg;    // 滑块背景样式
static lv_style_t style_indic; // 滑块指标样式(已选中部分)
static lv_style_t style_knob;  // 滑块旋钮样式

/* 1. 初始化样式 */
// 背景样式(未选中部分)
lv_style_init(&style_bg);
lv_style_set_bg_color(&style_bg, lv_color_hex(0xEEEEEE)); // 浅灰色背景
lv_style_set_bg_opa(&style_bg, LV_OPA_100);              // 不透明
lv_style_set_radius(&style_bg, 10);                       // 圆角10px
lv_style_set_pad_ver(&style_bg, 5);                       // 上下内边距5px(控制高度)

// 指标样式(已选中部分)
lv_style_init(&style_indic);
lv_style_set_bg_color(&style_indic, lv_color_hex(0x007BFF)); // 蓝色指标
lv_style_set_bg_opa(&style_indic, LV_OPA_100);
lv_style_set_radius(&style_indic, 10);                     // 与背景圆角一致
lv_style_set_pad_ver(&style_indic, 5);                     // 与背景高度匹配

// 旋钮样式(拖动的圆形按钮)
lv_style_init(&style_knob);
lv_style_set_bg_color(&style_knob, lv_color_hex(0xFFFFFF)); // 白色旋钮
lv_style_set_border_color(&style_knob, lv_color_hex(0x007BFF)); // 蓝色边框
lv_style_set_border_width(&style_knob, 2);                  // 边框宽度2px
lv_style_set_radius(&style_knob, LV_RADIUS_CIRCLE);         // 圆形旋钮
lv_style_set_width(&style_knob, 20);                     // 旋钮大小20x20px
lv_style_set_height(&style_knob, 20);  

/* 2. 创建滑块对象 */
lv_obj_t *slider = lv_slider_create(lv_scr_act()); // 在当前屏幕创建滑块
lv_obj_set_size(slider, 200, LV_SIZE_CONTENT);    // 宽度200px,高度自适应
lv_obj_center(slider);                             // 居中显示

/* 3. 为滑块的不同部分绑定样式 */
// 背景部分(LV_PART_MAIN 对应滑块整体背景)
lv_obj_add_style(slider, &style_bg, LV_PART_MAIN | LV_STATE_DEFAULT);
// 指标部分(LV_PART_INDICATOR 对应已选中的进度条)
lv_obj_add_style(slider, &style_indic, LV_PART_INDICATOR | LV_STATE_DEFAULT);
// 旋钮部分(LV_PART_KNOB 对应可拖动的按钮)
lv_obj_add_style(slider, &style_knob, LV_PART_KNOB | LV_STATE_DEFAULT);

/* 4. 可选:添加滑块值显示标签 */
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Value: 0");
lv_obj_align_to(label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); // 显示在滑块下方

// 2. 绑定事件时传递回调函数和用户数据
lv_obj_add_event_cb(slider, slider_event_cb, 
                    LV_EVENT_VALUE_CHANGED, label);

网站公告

今日签到

点亮在社区的每一天
去签到