弹性布局 (Flex)

概述

弹性布局(简称 Flex)是 CSS Flexbox 的一个子集。

它可以将项目排列为行或列(轨道),处理换行,调整项目和轨道之间的间距,处理扩展以使项目根据最小/最大宽度和高度填充剩余空间。

要将对象设置为弹性容器,请调用 lv_obj_set_layout(obj, LV_LAYOUT_FLEX)

请注意,LVGL 的弹性布局功能需要在 lv_conf.h 中通过 LV_USE_FLEX 全局启用。

术语

  • 轨道:行或列

  • 主方向:行或列,即项目排列的方向

  • 交叉方向:与主方向垂直的方向

  • 换行:如果轨道中没有更多空间,则开始新轨道

  • 扩展:如果在项目上设置了扩展,它将扩展以填充轨道上的剩余空间。 可用空间将根据项目的扩展值按比例分配(值越大,分配的空间越多)

  • 间隙:行和列之间或轨道上的项目之间的空间

简单接口

通过以下函数,可以在任何父对象上设置弹性布局。

弹性流

lv_obj_set_flex_flow(obj, flex_flow)

flex_flow 的可能值包括:

  • LV_FLEX_FLOW_ROW 将子项排列为一行,不换行

  • LV_FLEX_FLOW_COLUMN 将子项排列为一列,不换行

  • LV_FLEX_FLOW_ROW_WRAP 将子项排列为一行,支持换行

  • LV_FLEX_FLOW_COLUMN_WRAP 将子项排列为一列,支持换行

  • LV_FLEX_FLOW_ROW_REVERSE 将子项排列为一行,不换行,但顺序反转

  • LV_FLEX_FLOW_COLUMN_REVERSE 将子项排列为一列,不换行,但顺序反转

  • LV_FLEX_FLOW_ROW_WRAP_REVERSE 将子项排列为一行,支持换行,但顺序反转

  • LV_FLEX_FLOW_COLUMN_WRAP_REVERSE 将子项排列为一列,支持换行,但顺序反转

弹性对齐

