12306网站开发语言,页面预加载wordpress,做外单的网站,自己建设网站怎么盈利1、简单布局
可以使用 lv_obj_set_pos(obj, x, y) 调整一个控件的位置#xff08;或者使用类似的函数单独调整一个方向的坐标#xff09;#xff0c;将它放在相对父容器左上角的合适位置。不过这种布局方式非常死板#xff0c;因为绝对坐标一旦设定就不能自动调整#xf…1、简单布局
可以使用 lv_obj_set_pos(obj, x, y) 调整一个控件的位置或者使用类似的函数单独调整一个方向的坐标将它放在相对父容器左上角的合适位置。不过这种布局方式非常死板因为绝对坐标一旦设定就不能自动调整而且当控件数量较多时也很难确定合适的坐标值。
上一节介绍过可以使用 lv_obj_align(obj, align, x_ofs, y_ofs) 设置一个控件相对父容器的对齐并用以下图片展示所有的对齐方式 从图片中可以看到控件之间不仅可以内对齐也可以外对齐。如果两个控件间没有包含关系也不要紧可以使用 lv_obj_align_to(obj, base, align, x_ofs, y_ofs); 设置两个控件的相对对齐方式。
这种对齐的方式对于控件不多的情况下来说是足够了但是有些时候需要对很多并列的控件布局例如一个计算机界面的所有按钮。这个时候常规的对齐方式就难以满足需求了。
因此LVGL 提供了两种更复杂的布局方式
flex弹性盒子grid网格
这两种布局和 CSS3 新增的 flex 布局和 grid 布局比较相似如果熟悉 CSS 的话对它们应该不会陌生。
2、flex布局
flex 是一个实验性质的布局首先需要确定已经在 lv_conf.h 大约 588 行的位置启用了 flex 布局
/*A layout similar to Flexbox in CSS.*/
#define LV_USE_FLEX 1后续介绍的 grid 布局也是如此。
2.1、创建flex布局
如果不添加任何布局方式那么所有的控件都会堆放在左上角。flex 布局可以将一些控件按行或列均匀布局并且可以自动调整它们的间距。
可以给一个容器设置一个 flex-flow 属性这样容器就可以使用 flex 布局方式
lv_obj_t* cont lv_obj_create(lv_scr_act());
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW);对于设置了 flex 布局的容器在其中创建的元素都会在一个坐标轴上均匀排布。例如以下使用 for 循环创建多个控件
lv_obj_set_size(cont, 300, 75);
for (uint8_t i 0; i 9; i) {lv_obj_t* btn lv_btn_create(cont);lv_obj_t* label lv_label_create(btn);lv_label_set_text_fmt(label, %d, i 1);
}效果为 尽管没有设置按钮的位置但是每一个按钮都会在水平位置上均匀排布。如果要让排布时不超过父容器的最大宽度可以使用 LV_FLEX_FLOW_ROW_WRAP 折行。
也可以使用按列的方式排布控件。可以通过 lv_flex_flow_t 枚举类型检查更多的 flex 布局形式。
2.2、flex布局的对齐
以上 flex 布局中各控件的尺寸和间距都是固定的并且第一个控件依然会出现在左上角。如果
可以使用
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_place);设置 flex 布局的对齐方式。该函数一次性会设置三个方面的对齐
main_place 设置行或列的对齐cross_place 设置控件在一行或一列内的对齐当控件高度或宽度不一致时就可以看出效果track_place flex-flow 方向上的对齐
如果接触过 CSS 的话可以明白这些对齐方式实际上就是 CSS 里的 justify-content 、align-items 和 align-content 。
例如以下调用
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);创建的每个控件之间在水平方向上均匀对齐、行内上下居中对齐并作为一个整体上下居中对齐效果为 又如以下调用
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_START, LV_FLEX_ALIGN_START);创建的每个控件之间在水平方向上两端对齐、行内顶端对齐并作为一个整体顶端对齐效果为 flex 布局还可以通过
void lv_obj_set_flex_grow(lv_obj_t *obj, uint8_t grow);动态调整各个控件的相对宽度实现更灵活的布局规则。例如以下代码在一个 flex-flow 框架内创建了 4 个按钮并将第二个按钮的相对宽度设置为其它按钮的两倍
for (uint8_t i 0; i 4; i) {lv_obj_t* btn lv_btn_create(cont);lv_obj_t* label lv_label_create(btn);lv_label_set_text_fmt(label, %d, i);if (i 1)lv_obj_set_flex_grow(btn, 2);elselv_obj_set_flex_grow(btn, 1);
}效果为 以下利用相对宽度创建了一个更复杂的类似数字输入键盘的布局规则
lv_obj_t* cont lv_obj_create(lv_scr_act());
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_size(cont, 160, 180);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_BETWEEN, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_set_style_base_dir(cont, LV_BASE_DIR_RTL, 0);
for (int8_t i 9; i 0; i--) {lv_obj_t* btn lv_btn_create(cont);lv_obj_t* label lv_label_create(btn);lv_label_set_text_fmt(label, %d, i);
}
lv_obj_t* btn lv_btn_create(cont);
lv_obj_set_flex_grow(btn, 2);
lv_obj_t* label lv_label_create(btn);
lv_label_set_text(label, OK);效果为 这里使用 lv_obj_set_style_base_dir() 函数设置从右向左的书写方式因此滚动条才会出现在左侧。后续介绍样式时还会介绍更多类似函数。
一般情况下 flex-grow 和带 wrap 的 flex-flow 是冲突的也就是说所有设置了 flex-grow 的控件都会在同一行布局但它们的宽度可能变得很窄。因此以上的各个数字按钮相对宽度并不一致。
使用这种布局创建键盘非常别扭不过好在 LVGL 提供了另一种形式的布局grid 。
3、grid布局
3.1、创建grid布局
grid 布局是一种网格形式的布局可以按行或列来对齐控件。
为了创建网格布局首先要给出格子的长度和宽度。一般来说可以通过两个数组分别描述网格每一行的宽度和每一列的宽度
static lv_coord_t col_size[] { 60, 60, 90, LV_GRID_TEMPLATE_LAST };
static lv_coord_t row_size[] { 40, 40, 30, LV_GRID_TEMPLATE_LAST };每一个数组都需要以 LV_GRID_TEMPLATE_LAST 结尾。然后就可以通过
void lv_obj_set_grid_dsc_array(lv_obj_t *obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[])函数为一个容器设置网格划分。 注意创建的数组一定要声明为 static 或全局变量因为这部分数据在后续渲染时才会被用上。 划分好了网格以后接下来就可以使用以下函数
void lv_obj_set_grid_cell(lv_obj_t * obj, lv_grid_align_t x_align, uint8_t col_pos, uint8_t col_span,lv_grid_align_t y_align, uint8_t row_pos, uint8_t row_span);将每一个控件摆放在合适的网格位置。align 指定每一个放置在网格上的控件相对格线的对齐pos 指定控件放置在哪个格子里最左上角的格子位置为 (0, 0) 有的控件可能占据不止一个格子的位置那么就需要使用 span 来跨越多格。
例如以下代码
for (uint8_t i 0; i 9; i) {uint8_t col i % 3;uint8_t row i / 3;lv_obj_t* btn lv_btn_create(cont);lv_obj_set_grid_cell(btn, LV_GRID_ALIGN_STRETCH, col, 1,LV_GRID_ALIGN_STRETCH, row, 1);lv_obj_t* label lv_label_create(btn);lv_label_set_text_fmt(label, r%d c%d, row, col);lv_obj_center(label);
}得到的网格为 这里使用 LV_GRID_ALIGN_STRETCH 让网格内的控件尺寸伸展至网格大小使网格布局的特点更加明显。
3.2、grid布局的对齐
使用网格布局时每个格子内的控件在创建时都可以在网格内对齐。除此之外还可以设置网格自身的对齐方式
void lv_obj_set_grid_align(lv_obj_t * obj, lv_grid_align_t column_align, lv_grid_align_t row_align);网格在横向和竖向对齐摆放时对齐方式都类似于 flex 因此可以认为 grid 是一种二维的 flex 布局。
例如如果略微修改以上代码添加如下语句
lv_obj_set_grid_align(cont, LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_END);
for (uint8_t i 0; i 9; i) {/* ... */lv_obj_set_grid_cell(btn, LV_GRID_ALIGN_START, col, 1,LV_GRID_ALIGN_START, row, 1);/* ... */
}这里去除了控件尺寸的伸展使网格的对齐特点更明显 网格也可以使用相对大小具体做法是利用 LV_GRID_FR(x) 宏计算相对宽度。例如以下定义了一个这样的宽度数组
static lv_coord_t col_pos[] { LV_GRID_FR(1), 60, LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST };那么第二列的宽度是绝对宽度 60 剩余的宽度被划分为 3 份第一列占 1 份第三列占 2 份。这种形式创建的网格可以适应容器的尺寸大小 4、组合控件
复选框
复选框(ckeckbox)是一种类似开关但是带有标签的控件。可以使用以下代码创建复选框并设置标签文本
lv_obj_t* check lv_checkbox_create(cont);
lv_checkbox_set_text(check, Use DMA);一般用复选框并列表示一些“是/否”的选项因此多个并列的复选项很适合使用 flex 布局表现。复选框可以通过状态 LV_STATE_CHECKED 检查是否被勾选。
LVGL 中没有提供单选按钮(radio button)这一控件不过可以使用复选框表示单选按钮。单选按钮在同一时间内只有且必须有一个选择框被选中。首先创建一个框架并使用列模式的 flex 布局
lv_obj_t* cont lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 140, 200);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER);然后可以在其中创建一些复选框
#define CHECKBOX_ITEMS 4
char* checkbox_labels[CHECKBOX_ITEMS] {Use parity bits, Use stop bit, Auto send, Debug mode };
for (uint8_t i 0; i CHECKBOX_ITEMS; i) {lv_obj_t* check lv_checkbox_create(cont);lv_checkbox_set_text(check, checkbox_labels[i]);
}为了实现单选按钮的效果需要在点击事件中清除上一个被选中的选择框。这里介绍一个技巧如何获取事件控件的父容器。如果一个控件被设置了冒泡事件标志 LV_OBJ_FLAG_EVENT_BUBBLE 那么该控件被点击时事件将会由它的父容器触发如果父容器也设置了这一标志位那么事件还会继续向上冒泡。
可以通过
lv_obj_t* lv_event_get_current_target(lv_event_t* e);获取最终触发真正送出事件的控件也就是冒泡后的父控件而之前介绍的 lv_event_get_target() 函数则获取的是最先触发事件的控件也就是子控件。这样通过设置合适的冒泡层数就可以同时获取控件与它的父容器了。
了解了这一特性后就可以编写合适的代码了。首先定义一个全局变量 checked_index 记录单选按钮组此刻选中的按钮索引号并作为用户数据传给回调函数中
static uint8_t checked_index 0;
/* ... */
lv_obj_add_event_cb(cont, radio_checked_cb, LV_EVENT_CLICKED, checked_index);
for (uint8_t i 0; i CHECKBOX_ITEMS; i) {/* ... */lv_obj_add_flag(check, LV_OBJ_FLAG_EVENT_BUBBLE);
}由于事件最终由父容器触发因此要给父容器提供回调函数。然后在回调函数中通过父容器与索引值取消上一个被点击的选择框选择选择点击的选择框并更新索引值
static void radio_checked_cb(lv_event_t* e) {uint8_t* post_checked_index lv_event_get_user_data(e);lv_obj_t* target lv_event_get_target(e);lv_obj_t* parent lv_event_get_current_target(e);if (target parent) return;lv_obj_clear_state(lv_obj_get_child(parent, *post_checked_index), LV_STATE_CHECKED);lv_obj_add_state(target, LV_STATE_CHECKED);*post_checked_index lv_obj_get_index(target);
}由于父容器也拥有点击事件因此首先要判断事件是否是由选择框触发的。这种事件处理方式非常简洁高效而且无需定义额外的辅助数组。
这样就可以使用复选框代替单选按钮了并且这样的回调函数是可以复用的如果有另一组单选按钮也可以使用类似的方式提供响应 5、列表
LVGL 的列表(list)表现形式更像大多数界面提供的标题栏菜单。这里先介绍列表仅仅是因为它比较简单。列表的核心函数只有 3 个
lv_obj_t *lv_list_create(lv_obj_t *parent);
lv_obj_t *lv_list_add_text(lv_obj_t *list, const char *txt);
lv_obj_t *lv_list_add_btn(lv_obj_t *list, const void *icon, const char *txt);以下应用这三个函数创建一个列表
lv_obj_t* list lv_list_create(lv_scr_act());
lv_list_add_text(list, group1);
for (uint8_t i 0; i 2; i)lv_list_add_btn(list, NULL, item);
lv_list_add_text(list, group2);
for (uint8_t i 0; i 3; i)lv_list_add_btn(list, NULL, item);效果为 默认创建的列表尺寸较大可以手动调整尺寸大小。
列表中的按钮和一般创建的按钮没有区别可以给返回值提供回调函数。按钮在创建时还可以指定按钮的图标图标的本质就是 Unicode 中的特殊符号在 lvgl/src/font/lv_symbol_def.h 中可以查看提供的特殊符号。