LVGL全程LittleVGL,是一个轻量化的,开源的,用于嵌入式GUI设计的图形库。并且配合LVGL模拟器,可以在电脑对界面进行编辑显示,测试通过后再移植进嵌入式设备中,实现高效的项目开发。
LVGL中文教程手册:百问网LVGL中文教程手册文档
以下的内容均是以VS环境下的LVGL模拟器进行展开,之后会再针对移植的事项进行说明。
一. 框架
学过QT或者其他UI开发的选手应该了解:
UI界面就是由一个个控件,以及控件之间的相互关系(如父子继承关系,事件触发等)组成的。每个控件都有其独有的属性(如样式,触发函数等),LVGL也是如此。
LVGL还带有主题功能,能够便捷地统一控件样式。
因为是面向嵌入式设备进行开发,LVGL还需要有硬件相关的驱动。
// 主函数代码
int main(int argc, char** argv)
{
/*Initialize LittlevGL*/
lv_init();
/*Initialize the HAL for LittlevGL*/
hal_init();
/*
* Demos, benchmarks, and tests.
*/
lv_demo();
while (1)
{
/* Periodically call the lv_task handler.
* It could be done in a timer interrupt or an OS task too.*/
lv_task_handler();
Sleep(10); /*Just to let the system breathe */
}
return 0;
}
- 对于功能实现,我们需要做的,就是将上面的lv_demo替换为我们自己写的函数。
- 因为lvgl内部是以不同线程的形式来处理各项任务,如刷屏等,并且我们也可以注册任务的,所以需要循环调用lv_task_handler来进行任务处理。
- lv_task_handler的调用除了在主函数的while中进行调用,也可以在OS或者定时器中断中进行调用,比如STM32使用FreeRTOS的一个线程进行处理。
二. 对象
对象具有各种属性,如位置尺寸等。
可以使用lv_obj_set _… 和 **lv_obj_get _…**来设置及获取属性信息。
/* 设置按键的大小 */
lv_obj_set_size(btn, 100, 50);
/* 获取按键的父级指针 */
lv_obj_get_parent(btn)
不同控件的特殊属性可以使用带控件名称的函数进行实现。
/* 设置滑块的最小值和最大值 */
lv_slider_set_range(slider1, 0, 100);
/* 设置回调函数 */
lv_slider_set_action(slider1, my_action);
工作机制
1. 亲子结构
父对象可以作为其子对象的容器。每个对象只能一个父对象(屏幕除外),但是一个父对象可以有无限多个子对象。
父对象的类型没有限制,但是有特殊的父对象(例如,按钮)和特殊的子对象(例如,标签)。
2. 子对象仅在父对象的范围内可见
3. 子对象的位置是相对于父对象的位置,父对象移动时,两者相对位置不变。
创建和删除对象
/**
* @param 指向父对象的指针。创建屏幕时以 NULL 作为父级。
* @param 此参数可选,表示创建新对象时,把 copy 对象上的属性值复制过来
*/
lv_obj_t * lv_ <type>_create(lv_obj_t * parent, lv_obj_t * copy);
/* 所有对象类型都有一个通用的删除功能。它删除对象及其所有子对象 */
void lv_obj_del(lv_obj_t * obj);
/* 异步删除 */
void lv_obj_del_async(lv_obj_t * obj)
/* 删除对象的所有子对象(但不会删除对象本身) */
void lv_obj_clean(lv_obj_t * obj);
屏幕对象
/* 创建屏幕 */
lv_obj_t * scr = lv_obj_create(NULL, NULL);
/* 加载屏幕 */
lv_scr_load(scr)
/* 获取活动屏幕对象 */
lv_obj_t * scr = lv_scr_act()
/* 动态加载屏幕 */
/**
* @brief 动态加载屏幕
* @param transition_type
* LV_SCR_LOAD_ANIM_NONE 延迟x毫秒后立即切换
* LV_SCR_LOAD_ANIM_OVER_LEFT/RIGHT/TOP/BOTTOM 将新屏幕移到给定方向上
* LV_SCR_LOAD_ANIM_MOVE_LEFT/RIGHT/TOP/BOTTOM 将旧屏幕和新屏幕都移至给定方向
* LV_SCR_LOAD_ANIM_FADE_ON 使新屏幕淡进旧屏幕
* @time 转换时间
* @delay 延迟时间
* @auto_del 切换后是否删除旧屏幕
*/
lv_scr_load_anim(scr, transition_type, time, delay, auto_del)
多显示屏的使用
lv_scr_act() , lv_scr_load() 和 lv_scr_load_anim() 将会在默认显示屏幕上操作。
/* 以最后一个注册的显示设备作为默认显示屏幕 */
lv_disp_drv_register(lv_disp_drv_t * driver)
/* 选择新的默认显示屏幕 */
lv_disp_set_default(disp)
零件
对象部件一般都有多个零件。例如,按钮仅具有主要部分,而滑块则由背景,指示器和旋钮组成。我们可以对不同零件分别进行样式设计。
命名规则:LV_ + < TYPE > _ PART _ < NAME >
如:LV_BTN_PART_MAIN 、 LV_SLIDER_PART_KNOB
/* 设置容器样式 */
lv_obj_set_style_local_value_str(h, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, "Text input");
状态
/* 状态列表 */
LV_STATE_DEFAULT // 默认或正常状态
LV_STATE_CHECKED // 选中或点击
LV_STATE_FOCUSED // 通过键盘或编码器聚焦或通过触摸板/鼠标单击
LV_STATE_EDITED // 由编码器编辑
LV_STATE_HOVERED // 鼠标悬停(现在还不支持)
LV_STATE_PRESSED // 按下
LV_STATE_DISABLED // 禁用或无效
- 当用户按下,释放,聚焦等对象时,状态通常由库自动检测更改。
- 状态可以手动检测更改。
/* 完全覆盖当前状态 */
lv_obj_set_state(obj, part, LV_STATE...)
/*设置或清除某个状态(但不更改其他状态) */
lv_obj_<add/clear>_state(obj, part, LV_STATE_...)
/* 例:可以组合使用状态值 */
lv_obj_set_state(obj, part, LV_STATE_PRESSED | LV_PRESSED_CHECKED) .
三. 对象层级
规则:新创建的对象会覆盖旧对象进行显示。
/* 对象或它的任何子对象被点击,将自动将该对象带到最前面显示。*/
lv_obj_set_top(obj,true);
/* 对象或它的任何子对象被点击,将自动将该对象带到最后面。*/
lv_obj_move_foreground(obj);
/* 对象被带到新的父级对象前面 */
lv_obj_set_parent(obj,new_parent);
顶层和系统层
顶层:layer_top
系统层:layer_sys
两者在显示器的所有屏幕上都是可见且通用的,但是,它们不会在多个物理显示器之间共享。
位置:layer_top始终位于默认屏幕的顶部, layer_sys则位于layer_top的顶部。
/* 设置顶层可点击,其会吸收所有用户单击,也就是说其他控件都点不了了。*/
lv_obj_set_click(lv_layer_top(), true);
根据系统层的特性,可以将鼠标光标放在系统层,保持其一直可见。
四. 事件
多个对象可以使用一个回调函数
/* 指定一个事件回调函数 */
lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL);
lv_obj_set_event_cb(btn, my_event_cb);
void my_event_cb(lv_obj_t * obj, lv_event_t event)
switch(event){
case LV_EVENT_PRESSED: /* 对象被按下 */
printf("Pressed\n");
break;
case LV_EVENT_SHORT_CLICKED: /* 对象被短点击 */
printf("Short clicked\n");
break;
case LV_EVENT_CLICKED: /* 对象被点击 */
printf("Clicked\n");
break;
}
事件类型参考:事件(Events)
LV_EVENT_REFRESH是特殊事件,因为它旨在供用户用来通知对象刷新自身。如:
- 通知标签根据一个或多个变量(例如当前时间)刷新其文本
- 语言更改时刷新标签
/* 手动发布事件 */
lv_event_send(obj, LV_EVENT_..., &custom_data);
/* 发布刷新事件至对象 */
lv_event_send_refresh(obj);
/* 发布刷新事件至对象及其子对象 */
lv_event_send_refresh_recursive(obj);
五. 输入设备
设备类型:
- 指针式输入设备,如触摸板或鼠标
- 键盘,如普通键盘或简单的数字键盘
- 带有左/右转向和推入选项的编码器
- 外部硬件按钮,分配给屏幕上的特定点
指针
指针设备通常指鼠标
lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv);
/* Declare the image file */
LV_IMG_DECLARE(mouse_cursor_icon);
/* Create an image object for the cursor */
lv_obj_t * cursor_obj = lv_img_create(lv_scr_act(), NULL);
/* Set the image source */
lv_img_set_src(cursor_obj, &mouse_cursor_icon);
/* Connect the image object to the driver */
lv_indev_set_cursor(mouse_indev, cursor_obj);
键盘和编码器
可以使用键盘和编码器选择对象,将像指针一样。所有想要通过键盘或编码器控制的对象需要添加到Group中。在每个组中,只有一个被聚焦的对象可以接收按下的键盘或编码器的动作。
/* 创建组 */
lv_group_t * g = lv_group_create()
/* 将对象添加进组 */
lv_group_add_obj(g, obj)
/* 关联组和设备 */
lv_indev_set_group(indev, g)
按键
/* 按键类型 */
LV_KEY_NEXT //专注于下一个对象
LV_KEY_PREV //专注于上一个对象
LV_KEY_ENTER //触发 LV_EVENT_PRESSED/CLICKED/LONG_PRESSED 等事件
LV_KEY_UP //增加值或向上移动
LV_KEY_DOWN //减小值或向下移动
LV_KEY_RIGHT //增加值或向右移动
LV_KEY_LEFT //减小值或向左移动
LV_KEY_ESC //关闭或退出(例如,关闭下拉列表)
LV_KEY_DEL //删除(例如,“ 文本”区域中右侧的字符)
LV_KEY_BACKSPACE //删除左侧的字符(例如,在文本区域中)
LV_KEY_HOME //转到开头/顶部(例如,在“ 文本”区域中)
LV_KEY_END //转到末尾(例如,在“ 文本”区域中)
其中,编码器仅使用 LV_KEY_LEFT , LV_KEY_RIGHT 和 LV_KEY_ENTER
编辑和导航模式
主要是针对编码器的应用。
LV_KEY_ENTER:切换编辑/浏览模式。
编辑模式:编码器左右旋为LV_KEY_LEFT/RIGHT
导航模式:编码器左右旋为LV_KEY_NEXT/PREV
状态
对象聚焦状态:LV_STATE_FOCUSED
对象编辑状态:LV_STATE_FOCUSED | LV_STATE_EDITED
因此需要设置这些状态下的样式,以区分对象状态。
六. 字体
字体属于是对象属性其中之一,可以通过设置对象样式的形式改变文本的字体样式。
/* Create a label on the second button */
lv_obj_t * label = lv_label_create(btn, NULL);
lv_style_t my_style;
lv_style_init(&my_style);
/*Set a larger font*/
lv_style_set_text_font(&my_style, LV_STATE_DEFAULT, &lv_font_montserrat_28);
/* Add style to the objct */
lv_obj_add_style(label, LV_LABEL_PART_MAIN, &my_style);
lv_label_set_text(label, "Button");
字体样式
有几种不同大小的内置字体,可以通过 LV_FONT _… 定义在 lv_conf.h 中启用。启用后即可按照上述的格式进行字体样式调用。
/* 普通字体 */
LV_FONT_MONTSERRAT_<VAL> // VAL为12,14,16 ... 48,数值越大字体越大
/* 特殊字体 */
LV_FONT_MONTSERRAT_12_SUBPX // 与常规12像素字体相同,但具有亚像素渲染
LV_FONT_MONTSERRAT_28_COMPRESSED // 与普通的28 px字体相同,但压缩字体为3 bpp
LV_FONT_DEJAVU_16_PERSIAN_HEBREW // 正常范围内的16像素字体+希伯来语,阿拉伯语,Perisan字母及其所有形式
LV_FONT_SIMSUN_16_CJK // 16 px字体,具有正常范围+ 1000个最常见的CJK部首
LV_FONT_UNSCII_8 // 仅包含ASCII字符的8 px像素完美字体
特殊符号
/* 直接调用 */
lv_label_set_text(my_label, LV_SYMBOL_OK);
/* 与字符一起用 */
lv_label_set_text(my_label, LV_SYMBOL_OK "Apply");
/* 多个符号一起用 */
lv_label_set_text(my_label, LV_SYMBOL_OK LV_SYMBOL_WIFI LV_SYMBOL_PLAY);
七. 动画效果
可以使用动画在开始值和结束值之间自动更改变量的值。
/* 初始化动画 */
lv_anim_t a;
lv_anim_init(&a);
/* --- 必选设置 --- */
/* 设置“动画制作”功能 */
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t) lv_obj_set_x);
/* 设置“动画制作”功能 */
lv_anim_set_var(&a, obj);
/* 动画时长[ms] */
lv_anim_set_time(&a, duration);
/* 设置开始和结束值。例如。 0、150 */
lv_anim_set_values(&a, start, end);
/* --- 可选设置 --- */
/* 开始动画之前的等待时间[ms] */
lv_anim_set_delay(&a, delay);
/* 设置路径(曲线)。默认为线性 */
lv_anim_set_path(&a, &path);
/* 设置一个回调以在动画准备好时调用。 */
lv_anim_set_ready_cb(&a, ready_cb);
/* 设置在动画开始时(延迟后)调用的回调。 */
lv_anim_set_start_cb(&a, start_cb);
/* 在此持续时间内,也向后播放动画。默认值为0(禁用)[ms] */
lv_anim_set_playback_time(&a, wait_time);
/* 播放前延迟。默认值为0(禁用)[ms] */
lv_anim_set_playback_delay(&a, wait_time);
/* 重复次数。默认值为1。LV_ANIM_REPEAT_INFINIT用于无限重复 */
lv_anim_set_repeat_count(&a, wait_time);
/* 重复之前要延迟。默认值为0(禁用)[ms] */
lv_anim_set_repeat_delay(&a, wait_time);
/* true(默认):立即应用开始值,false:延迟设置动画后再应用开始值。真正开始。 */
lv_anim_set_early_apply(&a, true/false);
/* 应用动画效果 */
lv_anim_start(&a);
路径设置
lv_anim_path_linear // 线性动画
lv_anim_path_step // 一步到位
lv_anim_path_ease_in // 渐进效果
lv_anim_path_ease_out // 渐退效果
lv_anim_path_ease_in_out // 渐进和渐退效果
lv_anim_path_overshoot // 超出最终值
lv_anim_path_bounce // 从最终值反弹一点(就像撞墙一样)
/* 初始化路径 */
lv_anim_path_t path;
lv_anim_path_init(&path);
lv_anim_path_set_cb(&path, lv_anim_path_<type>);
lv_anim_path_set_user_data(&path, &foo); /* 自定义数据(可选) */
/* 在动画中设置路径 */
lv_anim_set_path(&a, &path);
速度设置
/* 将速度转换为时间再赋值 */
lv_anim_set_time(&a, lv_anim_speed_to_time(speed, start, end));
删除动画
lv_anim_del(var,func)