使用 lv_obj_set_flex_align(obj, main_place, cross_place, track_cross_place) 管理子项的排列。

  • main_place 决定如何在主轴上分配轨道中的项目。例如,在 LV_FLEX_FLOW_ROW_WRAP 中将项目对齐到右侧。(在 CSS 中称为 justify-content

  • cross_place 决定如何在交叉轴上分配轨道中的项目。例如,如果项目具有不同的高度,将它们对齐到轨道的底部。(在 CSS 中称为 align-items

  • track_cross_place 决定如何分配轨道。(在 CSS 中称为 align-content

可能的值包括:

  • LV_FLEX_ALIGN_START 表示水平左对齐或垂直顶部对齐。(默认值)

  • LV_FLEX_ALIGN_END 表示水平右对齐或垂直底部对齐。

  • LV_FLEX_ALIGN_CENTER 居中对齐。

  • LV_FLEX_ALIGN_SPACE_EVENLY 项目分布均匀,任何两个项目之间的间距(以及与边缘的间距)相等。不适用于 track_cross_place

  • LV_FLEX_ALIGN_SPACE_AROUND 项目在轨道中均匀分布,周围间距相等。 注意,视觉上间距并不相等,因为所有项目两侧的间距相等。 第一个项目与容器边缘之间有一个单位的间距,但与下一个项目之间有两个单位的间距,因为下一个项目也有自己的间距。不适用于 track_cross_place

  • LV_FLEX_ALIGN_SPACE_BETWEEN 项目在轨道中均匀分布:第一个项目在起始线,最后一个项目在结束线。不适用于 track_cross_place

弹性扩展

弹性扩展可用于使一个或多个子项填充轨道上的可用空间。当多个子项具有扩展参数时,可用空间将按扩展值的比例分配。 例如,有 400 像素的剩余空间和 4 个对象具有扩展值:

  • A 的扩展值 = 1

  • B 的扩展值 = 1

  • C 的扩展值 = 2

AB 的大小为 100 像素,C 的大小为 200 像素。

可以使用 lv_obj_set_flex_grow(child, value) 在子项上设置弹性扩展。value 需要大于 1,或者设置为 0 以禁用子项的扩展。

样式接口

所有与弹性相关的值在底层都是样式属性,可以像其他样式属性一样使用。以下是与弹性相关的样式属性:

  • FLEX_FLOW

  • FLEX_MAIN_PLACE

  • FLEX_CROSS_PLACE

  • FLEX_TRACK_PLACE

  • FLEX_GROW

内部填充

要修改弹性布局在对象之间插入的最小空间,可以在弹性容器样式上设置以下属性:

  • pad_row 设置行之间的填充。

  • pad_column 设置列之间的填充。

例如,如果不希望对象之间有任何填充,可以使用:lv_style_set_pad_column(&row_container_style, 0)

其他功能

RTL

如果容器的基本方向设置为 LV_BASE_DIR_RTL,则在 ROW 布局中 LV_FLEX_ALIGN_STARTLV_FLEX_ALIGN_END 的含义将交换。即 START 将表示右对齐。

ROW 布局中的项目,以及 COLUMN 布局中的轨道,将从右到左排列。

新轨道

可以使用 lv_obj_add_flag(child, LV_OBJ_FLAG_FLEX_IN_NEW_TRACK) 强制弹性布局将项目放入新行。

示例

使用弹性盒的简单行和列布局

C code  

 GitHub
#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
 * A simple row and a column layout with flexbox
 */
void lv_example_flex_1(void)
{
    /*Create a container with ROW flex direction*/
    lv_obj_t * cont_row = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont_row, 300, 75);
    lv_obj_align(cont_row, LV_ALIGN_TOP_MID, 0, 5);
    lv_obj_set_flex_flow(cont_row, LV_FLEX_FLOW_ROW);

    /*Create a container with COLUMN flex direction*/
    lv_obj_t * cont_col = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont_col, 200, 150);
    lv_obj_align_to(cont_col, cont_row, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
    lv_obj_set_flex_flow(cont_col, LV_FLEX_FLOW_COLUMN);

    uint32_t i;
    for(i = 0; i < 10; i++) {
        lv_obj_t * obj;
        lv_obj_t * label;

        /*Add items to the row*/
        obj = lv_btn_create(cont_row);
        lv_obj_set_size(obj, 100, LV_PCT(100));

        label = lv_label_create(obj);
        lv_label_set_text_fmt(label, "Item: %u", i);
        lv_obj_center(label);

        /*Add items to the column*/
        obj = lv_btn_create(cont_col);
        lv_obj_set_size(obj, LV_PCT(100), LV_SIZE_CONTENT);

        label = lv_label_create(obj);
        lv_label_set_text_fmt(label, "Item: %"LV_PRIu32, i);
        lv_obj_center(label);
    }
}

#endif

MicroPython code  

 GitHub Simulator
#
# A simple row and a column layout with flexbox
#

# Create a container with ROW flex direction
cont_row = lv.obj(lv.scr_act())
cont_row.set_size(300, 75)
cont_row.align(lv.ALIGN.TOP_MID, 0, 5)
cont_row.set_flex_flow(lv.FLEX_FLOW.ROW)

# Create a container with COLUMN flex direction
cont_col = lv.obj(lv.scr_act())
cont_col.set_size(200, 150)
cont_col.align_to(cont_row, lv.ALIGN.OUT_BOTTOM_MID, 0, 5)
cont_col.set_flex_flow(lv.FLEX_FLOW.COLUMN)

for i in range(10):
    # Add items to the row
    obj = lv.btn(cont_row)
    obj.set_size(100, lv.pct(100))

    label = lv.label(obj)
    label.set_text("Item: {:d}".format(i))
    label.center()

    # Add items to the column
    obj = lv.btn(cont_col)
    obj.set_size(lv.pct(100), lv.SIZE.CONTENT)

    label = lv.label(obj)
    label.set_text("Item: {:d}".format(i))
    label.center()


在行中排列项目,带有换行和均匀间距

C code  

 GitHub
#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
 * Arrange items in rows with wrap and place the items to get even space around them.
 */
void lv_example_flex_2(void)
{
    static lv_style_t style;
    lv_style_init(&style);
    lv_style_set_flex_flow(&style, LV_FLEX_FLOW_ROW_WRAP);
    lv_style_set_flex_main_place(&style, LV_FLEX_ALIGN_SPACE_EVENLY);
    lv_style_set_layout(&style, LV_LAYOUT_FLEX);

    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont, 300, 220);
    lv_obj_center(cont);
    lv_obj_add_style(cont, &style, 0);

    uint32_t i;
    for(i = 0; i < 8; i++) {
        lv_obj_t * obj = lv_obj_create(cont);
        lv_obj_set_size(obj, 70, LV_SIZE_CONTENT);
        lv_obj_add_flag(obj, LV_OBJ_FLAG_CHECKABLE);

        lv_obj_t * label = lv_label_create(obj);
        lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
        lv_obj_center(label);
    }
}

#endif

MicroPython code  

 GitHub Simulator
#
# Arrange items in rows with wrap and place the items to get even space around them.
#
style = lv.style_t()
style.init()
style.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)
style.set_flex_main_place(lv.FLEX_ALIGN.SPACE_EVENLY)
style.set_layout(lv.LAYOUT_FLEX.value)

cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.add_style(style, 0)

for i in range(8):
    obj = lv.obj(cont)
    obj.set_size(70, lv.SIZE.CONTENT)

    label = lv.label(obj)
    label.set_text("{:d}".format(i))
    label.center()


演示弹性增长

C code  

 GitHub
#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
 * Demonstrate flex grow.
 */
void lv_example_flex_3(void)
{
    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont, 300, 220);
    lv_obj_center(cont);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);

    lv_obj_t * obj;
    obj = lv_obj_create(cont);
    lv_obj_set_size(obj, 40, 40);           /*Fix size*/

    obj = lv_obj_create(cont);
    lv_obj_set_height(obj, 40);
    lv_obj_set_flex_grow(obj, 1);           /*1 portion from the free space*/

    obj = lv_obj_create(cont);
    lv_obj_set_height(obj, 40);
    lv_obj_set_flex_grow(obj, 2);           /*2 portion from the free space*/

    obj = lv_obj_create(cont);
    lv_obj_set_size(obj, 40, 40);           /*Fix size. It is flushed to the right by the "grow" items*/
}

#endif

MicroPython code  

 GitHub Simulator
#
# Demonstrate flex grow.
#

cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW)

obj = lv.obj(cont)
obj.set_size(40, 40)           # Fix size

obj = lv.obj(cont)
obj.set_height(40)
obj.set_flex_grow(1)           # 1 portion from the free space

obj = lv.obj(cont)
obj.set_height(40)
obj.set_flex_grow(2)           # 2 portion from the free space

obj = lv.obj(cont)
obj.set_size(40, 40)           # Fix size. It is flushed to the right by the "grow" items


演示弹性增长

C code  

 GitHub
#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
 * Reverse the order of flex items
 */
void lv_example_flex_4(void)
{

    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont, 300, 220);
    lv_obj_center(cont);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN_REVERSE);

    uint32_t i;
    for(i = 0; i < 6; i++) {
        lv_obj_t * obj = lv_obj_create(cont);
        lv_obj_set_size(obj, 100, 50);

        lv_obj_t * label = lv_label_create(obj);
        lv_label_set_text_fmt(label, "Item: %"LV_PRIu32, i);
        lv_obj_center(label);
    }
}

#endif

MicroPython code  

 GitHub Simulator
#
# Reverse the order of flex items
#
cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.COLUMN_REVERSE)

for i  in range(6):
    obj = lv.obj(cont)
    obj.set_size(100, 50)

    label = lv.label(obj)
    label.set_text("Item: " + str(i))
    label.center()


演示列和行间隙样式属性

C code  

 GitHub
#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

static void row_gap_anim(void * obj, int32_t v)
{
    lv_obj_set_style_pad_row(obj, v, 0);
}

static void column_gap_anim(void * obj, int32_t v)
{
    lv_obj_set_style_pad_column(obj, v, 0);
}

