目录
简介
LVGL(Light and Versatile Graphics Library)不仅提供了基础控件,还包含了许多高级控件,用于实现更复杂和功能丰富的用户界面。本博客将详细介绍LVGL中的高级控件,包括图表、表格、消息框、键盘、日历以及动画等,帮助读者深入掌握LVGL的高级功能。
高级控件概述
什么是高级控件?
高级控件是指那些功能更复杂、需要更多配置和处理的GUI组件。它们通常用于实现特定的功能,如数据可视化、复杂输入、数据展示等。
LVGL中的高级控件
LVGL提供的高级控件包括但不限于:
- 图表(Chart):用于数据可视化。
- 表格(Table):用于展示结构化数据。
- 消息框(Message Box):用于显示提示信息和用户交互。
- 键盘(Keyboard):用于文本输入。
- 日历(Calendar):用于日期选择和展示。
- 动画(Animations):用于界面元素的动态效果。
常用高级控件详解
图片按钮部件(lv_imgbtn)
图片按钮部件和按钮部件是非常相似的,它只不过是在按钮部件的基础上增加了图片显示的功能。
LV_IMG_DECLARE(img_zdyz);
void lv_mainstart(void)
{
/* 定义并创建图片按钮 */
lv_obj_t* imgbtn = lv_imgbtn_create(lv_scr_act());
/* 设置图片释放时的图片 */
lv_imgbtn_set_src(imgbtn, LV_IMGBTN_STATE_RELEASED, NULL, &img_zdyz, NULL);
/* 设置图片按钮大小 */
lv_obj_set_size(imgbtn, 100, 100);
/* 设置图片按钮位置 */
lv_obj_center(imgbtn);
lv_obj_set_style_img_recolor_opa(imgbtn, LV_OPA_30, LV_STATE_PRESSED);
lv_obj_set_style_img_recolor(imgbtn, lv_color_black(), LV_STATE_PRESSED);
lv_obj_set_style_translate_y(imgbtn, 5, LV_STATE_PRESSED);
}
滚轮部件(lv_roller)
滚轮部件可展现多个选项,用户可以通过滚动的形式,从这些选项中选择所需的内容。在用户界面设计中,该部件常用于设置时间、日期等。
static void event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = lv_event_get_target_obj(e);
if(code == LV_EVENT_VALUE_CHANGED) {
char buf[32];
lv_roller_get_selected_str(obj, buf, sizeof(buf));
LV_LOG_USER("Selected month: %s\n", buf);
}
}
/**
* An infinite roller with the name of the months
*/
void lv_example_roller_1(void)
{
lv_obj_t * roller1 = lv_roller_create(lv_screen_active());
lv_roller_set_options(roller1,
"January\n"
"February\n"
"March\n"
"April\n"
"May\n"
"June\n"
"July\n"
"August\n"
"September\n"
"October\n"
"November\n"
"December",
LV_ROLLER_MODE_INFINITE);
lv_roller_set_visible_row_count(roller1, 4);
lv_obj_center(roller1);
lv_obj_add_event_cb(roller1, event_handler, LV_EVENT_ALL, NULL);
}
线条(lv_line)
线条部件由多个点连接而成,它可用于修饰界面或者展示数据。
void lv_example_line_1(void)
{
/*Create an array for the points of the line*/
static lv_point_precise_t line_points[] = { {5, 5}, {70, 70}, {120, 10}, {180, 60}, {240, 10} };
/*Create style*/
static lv_style_t style_line;
lv_style_init(&style_line);
lv_style_set_line_width(&style_line, 8);
lv_style_set_line_color(&style_line, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_line_rounded(&style_line, true);
/*Create a line and apply the new style*/
lv_obj_t * line1;
line1 = lv_line_create(lv_screen_active());
lv_line_set_points(line1, line_points, 5); /*Set the points*/
lv_obj_add_style(line1, &style_line, 0);
lv_obj_center(line1);
}
图表(Chart)
图表控件用于数据可视化,支持多种图表类型,如折线图、柱状图、饼图等。
/*Create a chart*/
lv_obj_t * chart;
chart = lv_chart_create(lv_screen_active());
lv_obj_set_size(chart, 200, 150);
lv_obj_center(chart);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE); /*Show lines and points too*/
/*Add two data series*/
lv_chart_series_t * ser1 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
lv_chart_series_t * ser2 = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_SECONDARY_Y);
int32_t * ser2_y_points = lv_chart_get_series_y_array(chart, ser2);
uint32_t i;
for(i = 0; i < 10; i++) {
/*Set the next points on 'ser1'*/
lv_chart_set_next_value(chart, ser1, (int32_t)lv_rand(10, 50));
/*Directly set points on 'ser2'*/
ser2_y_points[i] = (int32_t)lv_rand(50, 90);
}
lv_chart_refresh(chart); /*Required after direct set*/
表格(Table)
表格控件用于展示结构化数据,支持多行多列。
void lv_example_table_1(void)
{
lv_obj_t * table = lv_table_create(lv_screen_active());
/*Fill the first column*/
lv_table_set_cell_value(table, 0, 0, "Name");
lv_table_set_cell_value(table, 1, 0, "Apple");
lv_table_set_cell_value(table, 2, 0, "Banana");
lv_table_set_cell_value(table, 3, 0, "Lemon");
lv_table_set_cell_value(table, 4, 0, "Grape");
lv_table_set_cell_value(table, 5, 0, "Melon");
lv_table_set_cell_value(table, 6, 0, "Peach");
lv_table_set_cell_value(table, 7, 0, "Nuts");
/*Fill the second column*/
lv_table_set_cell_value(table, 0, 1, "Price");
lv_table_set_cell_value(table, 1, 1, "$7");
lv_table_set_cell_value(table, 2, 1, "$4");
lv_table_set_cell_value(table, 3, 1, "$6");
lv_table_set_cell_value(table, 4, 1, "$2");
lv_table_set_cell_value(table, 5, 1, "$5");
lv_table_set_cell_value(table, 6, 1, "$1");
lv_table_set_cell_value(table, 7, 1, "$9");
/*Set a smaller height to the table. It'll make it scrollable*/
lv_obj_set_height(table, 200);
lv_obj_center(table);
/*Add an event callback to to apply some custom drawing*/
lv_obj_add_event_cb(table, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
lv_obj_add_flag(table, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);
}
事件处理
static void draw_event_cb(lv_event_t * e)
{
lv_draw_task_t * draw_task = lv_event_get_draw_task(e);
lv_draw_dsc_base_t * base_dsc = (lv_draw_dsc_base_t *)lv_draw_task_get_draw_dsc(draw_task);
/*If the cells are drawn...*/
if(base_dsc->part == LV_PART_ITEMS) {
uint32_t row = base_dsc->id1;
uint32_t col = base_dsc->id2;
/*Make the texts in the first cell center aligned*/
if(row == 0) {
lv_draw_label_dsc_t * label_draw_dsc = lv_draw_task_get_label_dsc(draw_task);
if(label_draw_dsc) {
label_draw_dsc->align = LV_TEXT_ALIGN_CENTER;
}
lv_draw_fill_dsc_t * fill_draw_dsc = lv_draw_task_get_fill_dsc(draw_task);
if(fill_draw_dsc) {
fill_draw_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_BLUE), fill_draw_dsc->color, LV_OPA_20);
fill_draw_dsc->opa = LV_OPA_COVER;
}
}
/*In the first column align the texts to the right*/
else if(col == 0) {
lv_draw_label_dsc_t * label_draw_dsc = lv_draw_task_get_label_dsc(draw_task);
if(label_draw_dsc) {
label_draw_dsc->align = LV_TEXT_ALIGN_RIGHT;
}
}
/*Make every 2nd row grayish*/
if((row != 0 && row % 2) == 0) {
lv_draw_fill_dsc_t * fill_draw_dsc = lv_draw_task_get_fill_dsc(draw_task);
if(fill_draw_dsc) {
fill_draw_dsc->color = lv_color_mix(lv_palette_main(LV_PALETTE_GREY), fill_draw_dsc->color, LV_OPA_10);
fill_draw_dsc->opa = LV_OPA_COVER;
}
}
}
}
消息框(Message Box)
消息框用于显示提示信息和用户交互,支持多种按钮类型。
void lv_example_msgbox_1(void)
{
lv_obj_t * mbox1 = lv_msgbox_create(NULL);
lv_msgbox_add_title(mbox1, "Hello");
lv_msgbox_add_text(mbox1, "This is a message box with two buttons.");
lv_msgbox_add_close_button(mbox1);
lv_obj_t * btn;
btn = lv_msgbox_add_footer_button(mbox1, "Apply");
lv_obj_add_event_cb(btn, event_cb, LV_EVENT_CLICKED, NULL);
btn = lv_msgbox_add_footer_button(mbox1, "Cancel");
lv_obj_add_event_cb(btn, event_cb, LV_EVENT_CLICKED, NULL);
return;
}
事件处理
static void event_cb(lv_event_t * e)
{
lv_obj_t * btn = lv_event_get_target_obj(e);
lv_obj_t * label = lv_obj_get_child(btn, 0);
LV_UNUSED(label);
LV_LOG_USER("Button %s clicked", lv_label_get_text(label));
}
键盘(Keyboard)
键盘控件用于文本输入,支持多种键盘类型,如数字键盘、符号键盘等。
void lv_example_keyboard_1(void)
{
/*Create a keyboard to use it with an of the text areas*/
lv_obj_t * kb = lv_keyboard_create(lv_screen_active());
/*Create a text area. The keyboard will write here*/
lv_obj_t * ta1;
ta1 = lv_textarea_create(lv_screen_active());
lv_obj_align(ta1, LV_ALIGN_TOP_LEFT, 10, 10);
lv_obj_add_event_cb(ta1, ta_event_cb, LV_EVENT_ALL, kb);
lv_textarea_set_placeholder_text(ta1, "Hello");
lv_obj_set_size(ta1, 140, 80);
lv_obj_t * ta2;
ta2 = lv_textarea_create(lv_screen_active());
lv_obj_align(ta2, LV_ALIGN_TOP_RIGHT, -10, 10);
lv_obj_add_event_cb(ta2, ta_event_cb, LV_EVENT_ALL, kb);
lv_obj_set_size(ta2, 140, 80);
lv_keyboard_set_textarea(kb, ta1);
/*The keyboard will show Arabic characters if they are enabled */
}
事件处理
static void ta_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * ta = lv_event_get_target_obj(e);
lv_obj_t * kb = (lv_obj_t *)lv_event_get_user_data(e);
if(code == LV_EVENT_FOCUSED) {
lv_keyboard_set_textarea(kb, ta);
lv_obj_remove_flag(kb, LV_OBJ_FLAG_HIDDEN);
}
if(code == LV_EVENT_DEFOCUSED) {
lv_keyboard_set_textarea(kb, NULL);
lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
}
}
日历(Calendar)
日历控件用于日期选择和展示,支持多种视图模式。
void lv_example_calendar_1(void)
{
lv_obj_t * calendar = lv_calendar_create(lv_screen_active());
lv_obj_set_size(calendar, 185, 230);
lv_obj_align(calendar, LV_ALIGN_CENTER, 0, 27);
lv_obj_add_event_cb(calendar, event_handler, LV_EVENT_ALL, NULL);
lv_calendar_set_today_date(calendar, 2021, 02, 23);
lv_calendar_set_month_shown(calendar, 2021, 02);
/*Highlight a few days*/
static lv_calendar_date_t highlighted_days[3]; /*Only its pointer will be saved so should be static*/
highlighted_days[0].year = 2021;
highlighted_days[0].month = 02;
highlighted_days[0].day = 6;
highlighted_days[1].year = 2021;
highlighted_days[1].month = 02;
highlighted_days[1].day = 11;
highlighted_days[2].year = 2022;
highlighted_days[2].month = 02;
highlighted_days[2].day = 22;
lv_calendar_set_highlighted_dates(calendar, highlighted_days, 3);
lv_calendar_header_dropdown_create(calendar);
}
事件处理
static void event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * obj = (lv_obj_t *)lv_event_get_current_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
lv_calendar_date_t date;
if(lv_calendar_get_pressed_date(obj, &date)) {
LV_LOG_USER("Clicked date: %02d.%02d.%d", date.day, date.month, date.year);
}
}
}
圆弧部件(lv_arc)
圆弧(arc)部件是一个较为常用的部件,它的应用场景非常多,例如:调节参数、显示
参数、显示进度,等等。
static void value_changed_event_cb(lv_event_t * e)
{
lv_obj_t * arc = lv_event_get_target_obj(e);
lv_obj_t * label = (lv_obj_t *)lv_event_get_user_data(e);
lv_label_set_text_fmt(label, "%" LV_PRId32 "%%", lv_arc_get_value(arc));
/*Rotate the label to the current position of the arc*/
lv_arc_rotate_obj_to_angle(arc, label, 25);
}
void lv_example_arc_1(void)
{
lv_obj_t * label = lv_label_create(lv_screen_active());
/*Create an Arc*/
lv_obj_t * arc = lv_arc_create(lv_screen_active());
lv_obj_set_size(arc, 150, 150);
lv_arc_set_rotation(arc, 135);
lv_arc_set_bg_angles(arc, 0, 270);
lv_arc_set_value(arc, 10);
lv_obj_center(arc);
lv_obj_add_event_cb(arc, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, label);
/*Manually update the label for the first time*/
lv_obj_send_event(arc, LV_EVENT_VALUE_CHANGED, NULL);
}
动画(Animations)
动画用于界面元素的动态效果,支持多种动画类型,如移动、缩放、旋转等。
animimg001.png
animimg002.png
animimg003.png
LV_IMAGE_DECLARE(animimg001);
LV_IMAGE_DECLARE(animimg002);
LV_IMAGE_DECLARE(animimg003);
static const lv_image_dsc_t * anim_imgs[3] = {
&animimg001,
& animimg002,
& animimg003,
};
void lv_example_animimg_1(void)
{
lv_obj_t * animimg0 = lv_animimg_create(lv_screen_active());
lv_obj_center(animimg0);
lv_animimg_set_src(animimg0, (const void **) anim_imgs, 3);
lv_animimg_set_duration(animimg0, 1000);
lv_animimg_set_repeat_count(animimg0, LV_ANIM_REPEAT_INFINITE);
lv_animimg_start(animimg0);
}
菜单部件(lv_menu)
菜单小部件可轻松创建多级菜单。命令、子菜单或者分隔条都可包括在菜单之中。每一个创建的菜单至多有四级子菜单。菜单部件是一组通常在功能上相关的命令或部件的容器。提供特殊的布局行为,并支持用户启动的工具栏大小调整和排列。
刻度(lv_scale)
void lv_example_scale_1(void)
{
lv_obj_t * scale = lv_scale_create(lv_screen_active());
lv_obj_set_size(scale, lv_pct(80), 100);
lv_scale_set_mode(scale, LV_SCALE_MODE_HORIZONTAL_BOTTOM);
lv_obj_center(scale);
lv_scale_set_label_show(scale, true);
lv_scale_set_total_tick_count(scale, 31);
lv_scale_set_major_tick_every(scale, 5);
lv_obj_set_style_length(scale, 5, LV_PART_ITEMS);
lv_obj_set_style_length(scale, 10, LV_PART_INDICATOR);
lv_scale_set_range(scale, 10, 40);
}
窗口部件(lv_win)
窗口部件就是一个灵活的页面,它常用于任务的前后台切换、多任务协同处理等场景 。
static void event_handler(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target_obj(e);
LV_UNUSED(obj);
LV_LOG_USER("Button %d clicked", (int)lv_obj_get_index(obj));
}
void lv_example_win_1(void)
{
lv_obj_t * win = lv_win_create(lv_screen_active());
lv_obj_t * btn;
btn = lv_win_add_button(win, LV_SYMBOL_LEFT, 40);
lv_obj_add_event_cb(btn, event_handler, LV_EVENT_CLICKED, NULL);
lv_win_add_title(win, "A title");
btn = lv_win_add_button(win, LV_SYMBOL_RIGHT, 40);
lv_obj_add_event_cb(btn, event_handler, LV_EVENT_CLICKED, NULL);
btn = lv_win_add_button(win, LV_SYMBOL_CLOSE, 60);
lv_obj_add_event_cb(btn, event_handler, LV_EVENT_CLICKED, NULL);
lv_obj_t * cont = lv_win_get_content(win); /*Content can be added here*/
lv_obj_t * label = lv_label_create(cont);
lv_label_set_text(label, "This is\n"
"a pretty\n"
"long text\n"
"to see how\n"
"the window\n"
"becomes\n"
"scrollable.\n"
"\n"
"\n"
"Some more\n"
"text to be\n"
"sure it\n"
"overflows. :)");
}
加载器部件(lv_spinner)
加载器在 UI 设计中很常见,它可用于提示用户,当前任务正在加载中。
void lv_example_spinner_1(void)
{
/*Create a spinner*/
lv_obj_t * spinner = lv_spinner_create(lv_screen_active());
lv_obj_set_size(spinner, 100, 100);
lv_obj_center(spinner);
lv_spinner_set_anim_params(spinner, 10000, 200);
}
微调器部件(lv_spinbox)
微调器部件本质上就是一个文本区域部件,它只不过在后者的基础上做了一些延伸。在UI 设计中,微调器主要用于精确调节某个参数值。
static lv_obj_t * spinbox;
static void lv_spinbox_increment_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
lv_spinbox_increment(spinbox);
}
}
static void lv_spinbox_decrement_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_SHORT_CLICKED || code == LV_EVENT_LONG_PRESSED_REPEAT) {
lv_spinbox_decrement(spinbox);
}
}
void lv_example_spinbox_1(void)
{
spinbox = lv_spinbox_create(lv_screen_active());
lv_spinbox_set_range(spinbox, -1000, 25000);
lv_spinbox_set_digit_format(spinbox, 5, 2);
lv_spinbox_step_prev(spinbox);
lv_obj_set_width(spinbox, 100);
lv_obj_center(spinbox);
int32_t h = lv_obj_get_height(spinbox);
lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_set_size(btn, h, h);
lv_obj_align_to(btn, spinbox, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
lv_obj_set_style_bg_image_src(btn, LV_SYMBOL_PLUS, 0);
lv_obj_add_event_cb(btn, lv_spinbox_increment_event_cb, LV_EVENT_ALL, NULL);
btn = lv_button_create(lv_screen_active());
lv_obj_set_size(btn, h, h);
lv_obj_align_to(btn, spinbox, LV_ALIGN_OUT_LEFT_MID, -5, 0);
lv_obj_set_style_bg_image_src(btn, LV_SYMBOL_MINUS, 0);
lv_obj_add_event_cb(btn, lv_spinbox_decrement_event_cb, LV_EVENT_ALL, NULL);
}
调试技巧
- 日志输出:使用串口输出调试信息。
- 断点调试:利用IDE的断点功能进行单步调试。
- 模拟器:使用LVGL模拟器进行高级控件的测试。
性能优化
- 减少控件数量:高级控件通常比较复杂,尽量减少不必要的控件。
- 优化事件处理:避免在事件回调中执行重计算或重绘操作。
- 使用缓存:对于复杂的数据展示,考虑使用缓存机制。
资源与参考
官方资源
社区支持
教程与示例
总结
LVGL提供了丰富的高级控件,使得嵌入式系统GUI开发变得更加灵活和强大。通过本指南的学习,你已经了解了LVGL中的常用高级控件及其使用方法。记住,实践是最好的老师,多动手编写代码,多参与社区讨论,才能不断提升自己的开发水平。