一、Flex布局(弹性布局)
1.1、概述
Flex布局具备以下特点:
- 方向灵活:可以控制子元素沿水平方向(row 默认)或垂直方向(column)排列。
- 自动填充:子元素可以按照比例分配空间、等分布局或者根据需要自动填充剩余空间。
- 对齐:对齐方式可以在水平/垂直起点、终点分别设置,包括居中、拉伸、两端对齐等多种方式。
- 响应式设计:Flex布局能够很好地适应不同尺寸的屏幕和窗口大小变化,让UI组件可以根据容器的尺寸自动调整位置和大小。
- 动态性:子元素的数量和大小可变时,Flex布局能够确保所有元素依然保持合理的布局结构,尤其适用于动态加载内容的场景。
它的灵活性和可以自动调整子元素位置的特点体现了“弹性”。
以下是一个具体的例子:
int main(int argc, char **argv)
{
lv_init();
hal_init();
{
lv_obj_t *cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 300, 320); // 设置容器大小
lv_obj_set_layout(cont, LV_LAYOUT_FLEX); // 设置为Flex布局
lv_obj_center(cont);
// 设置Flex流和对齐方式
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
// 创建多个子项并添加到容器中
for (int i = 0; i < 10; i++)
{
lv_obj_t *item = lv_btn_create(cont);
lv_obj_set_size(item, 50, 80); // 每个按钮大小相同
}
}
while (1)
{
lv_task_handler();
usleep(5 * 1000);
}
return 0;
}
1.2、重要函数
1、void lv_obj_set_flex_flow(lv_obj_t *obj, lv_flex_flow_t flow)
设置Flex布局流动方向和换行规则。
- LV_FLEX_FLOW_ROW:子元素会按照从左到右的顺序排列,并且不会自动换行。当所有子元素都放置在同一行时,如果空间不够,后面的元素将被覆盖或剪裁。
int main(int argc, char **argv)
{
lv_init();
hal_init();
{
lv_obj_t *cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 300, 320); // 设置容器大小
lv_obj_set_layout(cont, LV_LAYOUT_FLEX); // 设置为Flex布局
lv_obj_center(cont);
// 设置Flex流和对齐方式
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
// 创建多个子项并添加到容器中
for (int i = 0; i < 10; i++)
{
lv_obj_t *item = lv_btn_create(cont);
lv_obj_set_size(item, 50, 80); // 每个按钮大小相同
// 创建标签并设置文本为对应的数字
lv_obj_t *label = lv_label_create(item);
char num_text[6]; // 假设足够容纳最大的数字和可能的'\0'
snprintf(num_text, sizeof(num_text), "%d", i+1);
lv_label_set_text(label, num_text);
lv_obj_center(label);
}
}
while (1)
{
lv_task_handler();
usleep(5 * 1000);
}
return 0;
}
- LV_FLEX_FLOW_COLUMN:子元素会按照从上到下的顺序排列,并且不会自动换行。当所有子元素都放置在同一列时,如果空间不够,下方的元素将被覆盖或剪裁。
- LV_FLEX_FLOW_ROW_WRAP:子元素水平方向排列,当一行的空间不足以容纳下一个子元素时,会自动换行至下一行继续放置子元素。
- LV_FLEX_FLOW_ROW_REVERSE:水平方向排列,与 LV_FLEX_FLOW_ROW 相反,子元素从右到左排列,不换行。
- LV_FLEX_FLOW_ROW_WRAP_REVERSE:类似于 LV_FLEX_FLOW_ROW_WRAP,但子元素按从右到左、然后换行至左侧下一行的顺序排列。
- LV_FLEX_FLOW_COLUMN_WRAP:子元素垂直方向排列,当一列空间不足时会自动开始新的一列。
- LV_FLEX_FLOW_COLUMN_REVERSE:子元素垂直方向排列,与 LV_FLEX_FLOW_COLUMN 相反,子元素从下到上排列,不换行。
- LV_FLEX_FLOW_COLUMN_WRAP_REVERSE:类似于 LV_FLEX_FLOW_COLUMN_WRAP ,但子元素先从底部开始向上排列,满后向下换行至下一列顶部继续排列。
主轴和交叉轴
- 主轴:Flex布局中的主要排列方向。
例如,在 LV_FLEX_FLOW_ROW 模式下,主轴为水平方向,即从左到右;而在LV_FLEX_FLOW_COLUMN 模式下,主轴则变为垂直方向,即从上到下。- 交叉轴:交叉轴与主轴垂直,也就是说它是在主轴之外的另一个维度。
对于主轴为水平方向的情况,交叉轴就是垂直方向;对于主轴为垂直方向的情况,交叉轴则变成水平方向。
轨道
每个Flex项目在主轴方向上占据的空间称为一个“主轴轨道”(main axis track),它相当于项目所分配到的一个纵贯主轴的区间。
在交叉轴方向上,如果存在多个项目并排或者层叠,那么每个Flex项目在交叉轴方向上占据的空间称为“交叉轴轨道”(cross axis track)。
2、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)
参数2:主轴上轨道如何分布
参数3:如果存在交叉轴,交叉轴上轨道的分布
参数4:存放子元素的区域是在对象的哪个部分,比如:
其他选项略。
- LV_FLEX_ALIGN_START:元素(或轨道)靠其所在轴的起始位置对齐。在水平方向上,为左对齐;在垂直方向上,则是顶部对齐。
- LV_FLEX_ALIGN_END:元素(或轨道)靠其所在轴的结束位置对齐。在水平方向上,为右对齐;在垂直方向上,则是底部对齐。
- LV_FLEX_ALIGN_CENTER:元素(或轨道)在其所在轴上居中对齐。
- LV_FLEX_ALIGN_SPACE_EVENLY:均匀分配所有元素之间的空间,包括元素之前和之后的空间。每个元素两侧的空间相等,并且两端留有额外的空间。这种对齐方式会让所有元素之间的间距以及元素与容器边缘的间距完全相等。
- LV_FLEX_ALIGN_SPACE_AROUND:此对齐方式也会使得每个元素周围都有相等的空白区域,但是这个“相等”是相对于元素自身而言。也就是说,每个元素的两侧(与其他元素相邻的部分)都得到了相等的额外空间,但这个额外空间是元素本身宽度的一半加上它与相邻元素间隔的一半。所以虽然每个元素周围的总空间是相等的,但看起来更像是每个元素都被等量的多余空间包围,而不是像 LV_FLEX_ALIGN_SPACE_EVENLY 那样整个容器内所有间隙都绝对相等。因此,在
此
模式下,元素自身的中心点会比 LV_FLEX_ALIGN_SPACE_EVENLY 情况下更靠近容器的中心线。 - LV_FLEX_ALIGN_SPACE_BETWEEN:在主轴上仅在元素之间分配空间,而不包括元素与容器边缘之间的空间。也就是说,第一个和最后一个元素分别紧贴容器的开始和结束位置。
来看具体的例子,首先是主轴的对齐方式:
LV_FLEX_ALIGN_START:
lv_obj_t *cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 400, 240); // 设置容器大小
lv_obj_set_layout(cont, LV_LAYOUT_FLEX); // 设置为Flex布局
// 设置Flex流和对齐方式
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
// 创建多个子项并添加到容器中
for (int i = 0; i < 10; i++)
{
lv_obj_t *item = lv_btn_create(cont);
lv_obj_set_size(item, 50, 80); // 每个按钮大小相同
// 创建标签并设置文本为对应的数字
lv_obj_t *label = lv_label_create(item);
char num_text[6]; // 假设足够容纳最大的数字和可能的'\0'
snprintf(num_text, sizeof(num_text), "%d", i+1);
lv_label_set_text(label, num_text);
lv_obj_center(label);
}
上面的例子平方向为主轴方向。设置 LV_FLEX_ALIGN_START 即水平方向使用元素都左对齐。
LV_FLEX_ALIGN_END:
LV_FLEX_ALIGN_CENTER:
LV_FLEX_ALIGN_SPACE_EVENLY:
LV_FLEX_ALIGN_SPACE_AROUND:
LV_FLEX_ALIGN_SPACE_BETWEEN:
再看交叉轴的对齐方式:
LV_FLEX_ALIGN_START:
int main(int argc, char **argv)
{
lv_init();
hal_init();
srand(time(NULL));
{
lv_obj_t *cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 400, 240); // 设置容器大小
lv_obj_set_layout(cont, LV_LAYOUT_FLEX); // 设置为Flex布局
// 设置Flex流和对齐方式
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(cont,LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);
// 创建多个子项并添加到容器中
for (int i = 0; i < 10; i++)
{
lv_obj_t *item = lv_btn_create(cont);
int height = rand() % 51 + 50;
lv_obj_set_size(item, 50, height);//设置随机高度
// 创建标签并设置文本为对应的数字
lv_obj_t *label = lv_label_create(item);
char num_text[6]; // 假设足够容纳最大的数字和可能的'\0'
snprintf(num_text, sizeof(num_text), "%d", i+1);
lv_label_set_text(label, num_text);
lv_obj_center(label);
}
}
while (1)
{
lv_task_handler();
usleep(5 * 1000);
}
return 0;
}
LV_FLEX_ALIGN_END:
LV_FLEX_ALIGN_CENTER:
交叉轴的其他对齐方式没有效果,不知是否不支持的原因。
3、void lv_obj_set_flex_grow(lv_obj_t *obj, uint8_t grow)
设置Flex布局中的对象的伸缩比例,以决定该对象如何在主轴方向上占用额外的自由空间。
参数2表示对象在分配剩余空间时所占的比例。如果多个子对象都设置了非零的伸缩值,那么它们将按照各自设定的比例来瓜分主轴方向上的剩余空间。
lv_obj_t *container = lv_obj_create(lv_scr_act());
lv_obj_set_layout(container, LV_LAYOUT_FLEX); // 设置为Flex布局
lv_obj_set_size(container, 300, 120); // 设置容器尺寸
// 创建并添加第一个按钮(固定大小)
lv_obj_t *btn_fixed = lv_btn_create(container);
lv_obj_set_size(btn_fixed, 80, 80); // 设置固定宽度和高度
lv_obj_set_flex_grow(btn_fixed, 0); // 不伸缩,保持固定大小
lv_obj_t *btn_stretch = lv_btn_create(container);
lv_obj_set_flex_grow(btn_stretch, 1); // 设置伸缩比例为1
btn_stretch = lv_btn_create(container);
lv_obj_set_flex_grow(btn_stretch, 3); // 设置伸缩比例为3
此例有三个按钮,第一个保持固定尺寸,第二、三个控件保持1:3的比例占满容器剩余空间。
二、网格布局
2.1、概述
网格布局将容器分割成多个行和列,并能够灵活地控制其中子对象的位置和大小。
static lv_coord_t col_dsc[] = {100, 100, 100, LV_GRID_TEMPLATE_LAST};
static lv_coord_t row_dsc[] = {50, 50, 50, LV_GRID_TEMPLATE_LAST};
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_grid_dsc_array(cont, col_dsc,row_dsc);
lv_obj_set_size(cont, 500, 300);
lv_obj_center(cont);
for(uint32_t row = 0; row < 3; ++row)
{
for(uint32_t col = 0; col < 3; ++col)
{
lv_obj_t * obj = lv_btn_create(cont);
lv_obj_set_grid_cell(obj,
LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text_fmt(label, "c%d, r%d", col, row);
lv_obj_center(label);
}
}
描述行和列的大小的数组最后一个元素必须是 LV_GRID_TEMPLATE_LAST。
2.2、重要函数
1、void lv_obj_set_grid_align(lv_obj_t *obj, lv_grid_align_t column_align, lv_grid_align_t row_align)
设置对象内子对象在网格布局中的列对齐方式和行对齐方式的函数。
对齐方式:
- LV_GRID_ALIGN_START:将元素放置在其所在单元格的起始位置。在水平方向上,即左对齐;在垂直方向上,即顶部对齐。
- LV_GRID_ALIGN_CENTER:将元素在其所在单元格内居中对齐。
- LV_GRID_ALIGN_END:将元素放置在其所在单元格的结束位置。在水平方向上,即右对齐;在垂直方向上,即底部对齐。
- LV_GRID_ALIGN_STRETCH:将元素拉伸以填充整个单元格。无论元素原始大小如何,都会调整大小以适应单元格的宽高。
- LV_GRID_ALIGN_SPACE_EVENLY:所有元素之间均匀分配额外空间。即除了每个元素之间的间距相等之外,首尾元素与容器边缘的距离也与元素间的距离相同。
- LV_GRID_ALIGN_SPACE_AROUND:在每个元素两侧添加相同大小的空间,使得元素与其左右两边的间距相等。
- LV_GRID_ALIGN_SPACE_BETWEEN:在所有相邻元素之间分配额外空间,但不包括容器边缘的元素。
各种对齐方式举例:
LV_GRID_ALIGN_START:
static lv_coord_t col_dsc[] = {100, 100, 100, LV_GRID_TEMPLATE_LAST};
static lv_coord_t row_dsc[] = {50, 50, 50, LV_GRID_TEMPLATE_LAST};
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_grid_dsc_array(cont, col_dsc,row_dsc);
lv_obj_set_grid_align(cont,LV_GRID_ALIGN_START,LV_GRID_ALIGN_CENTER);
lv_obj_set_size(cont, 500, 300);
lv_obj_center(cont);
for(uint32_t row = 0; row < 3; ++row)
{
for(uint32_t col = 0; col < 3; ++col)
{
lv_obj_t * obj = lv_btn_create(cont);
lv_obj_set_grid_cell(obj,
LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text_fmt(label, "c%d, r%d", col, row);
lv_obj_center(label);
}
}
LV_GRID_ALIGN_CENTER:
LV_GRID_ALIGN_END:
LV_GRID_ALIGN_STRETCH 是用于网格布局中的单元格的:
static lv_coord_t col_dsc[] = {100, 100, 100, LV_GRID_TEMPLATE_LAST};
static lv_coord_t row_dsc[] = {50, 50, 50, LV_GRID_TEMPLATE_LAST};
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_grid_dsc_array(cont, col_dsc,row_dsc);
lv_obj_set_grid_align(cont,LV_GRID_ALIGN_CENTER,LV_GRID_ALIGN_CENTER);
lv_obj_set_size(cont, 500, 300);
lv_obj_center(cont);
for(uint32_t row = 0; row < 3; ++row)
{
for(uint32_t col = 0; col < 3; ++col)
{
lv_obj_t * obj = lv_btn_create(cont);
lv_obj_set_grid_cell(obj,
col == 1 ? LV_GRID_ALIGN_START : LV_GRID_ALIGN_STRETCH, col, 1,
LV_GRID_ALIGN_STRETCH, row, 1);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text_fmt(label, "c%d, r%d", col, row);
lv_obj_center(label);
}
}
LV_GRID_ALIGN_SPACE_EVENLY:
LV_GRID_ALIGN_SPACE_AROUND:
LV_GRID_ALIGN_SPACE_BETWEEN:
2、void lv_obj_set_grid_cell(lv_obj_t *obj, lv_grid_align_t column_align, uint8_t col_pos, uint8_t col_span, lv_grid_align_t row_align, uint8_t row_pos, uint8_t row_span)
设置对象在网格布局中的位置和尺寸。可以精确地控制一个对象占据网格布局中的哪些单元格,并指定对象在其所在单元格内的对齐方式。
参数:
- col_pos:对象所在的起始列索引。例如,如果值为0,则对象将从第一列开始。
- col_span:对象所占据的列数。例如,如果值为2,则对象将会占用从col_pos开始的连续两列。
- row_pos:对象所在的起始行索引。
- row_span:对象所占据的行数。
3、static inline lv_coord_t lv_grid_fr(uint8_t x)
LV_GRID_FR(x)
用于定义网格列或行的宽度/高度为剩余空间的一个比例。// 假设有一个容器宽度为100% lv_coord_t col_dsc[] = { 100, // 第一列宽度固定为100像素 LV_GRID_FR(1), // 第二列占据剩余空间的1/3 LV_GRID_FR(2) // 第三列占据剩余空间的2/3 LV_GRID_TEMPLATE_LAST, // 必须有的占位符表示数组结束 };