/**
 * Demonstrate the effect of column and row gap style properties
 */
void lv_example_flex_5(void)
{
    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_size(cont, 300, 220);
    lv_obj_center(cont);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);

    uint32_t i;
    for(i = 0; i < 9; i++) {
        lv_obj_t * obj = lv_obj_create(cont);
        lv_obj_set_size(obj, 70, LV_SIZE_CONTENT);

        lv_obj_t * label = lv_label_create(obj);
        lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
        lv_obj_center(label);
    }

    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_var(&a, cont);
    lv_anim_set_values(&a, 0, 10);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);

    lv_anim_set_exec_cb(&a, row_gap_anim);
    lv_anim_set_time(&a, 500);
    lv_anim_set_playback_time(&a, 500);
    lv_anim_start(&a);

    lv_anim_set_exec_cb(&a, column_gap_anim);
    lv_anim_set_time(&a, 3000);
    lv_anim_set_playback_time(&a, 3000);
    lv_anim_start(&a);
}

#endif

MicroPython code  

 GitHub Simulator
def row_gap_anim(obj, v):
    obj.set_style_pad_row(v, 0)


def column_gap_anim(obj, v):
    obj.set_style_pad_column(v, 0)

#
# Demonstrate the effect of column and row gap style properties
#

cont = lv.obj(lv.scr_act())
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)

for i in range(9):
    obj = lv.obj(cont)
    obj.set_size(70, lv.SIZE.CONTENT)

    label = lv.label(obj)
    label.set_text(str(i))
    label.center()

a_row = lv.anim_t()
a_row.init()
a_row.set_var(cont)
a_row.set_values(0, 10)
a_row.set_repeat_count(lv.ANIM_REPEAT.INFINITE)

a_row.set_time(500)
a_row.set_playback_time(500)
a_row.set_custom_exec_cb(lambda a,val: row_gap_anim(cont,val))
lv.anim_t.start(a_row)

a_col = lv.anim_t()
a_col.init()
a_col.set_var(cont)
a_col.set_values(0, 10)
a_col.set_repeat_count(lv.ANIM_REPEAT.INFINITE)

a_col.set_time(3000)
a_col.set_playback_time(3000)
a_col.set_custom_exec_cb(lambda a,val: column_gap_anim(cont,val))

lv.anim_t.start(a_col)


RTL 基础方向更改项目顺序

C code  

 GitHub
#include "../../lv_examples.h"
#if LV_USE_FLEX && LV_BUILD_EXAMPLES

/**
 * RTL base direction changes order of the items.
 * Also demonstrate how horizontal scrolling works with RTL.
 */
void lv_example_flex_6(void)
{
    lv_obj_t * cont = lv_obj_create(lv_scr_act());
    lv_obj_set_style_base_dir(cont, LV_BASE_DIR_RTL, 0);
    lv_obj_set_size(cont, 300, 220);
    lv_obj_center(cont);
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);

    uint32_t i;
    for(i = 0; i < 20; i++) {
        lv_obj_t * obj = lv_obj_create(cont);
        lv_obj_set_size(obj, 70, LV_SIZE_CONTENT);

        lv_obj_t * label = lv_label_create(obj);
        lv_label_set_text_fmt(label, "%"LV_PRIu32, i);
        lv_obj_center(label);
    }
}
#endif

MicroPython code  

 GitHub Simulator
#
# RTL base direction changes order of the items.
# Also demonstrate how horizontal scrolling works with RTL.
#

cont = lv.obj(lv.scr_act())
cont.set_style_base_dir(lv.BASE_DIR.RTL,0)
cont.set_size(300, 220)
cont.center()
cont.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP)

for i in range(20):
    obj = lv.obj(cont)
    obj.set_size(70, lv.SIZE.CONTENT)

    label = lv.label(obj)
    label.set_text(str(i))
    label.center()


API

Enums

enum lv_flex_align_t

Values:

enumerator LV_FLEX_ALIGN_START
enumerator LV_FLEX_ALIGN_END
enumerator LV_FLEX_ALIGN_CENTER
enumerator LV_FLEX_ALIGN_SPACE_EVENLY
enumerator LV_FLEX_ALIGN_SPACE_AROUND
enumerator LV_FLEX_ALIGN_SPACE_BETWEEN
enum lv_flex_flow_t

Values:

enumerator LV_FLEX_FLOW_ROW
enumerator LV_FLEX_FLOW_COLUMN
enumerator LV_FLEX_FLOW_ROW_WRAP
enumerator LV_FLEX_FLOW_ROW_REVERSE
enumerator LV_FLEX_FLOW_ROW_WRAP_REVERSE
enumerator LV_FLEX_FLOW_COLUMN_WRAP
enumerator LV_FLEX_FLOW_COLUMN_REVERSE
enumerator LV_FLEX_FLOW_COLUMN_WRAP_REVERSE

Functions

LV_EXPORT_CONST_INT(LV_OBJ_FLAG_FLEX_IN_NEW_TRACK)
void lv_flex_init(void)

Initialize a flex layout the default values

Parameters

flex -- pointer to a flex layout descriptor

void lv_obj_set_flex_flow(lv_obj_t *obj, lv_flex_flow_t flow)

Set hot the item should flow

Parameters
  • flex -- pointer to a flex layout descriptor

  • flow -- an element of lv_flex_flow_t.

void lv_obj_set_flex_align(lv_obj_t *obj, lv_flex_align_t main_place, lv_flex_align_t cross_place, lv_flex_align_t track_cross_place)

Set how to place (where to align) the items and tracks

Parameters
  • flex -- pointer: to a flex layout descriptor

  • main_place -- where to place the items on main axis (in their track). Any value of lv_flex_align_t.

  • cross_place -- where to place the item in their track on the cross axis. LV_FLEX_ALIGN_START/END/CENTER

  • track_place -- where to place the tracks in the cross direction. Any value of lv_flex_align_t.

void lv_obj_set_flex_grow(lv_obj_t *obj, uint8_t grow)

Sets the width or height (on main axis) to grow the object in order fill the free space

Parameters
  • obj -- pointer to an object. The parent must have flex layout else nothing will happen.

  • grow -- a value to set how much free space to take proportionally to other growing items.

void lv_style_set_flex_flow(lv_style_t *style, lv_flex_flow_t value)
void lv_style_set_flex_main_place(lv_style_t *style, lv_flex_align_t value)
void lv_style_set_flex_cross_place(lv_style_t *style, lv_flex_align_t value)
void lv_style_set_flex_track_place(lv_style_t *style, lv_flex_align_t value)
void lv_style_set_flex_grow(lv_style_t *style, uint8_t value)
void lv_obj_set_style_flex_flow(lv_obj_t *obj, lv_flex_flow_t value, lv_style_selector_t selector)
void lv_obj_set_style_flex_main_place(lv_obj_t *obj, lv_flex_align_t value, lv_style_selector_t selector)
void lv_obj_set_style_flex_cross_place(lv_obj_t *obj, lv_flex_align_t value, lv_style_selector_t selector)
void lv_obj_set_style_flex_track_place(lv_obj_t *obj, lv_flex_align_t value, lv_style_selector_t selector)
void lv_obj_set_style_flex_grow(lv_obj_t *obj, uint8_t value, lv_style_selector_t selector)
static inline lv_flex_flow_t lv_obj_get_style_flex_flow(const lv_obj_t *obj, uint32_t part)
static inline lv_flex_align_t lv_obj_get_style_flex_main_place(const lv_obj_t *obj, uint32_t part)
static inline lv_flex_align_t lv_obj_get_style_flex_cross_place(const lv_obj_t *obj, uint32_t part)
static inline lv_flex_align_t lv_obj_get_style_flex_track_place(const lv_obj_t *obj, uint32_t part)
static inline uint8_t lv_obj_get_style_flex_grow(const lv_obj_t *obj, uint32_t part)

Variables

uint16_t LV_LAYOUT_FLEX
lv_style_prop_t LV_STYLE_FLEX_FLOW
lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE
lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE
lv_style_prop_t LV_STYLE_FLEX_TRACK_PLACE
lv_style_prop_t LV_STYLE_FLEX_GROW