LVGL-(Light and Versatile Graphics Library)
- ■ LVGL-简介
- ■ LVGL-学习链接
- ■ LVGL-工具类
- ■ LVGL-PC环境搭建
- ■ LVGL-源码移植
- ■ LVGL-工作机制
- ■ LVGL-系统层-顶层和活动屏幕
- ■ LVGL-基础对象(lv_obj_t)
- ■ LVGL-界面切换
- ■ LVGL-Flex和Grid布局
- ■ LVGL-lv_timer_t (定时器)
- ■ LVGL-lv_group_t
- ■ LVGL-lv_theme_t
- ■ LVGL-颜色
- ■ LVGL-动画效果 lv_anim_t
- ■ LVGL-标签部件 lv_label_create
- ■ LVGL-按钮部件 lv_btn_create
- ■ LVGL-按钮矩阵部件 lv_btnmatrix_create
- ■ LVGL-图片按钮 lv_imgbtn_create
- ■ LVGL-复选框部件 lv_checkbox_create
- ■ LVGL-键盘部件 lv_keyboard_create
- ■ LVGL-开关部件 lv_switch_create
- ■ LVGL-实体按键 lv_keyboard_create
- ■ LVGL-加载器部件 lv_spinner_create
- ■ LVGL-下拉列表部件 lv_dropdown
- ■ LVGL-内置图标字体
- ■ LVGL-LED部件 lv_led_create
- ■ LVGL-列表部件 lv_list_create
- ■ LVGL-下拉列表选项 lv_dropdown_create
- ■ LVGL-滑动部件 lv_slider_create
- ■ LVGL-滚动部件 lv_roller_create
- ■ LVGL-进度条部件 lv_bar_create
- ■ LVGL-圆弧部件 lv_arc_create
- ■ LVGL-线条部件 lv_line_create
- ■ LVGL-图片部件 lv_img_create
- ■ LVGL-LVGL-BMP,PNG,JPEG,GIF
- ■ LVGL-色环部件 lv_colorwheel_create
- ■ LVGL-文本区域 lv_textarea_create
- ■ LVGL-选项卡 lv_tabview_create
- ■ LVGL-平铺视图 lv_tileview_create
- ■ LVGL-窗口部件 lv_win_create
- ■ LVGL-消息框 lv_msgbox
- ■ LVGL-微调 lv_spinbox_create
- ■ LVGL-表格 lv_table_create
- ■ LVGL- ASCII ,UTF-8 编码
- ■ LVGL-文件系统移植
- ■ LVGL-字库应用
- ■ LVGL-二维码库
- ■ LVGL-重点
- ■ LVGL bug
■ LVGL-简介
LVGL采用的是面向对象的编程思想,以抽象的类来实例化不同的对象(部件)
■ LVGL-学习链接
官网编程函数查询
官网
github仓库
博客站点
sim 在线模拟器网站
嵌入式GUI框架对比
LVGL 爱洋葱
Get IoT !网站
■ LVGL-工具类
■ LVGL-codeblock
■ LVGL-使用GUI Guider 拖拽式设计LVGL
■ LVGL-使用SquareLine Studio图形开发工具
■ LVGL-PC环境搭建
选择不同版本就复制不用的实例来运行学习
■ LVGL-源码移植
■ LVGL-工作机制
- 父子结构
父对象可以作为其子对象的容器。每个对象只能一个父对象(屏幕除外),但是一个父对象可以有无限多个子对象。
父对象的类型没有限制,但是有特殊的父对象(例如,按钮)和特殊的子对象(例如,标签)。 - 子对象仅在父对象的范围内可见
- 子对象的位置是相对于父对象的位置,父对象移动时,两者相对位置不变。
■ LVGL-系统层-顶层和活动屏幕
属性 | 描述 | 描述 |
---|---|---|
系统层的指针 | lv_layer_sys() | 例如,它将鼠标光标放在那里以确保它始终可见。 |
返回指向顶层 | lv_layer_top() | layer_top 来创建一些随处可见的内容。例如,菜单栏,弹出窗口等。 如果启用了 click 属性,那么 layer_top 将吸收所有用户单击并充当模态。lv_obj_set_click(lv_layer_top(), true); |
获取活动屏幕 | lv_scr_act() |
lv_obj_t * sys = lv_layer_sys(); 系统层
lv_obj_t * top = lv_layer_top(); 顶层
lv_obj_t * act = lv_scr_act(); 默认屏幕
■ LVGL-基础对象(lv_obj_t)
■ LVGL-界面切换
■ LVGL-Flex和Grid布局
■ LVGL-lv_timer_t (定时器)
typedef struct _lv_timer_t {
uint32_t period; // 定时器运行的频率
uint32_t last_run; // 定时器上次运行的时间
lv_timer_cb_t timer_cb; // 定时器注册的回调函数
void * user_data; // 用户自定义数据
int32_t repeat_count; // 重复次数,-1为永久重复,0为关闭,大于0为重复次数。
uint32_t paused : 1; // 定时器运行状态
} lv_timer_t;
void my_timer_cb(lv_timer_t *tmr)
{
LV_LOG_USER("my_timer_cb test ......");
}
lv_timer_t *my_time = NULL;
my_time = lv_timer_create(my_timer_cb, 1000, 0); // 运行周期为lvgl的1000个滴答时钟
lv_timer_set_repeat_count(my_time, 5); // 运行指定次数的定时器
lv_timer_reset(my_time); // 重新开始计时
lv_timer_pause(my_time); // 暂停
lv_timer_resume(my_time); // 暂停后恢复运行
lv_timer_ready(my_time); // 立马运行一次,下一次按周期运行
lv_timer_set_period(my_time, 5000); // 5秒周期
lv_timer_del(my_time); // 删除定时器
lv_timer_set_cb(my_time, my_timer_cb); // 设置回调函数 设置两个回调函数,最后一个有效。
lv_timer_t *my_time2 = lv_timer_get_next(my_time); // 遍历定时器
■ LVGL-lv_group_t
■ LVGL-lv_theme_t
void ui_init(void)
{
lv_disp_t * dispp = lv_disp_get_default();
lv_theme_t * theme = lv_theme_default_init(dispp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
true, LV_FONT_DEFAULT);
lv_disp_set_theme(dispp, theme);
ui_HomePage_screen_init();
lv_disp_load_scr(ui_HomePage);
}
■ LVGL-颜色
lv_color_hex(0x808080)
■ LVGL-动画效果 lv_anim_t
- 使用动画在开始值和结束值之间自动更改变量的值。
/* 初始化动画 */
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)
■ LVGL-标签部件 lv_label_create
■ LVGL-按钮部件 lv_btn_create
■ LVGL-按钮矩阵部件 lv_btnmatrix_create
■ LVGL-图片按钮 lv_imgbtn_create
■ LVGL-复选框部件 lv_checkbox_create
■ LVGL-键盘部件 lv_keyboard_create
■ LVGL-开关部件 lv_switch_create
■ LVGL-实体按键 lv_keyboard_create
■ LVGL-加载器部件 lv_spinner_create
== ■ 示例一:==
static void lv_example_spinner(void)
{
spinner = lv_spinner_create(lv_scr_act(), 1000, 60); /* 创建加载器 */
lv_obj_align(spinner, LV_ALIGN_CENTER, 0, -scr_act_height() / 15 ); /* 设置位置 */
lv_obj_set_size(spinner, scr_act_height() / 5, scr_act_height() / 5); /* 设置大小 */
lv_obj_set_style_arc_width(spinner, scr_act_height() / 35, LV_PART_MAIN); /* 设置主体圆弧宽度 */
lv_obj_set_style_arc_width(spinner, scr_act_height() / 35, LV_PART_INDICATOR); /* 设置指示器圆弧宽度 */
}
■ LVGL-下拉列表部件 lv_dropdown
■ LVGL-内置图标字体
可以打开lv_symbol_def.h文件
#define LV_SYMBOL_AUDIO "\xef\x80\x81" /*61441, 0xF001*/
#define LV_SYMBOL_VIDEO "\xef\x80\x88" /*61448, 0xF008*/
#define LV_SYMBOL_LIST "\xef\x80\x8b" /*61451, 0xF00B*/
#define LV_SYMBOL_OK "\xef\x80\x8c" /*61452, 0xF00C*/
/* 直接调用 */
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);
示例一:图标
void lv_mainstart(void)
{
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, LV_SYMBOL_AUDIO"AUDIO");
}
■ LVGL-LED部件 lv_led_create
== 示例一:==
/**
* @brief LED事件回调
* @param *e :事件相关参数的集合,它包含了该事件的所有数据
* @return 无
*/
static void led_event_cb(lv_event_t* e)
{
lv_obj_t* led = lv_event_get_target(e); /* 获取触发源 */
lv_led_toggle(led); /* 翻转LED状态 */
}
/**
* @brief LED1
* @param 无
* @return 无
*/
static void lv_example_led_1(void)
{
/* 根据活动屏幕宽度选择字体 */
if (scr_act_width() <= 480)
{
font = &lv_font_montserrat_14;
}
else
{
font = &lv_font_montserrat_20;
}
/* 创建基础对象作为背景 */
obj = lv_obj_create(lv_scr_act());
lv_obj_set_size(obj, scr_act_width() * 5 /6 , scr_act_height() * 3 /5);
lv_obj_align(obj, LV_ALIGN_CENTER, 0 , 0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xefefef), LV_STATE_DEFAULT);
lv_obj_t* led = lv_led_create(obj); /* 创建LED */
lv_obj_set_size(led, scr_act_height() /5 , scr_act_height() /5); /* 设置LED大小 */
lv_obj_align(led, LV_ALIGN_CENTER, -scr_act_width() * 4/ 15, -scr_act_height() /15); /* 设置LED位置 */
lv_led_off(led); /* 关闭LED */
lv_obj_add_event_cb(led, led_event_cb, LV_EVENT_CLICKED, NULL); /* 设置LED事件回调 */
lv_obj_t *label = lv_label_create(lv_scr_act()); /* 创建LED功能标签 */
lv_label_set_text(label, "ROOM 1"); /* 设置文本 */
lv_obj_set_style_text_font(label, font, LV_STATE_DEFAULT); /* 设置字体 */
lv_obj_align_to(label, led, LV_ALIGN_OUT_BOTTOM_MID, 0, scr_act_height() /15 ); /* 设置位置 */
}
/**
* @brief LED2
* @param 无
* @return 无
*/
static void lv_example_led_2(void)
{
lv_obj_t* led = lv_led_create(obj); /* 创建LED */
lv_obj_set_size(led, scr_act_height() /5 , scr_act_height() /5); /* 设置LED大小 */
lv_obj_align(led, LV_ALIGN_CENTER, 0, -scr_act_height() /15); /* 设置LED位置 */
lv_led_set_color(led, lv_color_hex(0xff0000)); /* 设置LED颜色 */
lv_led_on(led); /* 打开LED */
lv_obj_add_event_cb(led, led_event_cb, LV_EVENT_CLICKED, NULL); /* 设置LED事件回调 */
lv_obj_t *label = lv_label_create(lv_scr_act()); /* 创建LED功能标签 */
lv_label_set_text(label, "ROOM 2"); /* 设置文本 */
lv_obj_set_style_text_font(label, font, LV_STATE_DEFAULT); /* 设置字体 */
lv_obj_align_to(label, led, LV_ALIGN_OUT_BOTTOM_MID, 0, scr_act_height() /15 ); /* 设置位置 */
}
/**
* @brief LED3
* @param 无
* @return 无
*/
static void lv_example_led_3(void)
{
lv_obj_t* led = lv_led_create(obj); /* 创建LED */
lv_obj_set_size(led, scr_act_height() /5 , scr_act_height() /5); /* 设置LED大小 */
lv_obj_align(led, LV_ALIGN_CENTER, scr_act_width() * 4/ 15, -scr_act_height() /15); /* 设置LED位置 */
lv_led_set_color(led, lv_color_hex(0x2fc827)); /* 设置LED颜色 */
lv_led_off(led); /* 关闭LED */
lv_obj_add_event_cb(led, led_event_cb, LV_EVENT_CLICKED, NULL); /* 设置LED事件回调 */
lv_obj_t *label = lv_label_create(lv_scr_act()); /* 创建LED功能标签 */
lv_label_set_text(label, "ROOM 3"); /* 设置文本 */
lv_obj_set_style_text_font(label, font, LV_STATE_DEFAULT); /* 设置字体 */
lv_obj_align_to(label, led, LV_ALIGN_OUT_BOTTOM_MID, 0, scr_act_height() /15 ); /* 设置位置 */
}
■ LVGL-列表部件 lv_list_create
■ LVGL-下拉列表选项 lv_dropdown_create
■ 示例一
/**
* @brief 下拉列表事件回调
* @param 无
* @return 无
*/
static void dropdown_event_cb(lv_event_t* e)
{
lv_event_code_t code = lv_event_get_code(e); /* 获取事件类型 */
lv_obj_t *dropdown = lv_event_get_target(e); /* 获取触发源 */
if (LV_EVENT_VALUE_CHANGED == code) /* 判断事件类型 */
{
char buf[10];
lv_dropdown_get_selected_str(dropdown, buf, sizeof(buf)); /* 获取当前选项文本 */
lv_label_set_text(label, buf); /* 显示当前选项文本 */
}
}
/**
* @brief 例1
* @param 无
* @return 无
*/
static void lv_example_dropdown_1(void)
{
/* 根据屏幕宽度选择字体和列表宽度 */
if (scr_act_width() <= 320)
{
dropdown_font = &lv_font_montserrat_14;
dropdown_width = 90;
}
else if (scr_act_width() <= 480)
{
dropdown_font = &lv_font_montserrat_18;
dropdown_width = 120;
}
else
{
dropdown_font = &lv_font_montserrat_22;
dropdown_width = 150;
}
lv_obj_t* dropdown = lv_dropdown_create(lv_scr_act()); /* 定义并创建下拉列表 */
lv_dropdown_set_options_static(dropdown, options); /* 添加下拉列表选项 */
lv_obj_set_style_text_font(dropdown, dropdown_font, LV_PART_MAIN); /* 设置下拉列表字体 */
lv_obj_set_width(dropdown, dropdown_width); /* 设置下拉列表宽度 */
lv_obj_align(dropdown, LV_ALIGN_CENTER, -scr_act_width() / 3, 0); /* 设置下拉列表位置 */
label = lv_label_create(lv_scr_act()); /* 定义并创建标签 */
lv_obj_set_style_text_font(label, dropdown_font, LV_PART_MAIN); /* 设置标签表字体 */
lv_obj_set_width(label, dropdown_width); /* 设置标签宽度 */
lv_obj_align_to(label, dropdown, LV_ALIGN_OUT_TOP_MID, 15, -scr_act_height() / 8); /* 设置标签位置 */
lv_label_set_text(label, "option 1"); /* 设置标签文本 */
lv_obj_add_event_cb(dropdown, dropdown_event_cb, LV_EVENT_VALUE_CHANGED, NULL); /* 添加下拉列表回调 */
}
/**
* @brief 例2
* @param 无
* @return 无
*/
static void lv_example_dropdown_2(void)
{
lv_obj_t* dropdown; /* 定义下拉列表 */
dropdown = lv_dropdown_create(lv_scr_act()); /* 创建下拉列表 */
lv_dropdown_set_options_static(dropdown, options); /* 添加下拉列表选项 */
lv_obj_set_style_text_font(dropdown, dropdown_font, LV_PART_MAIN); /* 设置下拉列表字体 */
lv_obj_set_width(dropdown, dropdown_width); /* 设置下拉列表宽度 */
lv_dropdown_set_dir(dropdown, LV_DIR_BOTTOM); /* 设置下拉列表方向 */
lv_dropdown_set_symbol(dropdown, LV_SYMBOL_DOWN); /* 设置下拉列表符号 */
lv_obj_align(dropdown, LV_ALIGN_CENTER, scr_act_width() / 8, -3 * scr_act_height() / 8); /* 设置下拉列表位置 */
dropdown = lv_dropdown_create(lv_scr_act()); /* 创建下拉列表 */
lv_dropdown_set_options_static(dropdown, options); /* 添加下拉列表选项 */
lv_obj_set_style_text_font(dropdown, dropdown_font, LV_PART_MAIN); /* 设置下拉列表字体 */
lv_obj_set_width(dropdown, dropdown_width); /* 设置下拉列表宽度 */
lv_dropdown_set_dir(dropdown, LV_DIR_LEFT); /* 设置下拉列表方向 */
lv_dropdown_set_symbol(dropdown, LV_SYMBOL_LEFT); /* 设置下拉列表符号 */
lv_obj_align(dropdown, LV_ALIGN_CENTER, scr_act_width() / 8, -1 * scr_act_height() / 8); /* 设置下拉列表位置 */
dropdown = lv_dropdown_create(lv_scr_act()); /* 创建下拉列表 */
lv_dropdown_set_options_static(dropdown, options); /* 添加下拉列表选项 */
lv_obj_set_style_text_font(dropdown, dropdown_font, LV_PART_MAIN); /* 设置下拉列表字体 */
lv_obj_set_width(dropdown, dropdown_width); /* 设置下拉列表宽度 */
lv_dropdown_set_dir(dropdown, LV_DIR_RIGHT); /* 设置下拉列表方向 */
lv_dropdown_set_symbol(dropdown, LV_SYMBOL_RIGHT); /* 设置下拉列表符号 */
lv_obj_align(dropdown, LV_ALIGN_CENTER, scr_act_width() / 8, 1 * scr_act_height() / 8); /* 设置下拉列表位置 */
dropdown = lv_dropdown_create(lv_scr_act()); /* 创建下拉列表 */
lv_dropdown_set_options_static(dropdown, options); /* 添加下拉列表选项 */
lv_obj_set_style_text_font(dropdown, dropdown_font, LV_PART_MAIN); /* 设置下拉列表字体 */
lv_obj_set_width(dropdown, dropdown_width); /* 设置下拉列表宽度 */
lv_dropdown_set_dir(dropdown, LV_DIR_TOP); /* 设置下拉列表方向 */
lv_dropdown_set_symbol(dropdown, LV_SYMBOL_UP); /* 设置下拉列表符号 */
lv_obj_align(dropdown, LV_ALIGN_CENTER, scr_act_width() / 8, 3 * scr_act_height() / 8); /* 设置下拉列表位置 */
}
■ 使用 SCROLL控件实现滚动功能
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#include "esp_log.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX
/**
* @brief 滚动结束后的事件
* @param event
*/
static void scroll_end_event(lv_event_t * e)
{
lv_obj_t * cont = lv_event_get_target(e); // 获取事件的初始对象
/* 获取事件的事件代码 */
if(lv_event_get_code(e) == LV_EVENT_SCROLL_END)
{
/* 判断是否在滚动中 */
if (lv_obj_is_scrolling(cont))
{
return;
}
lv_coord_t child_cnt = lv_obj_get_child_cnt(cont); // 获取子界面的数量
lv_coord_t mid_btn_index = (child_cnt - 1) / 2; // 中间界面的位置
/* 获取父对象y轴的中心坐标值 */
lv_area_t cont_a;
lv_obj_get_coords(cont, &cont_a); // 将cont对象的坐标复制到cont_a
lv_coord_t cont_y_center = cont_a.y1 + lv_area_get_width(&cont_a) / 2; // 获取界面的宽像素大小/2
/* 注意,这里的中心显示界面的坐标不在正中心,所以这里加上了差值 */
cont_y_center += 69;
/* 遍历子界面 */
for (lv_coord_t i = 0; i < child_cnt; i++) {
lv_obj_t* child = lv_obj_get_child(cont, i); // 通过索引获取子对象
/* 获取子对象y轴的中心坐标值 */
lv_area_t child_a;
lv_obj_get_coords(child, &child_a);
lv_coord_t child_y_center = child_a.y1 + lv_area_get_width(&child_a) / 2; // 获取界面中按钮宽像素值的大小/2
/* 子界面的坐标与父界面的坐标相等时,说明当前界面在父界面中显示 */
if (child_y_center == cont_y_center)
{
/* 当前显示界面的索引 */
lv_coord_t current_btn_index = lv_obj_get_index(child);
/* 判断界面移动的数数据,并将当前界面的索引改为中间位置 */
/* 因为是在滑动结束后实现的,建议界面较多的情况下使用此方式,当界面较少,一次滑动太多界面时,容易滑倒边界出现卡顿现象 */
lv_coord_t move_btn_quantity = LV_ABS(current_btn_index - mid_btn_index);
for (lv_coord_t j = 0; j < move_btn_quantity; j++)
{
/* 向右滑动 */
if (current_btn_index < mid_btn_index)
{
lv_obj_move_to_index(lv_obj_get_child(cont, child_cnt - 1), 0); // 将最后一个界面索引改为第一个界面
lv_obj_scroll_to_view(lv_obj_get_child(cont, mid_btn_index), LV_ANIM_OFF); // lv_obj_get_child 通过子索引获取对象的子对象
}
/* 向左滑动 */
if (current_btn_index > mid_btn_index)
{
lv_obj_move_to_index(lv_obj_get_child(cont, 0), child_cnt - 1); // 将第一个界面的索引值改为最后一个界面
lv_obj_scroll_to_view(lv_obj_get_child(cont, mid_btn_index), LV_ANIM_OFF); // lv_obj_get_child 通过子索引获取对象的子对象
}
}
/* 保证界面居中显示 */
lv_obj_set_style_translate_y(lv_obj_get_child(cont, mid_btn_index), 0, 0);
break;
}
}
}
}
static void scroll_event_cb(lv_event_t * e)
{
lv_obj_t * cont = lv_event_get_target(e);
lv_area_t cont_a;
lv_obj_get_coords(cont, &cont_a);
lv_coord_t cont_y_center = cont_a.y1 + lv_area_get_height(&cont_a) / 2;
lv_coord_t r = lv_obj_get_height(cont) * 7 / 10;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_cnt(cont);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(cont, i);
lv_area_t child_a;
lv_obj_get_coords(child, &child_a);
lv_coord_t child_y_center = child_a.y1 + lv_area_get_height(&child_a) / 2;
lv_coord_t diff_y = child_y_center - cont_y_center;
diff_y = LV_ABS(diff_y);
/*Get the x of diff_y on a circle.*/
lv_coord_t x;
/*If diff_y is out of the circle use the last point of the circle (the radius)*/
if(diff_y >= r) {
x = r;
}
else {
/*Use Pythagoras theorem to get x from radius and y*/
uint32_t x_sqr = r * r - diff_y * diff_y;
lv_sqrt_res_t res;
lv_sqrt(x_sqr, &res, 0x8000); /*Use lvgl's built in sqrt root function*/
x = r - res.i;
}
/*Translate the item by the calculated X coordinate*/
lv_obj_set_style_translate_x(child, x, 0);
/*Use some opacity with larger translations*/
lv_opa_t opa = lv_map(x, 0, r, LV_OPA_TRANSP, LV_OPA_COVER);
lv_obj_set_style_opa(child, LV_OPA_COVER - opa, 0);
}
}
/**
* Translate the object as they scroll
*/
void lvgl_scroll_test(void)
{
lv_obj_t * cont = lv_obj_create(lv_scr_act());
lv_obj_set_size(cont, 200, 200);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
// lv_obj_add_event_cb(cont, scroll_event_cb, LV_EVENT_SCROLL, NULL); // 为了方便实现,先把滚动的动画屏蔽
lv_obj_add_event_cb(cont, scroll_end_event, LV_EVENT_SCROLL_END, NULL);
lv_obj_set_style_radius(cont, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_clip_corner(cont, true, 0);
lv_obj_set_scroll_dir(cont, LV_DIR_VER);
lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER);
lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);
uint32_t i;
for(i = 0; i < 20; i++) {
lv_obj_t * btn = lv_btn_create(cont);
lv_obj_set_width(btn, lv_pct(100));
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Button %"LV_PRIu32, i);
}
/*---------------------------------------- 指定中心显示界面 ----------------------------------------*/
lv_coord_t mid_btn_index = (lv_obj_get_child_cnt(cont) - 1) / 2; // 如果界面为偶数,将中间数向下取整的界面设置为中间界面
lv_coord_t child_cnt = lv_obj_get_child_cnt(cont); // 获取子界面的数量
int roll_direction = mid_btn_index - mid_btn_index; // 确定滚动方向
/* 通过循环将指定界面移到中心位置 */
for (lv_coord_t i = 0; i < LV_ABS(roll_direction); i++)
{
if (roll_direction > 0)
{
lv_obj_move_to_index(lv_obj_get_child(cont, child_cnt - 1), 0); // 将最后一个界面的索引更改为 0 (移至第一个界面)
}
else
{
lv_obj_move_to_index(lv_obj_get_child(cont, 0), child_cnt - 1); // 将第一个界面的索引值改为最大值(移至最后一个界面)
}
}
/*当按钮数为偶数时,确保按钮居中*/
lv_obj_scroll_to_view(lv_obj_get_child(cont, mid_btn_index), LV_ANIM_OFF); // 滚动到一个对象,直到它在其父对象上可见
}
#endif
■ LVGL-滑动部件 lv_slider_create
■ LVGL-滚动部件 lv_roller_create
■ LVGL-进度条部件 lv_bar_create
■ LVGL-圆弧部件 lv_arc_create
■ LVGL-线条部件 lv_line_create
■ LVGL-图片部件 lv_img_create
■ LVGL-LVGL-BMP,PNG,JPEG,GIF
■ LVGL-色环部件 lv_colorwheel_create
■ LVGL-文本区域 lv_textarea_create
■ LVGL-选项卡 lv_tabview_create
■ LVGL-平铺视图 lv_tileview_create
■ LVGL-窗口部件 lv_win_create
■ LVGL-消息框 lv_msgbox
■ LVGL-微调 lv_spinbox_create
■ LVGL-表格 lv_table_create
■ LVGL- ASCII ,UTF-8 编码
用户需要在 LVGL 工程中启用UTF-8 编码, 可以打开 lv_conf.h 文件, 修改 LV_TXT_ENC 配置。
/* 为字符串选择字符编码.
* IDE 或编辑器应该具有相同的字符编码
* 1. - LV_TXT_ENC_UTF8
* 2. - LV_TXT_ENC_ASCII
* */
#define LV_TXT_ENC LV_TXT_ENC_UTF8
建议大家将 MDK 软件设置为 Chinese GB2312 编码, 以更好地兼容中文。
■ LVGL-文件系统移植
■ LVGL-字库应用
■ LVGL-二维码库
■ LVGL-重点
■ 手势事件
void ui_event_HomePage(lv_event_t * e)
{
lv_event_code_t event_code = lv_event_get_code(e);
lv_obj_t * target = lv_event_get_target(e);
if(event_code == LV_EVENT_GESTURE) //手势事件
{
if(lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_RIGHT) //获取一个手势
{
user_Stack_Pop(&ScrRenewStack);
ui_MenuPage_screen_init();
lv_scr_load_anim(ui_MenuPage,LV_SCR_LOAD_ANIM_MOVE_RIGHT,100,0,true);
user_Stack_Push(&ScrRenewStack,(long long int)&ui_HomePage);
user_Stack_Push(&ScrRenewStack,(long long int)&ui_MenuPage);
}
}
}
■ 电子书项目 页面翻页 -> 手势事件
== ui_ebook_txt 控件才有手势事件 ,子控件验证没有手势事件==
lv_obj_t * ui_ebook_txt = NULL; //电子书root
void ui_ebook_screen_init(void)
{
ui_ebook_txt = lv_obj_create(NULL); 、、
lv_obj_clear_flag(ui_ebook_txt, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_width(ui_ebook_txt, LV_PCT(100));
lv_obj_set_height(ui_ebook_txt, LV_PCT(100));
lv_obj_add_event_cb(ui_ebook_txt, ui_event_ebook, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ui_ebook_txt, ebook_keyinput_event_cb, LV_EVENT_GESTURE, NULL);
lv_obj_set_style_bg_color(ui_ebook_txt, lv_color_hex(0xfcfcfc), LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(ui_ebook_txt, 255, LV_PART_MAIN | LV_STATE_DEFAULT);
}
void ebook_keyinput_event_cb(lv_event_t *event)
{
lv_event_code_t code = lv_event_get_code(event);
lv_obj_t *target=lv_event_get_target(event);
if(code == LV_EVENT_GESTURE)
{
if(target==ui_ebook_txt)
{
lv_dir_t dir =lv_indev_get_gesture_dir(lv_indev_get_act());
switch (dir){
case LV_DIR_LEFT:
case LV_DIR_TOP:
// printf("shine+ %s,%d,dir=%d\n",__func__,__LINE__,dir);
change_ebook_txt_info(LV_KEY_DOWN,0);
break;
case LV_DIR_RIGHT:
case LV_DIR_BOTTOM:
// printf("shine+ %s,%d,dir=%d\n",__func__,__LINE__,dir);
change_ebook_txt_info(LV_KEY_UP,0);
break;
default :
break;
}
}
}
}
■ 按键处理
■ 按键驱动
//初始化按键线程
void api_key_get_init()
{
pthread_t thread_id = 0;
pthread_attr_t attr;
key_queue_init(&m_key_queue);
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 0x2000);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); //release task resource itself
if(pthread_create(&thread_id, &attr, _key_task, NULL)) {
return;
}
pthread_attr_destroy(&attr);
key_simu_start();
}
//进程中读取按键消息
static void *_key_task(void *arg)
{
struct pollfd *pfd;
int cnt;
KEY_MSG_s key_msg;
struct input_event *t;
int input_cnt = 0;
uint8_t input_name[32];
int key_fds[INPUT_DEV_MAX] = {0,};
int i;
for (i = 0; i < INPUT_DEV_MAX; i++)
{
#if (SOWYE_PLAYER_APP == 1)
if(i==1) // 触摸屏 event1
{
continue;
}
#endif
sprintf(input_name, "/dev/input/event%d", i);
key_fds[input_cnt] = open(input_name, O_RDONLY);
if (key_fds[input_cnt] < 0){
break;
}
printf("\nopen %s, line:%d. i=%d, %s\r\n",__func__,i,__LINE__,input_name);
input_cnt++;
}
if (0 == input_cnt){
printf("\n%s(), line:%d. No Input device!!!\r\n", __func__, __LINE__);
return NULL;
}
printf("%s(), input device num: %d.\r\n", __func__, input_cnt);
pfd = (struct pollfd*)malloc(sizeof(struct pollfd)*input_cnt);
memset(pfd, 0, sizeof(struct pollfd)*input_cnt);
for (i = 0; i < input_cnt; i ++){
pfd[i].fd = key_fds[i];
pfd[i].events = POLLIN | POLLRDNORM;
}
t = &key_msg.key_event;
#ifdef BLUETOOTH_SUPPORT
bluetooth_ir_key_init(bt_key_queue_write_ctrl);
#endif
// update_tp = POINT_CLEAR_STATE;
while (1){
if (poll(pfd, input_cnt, -1) <= 0){
continue;
}
for (i = 0; i < input_cnt; i ++)
{
if (pfd[i].revents != 0){
bzero(t, sizeof(struct input_event));
cnt = read(key_fds[i], t, sizeof(struct input_event));
if (m_key_off){
api_sleep_ms(10);
continue;
}
if (cnt != sizeof(struct input_event)){
printf("cnt = %d, err(%d):%s\r\n", cnt, errno, strerror(errno));
api_sleep_ms(5);
}
#if 0
// 遥控器
// type1 : 0x0001, code : 0x0200, value : 0x00000000 ,event 0, keytype=-1515870811
// type1 : 0x0000, code : 0x0000, value : 0x00000000 ,event 0, keytype=-1515870811
// 触摸屏
// type1 : 0x0003, code : 0x0035, value : 0x00000565 ,event 1, keytype=-1515870811
// type1 : 0x0003, code : 0x0036, value : 0x00000347 ,event 1, keytype=-1515870811
// key
// type1 : 0x0004, code : 0x0004, value : 0x00000105 ,event 2, keytype=-1515870811
// type1 : 0x0000, code : 0x0000, value : 0x00000000 ,event 2, keytype=-1515870811
printf("type1 : 0x%04X, code : 0x%04X, value : 0x%08d ,event%2d, keytype=%d\r\n",t->type, t->code, t->value ,i ,(int)key_msg.key_type);
#endif
if (_key_event_is_valid(t))
{
//here may change the key type, for sometims
//event0 may be IR, may be GPIO or ADC key.
if (0 == i)
key_msg.key_type = KEY_TYPE_IR;
else if (1 == i)
key_msg.key_type = KEY_TYPE_ADC;
else if (2 == i)
key_msg.key_type = KEY_TYPE_GPIO;
// printf("shine555 func=%s,line=%d,key_msg.key_type=%d,t->type=%d, t->code=%d, t->value=%ld \n", __func__,__LINE__,
// key_msg.key_type, t->type, t->code, t->value);
key_queue_write(&m_key_queue, &key_msg);
// 长按 遥控器
// shine555 func=_key_task,line=560,key_msg.key_type=0,t->type=1, t->code=106, t->value=1;
// shine555 func=_key_task,line=560,key_msg.key_type=0,t->type=4, t->code=106, t->value=1; //4
// shine555 func=_key_task,line=560,key_msg.key_type=0,t->type=4, t->code=106, t->value=1; //4
// shine555 func=_key_task,line=560,key_msg.key_type=0,t->type=4, t->code=106, t->value=1; //4
// shine555 func=_key_task,line=560,key_msg.key_type=0,t->type=1, t->code=106, t->value=0; // 0
//短按 遥控器
// shine555 func=_key_task,line=560,key_msg.key_type=0,t->type=1, t->code=28, t->value=1;
// shine555 func=_key_task,line=560,key_msg.key_type=0,t->type=1, t->code=28, t->value=0;
//+按键-短按
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=352, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=352, t->value=0
//+按键-长按
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=352, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=4, t->code=352, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=4, t->code=352, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=4, t->code=352, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=352, t->value=0
//-按键-短按
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=105, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=105, t->value=0
//-按键-长按
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=105, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=4, t->code=105, t->value=1
// shine555 func=_key_task,line=560,key_msg.key_type=1,t->type=1, t->code=105, t->value=0
}
}
}
api_sleep_ms(5);
}
for (i = 0; i < input_cnt; i ++){
if (key_fds[i] > 0)
close(key_fds[i]);
}
free(pfd);
return 0;
}
//读取消息队列中的按键
KEY_MSG_s *api_key_msg_get(void)
{
KEY_MSG_s *key_msg = NULL;
if (key_queue_read(&m_key_queue, &key_msg))
return key_msg;
else
return NULL;
}
/*Get the currently being pressed key. 0 if no key is pressed*/
static uint32_t keypad_read_key(lv_indev_data_t *lv_key)
{
/*Your code comes here*/
struct input_event *t;
uint32_t ret = 0;
KEY_MSG_s *key_msg = NULL;
key_msg = api_key_msg_get();
if (!key_msg){
return 0;
}
t = &key_msg->key_event;
// printf("shine + %s,%d,key_msg->key_type=%d,t->type=%d, t->code=%d, t->value=%ld \n",
// __func__,__LINE__,
// key_msg->key_type, t->type, t->code, t->value); //t->type = EV_KEY ==0x01
if(t->value == 1) //pressed
{
ret = key_preproc(t->code);
lv_key->state = (ret==0)?LV_INDEV_STATE_REL:LV_INDEV_STATE_PR;
}
else if(t->value == 0) //released
{
ret = t->code;
lv_key->state = LV_INDEV_STATE_REL;
}
//power key is valid while key release.
if(t->code == KEY_POWER && t->value == 0){
#ifdef LVGL_MBOX_STANDBY_SUPPORT
win_open_lvmbox_standby();
lv_key->state = LV_INDEV_STATE_PR;
ret = V_KEY_POWER;
#else
enter_standby();
#endif
}
lv_key->key = keypad_key_map2_lvkey(ret);
// printf("shine = %s,%d,key_msg->key_type=%d,t->type=%d, t->code=%d, t->value=%ld,lv_key->key=%d \n",
// __func__,__LINE__,
// key_msg->key_type, t->type, t->code, t->value, lv_key->key);
return ret;
}
/*Will be called by the library to read the mouse*/
void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
/*Get whether the a key is pressed and save the pressed key*/
keypad_read_key(data);
}
//lvgl 按键初始化
int key_init(void)
{
static lv_indev_drv_t keypad_driver;
// keypad_init(); //_key_task 线程中有打开。
lv_indev_drv_init(&keypad_driver);
keypad_driver.type = LV_INDEV_TYPE_KEYPAD;
keypad_driver.read_cb = keypad_read;
indev_keypad = lv_indev_drv_register(&keypad_driver);
g = lv_group_create(); //注释也正常运行
lv_group_set_default(g);
lv_indev_set_group(indev_keypad, g);
return 0;
}
■ 按键上层处理
按键 事件 显示页面一定要有btn 才能有这个事件触发。
static void sowye_test_event_handle(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_SCREEN_LOAD_START) {
// key_set_group(sowye_test_page_g);
}else if(code == LV_EVENT_GESTURE)
{
if(++index1 >= COLORSIZE) { index1=0;}
lv_obj_set_style_bg_color(obj, color1[index1], LV_PART_MAIN | LV_STATE_DEFAULT);
}
}
static void sowye_obj_event_handle(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *target = lv_event_get_target(e);
if (code == LV_EVENT_KEY)
{
printf("%s,%d\n",__func__,__LINE__);
uint8_t key = lv_indev_get_key(lv_indev_get_act());
if (key == LV_KEY_ENTER)
{
if(++index1 >= COLORSIZE)
{
index1=0;
}
lv_obj_set_style_bg_color(obj, color1[index1], LV_PART_MAIN | LV_STATE_DEFAULT);
}
}
}
lv_obj_t* sowye_ui_test_init()
{
sowye_ui_test_page = lv_obj_create(NULL);
lv_obj_remove_style_all(sowye_ui_test_page);
lv_obj_set_size(sowye_ui_test_page,LV_PCT(100),LV_PCT(100));
lv_obj_add_event_cb(sowye_ui_test_page, sowye_test_event_handle, LV_EVENT_ALL, 0);
// 创建电池外壳
obj = lv_obj_create(sowye_ui_test_page);
lv_obj_remove_style_all(obj);
lv_obj_set_size(obj, LV_PCT(100), LV_PCT(100));
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event_cb(obj, sowye_obj_event_handle, LV_EVENT_ALL, 0);
lv_obj_set_style_bg_color(obj, color1[index1], LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(obj, 255, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_OFF); // 清除垂直和水平滚动条
//如果页面没有按钮,就不会有key事件。
btn1 = lv_btn_create(obj);
lv_obj_set_size(btn1, LV_PCT(100), LV_PCT(10));
lv_obj_align(btn1, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_add_event_cb(btn1, sowye_btn1_event_handle, LV_EVENT_ALL, 0);
return sowye_ui_test_page;
}
■ 触摸处理
void lv_port_indev_init(void)
{
/**
* Here you will find example implementation of input devices supported by LittelvGL:
* - Touchpad
* - Mouse (with cursor support)
* - Keypad (supports GUI usage only with key)
* - Encoder (supports GUI usage only with: left, right, push)
* - Button (external buttons to press points on the screen)
*
* The `..._read()` function are only examples.
* You should shape them according to your hardware
*/
static lv_indev_drv_t indev_drv;
/*------------------
* Touchpad
* -----------------*/
/*Initialize your touchpad if you have*/
touchpad_init();
/*Register a touchpad input device*/
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
static void touchpad_init(void)
{
/*Your code comes here*/
}
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static lv_coord_t last_x = 0;
static lv_coord_t last_y = 0;
/*Save the pressed coordinates and the state*/
if(touchpad_is_pressed()) {
touchpad_get_xy(&last_x, &last_y);
data->state = LV_INDEV_STATE_PR;
}else{
data->state = LV_INDEV_STATE_REL;
}
/*Set the last pressed coordinates*/
data->point.x = last_x;
data->point.y = last_y;
}
/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
CST816_Get_XY_AXIS();
(*x) = CST816_Instance.X_Pos;
(*y) = CST816_Instance.Y_Pos;
}
/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
/*Your code comes here*/
if(CST816_Get_FingerNum()!=0x00 && CST816_Get_FingerNum()!=0xFF)
{return true;}
else
{return false;}
}
/*
*********************************************************************************************************
* 函 数 名: CST816_Get_FingerNum
* 功能说明: 读取触摸屏的手指触摸个数,0xFF为睡眠
* 形 参:无
* 返 回 值: 返回芯片ID
*********************************************************************************************************
*/
uint8_t CST816_Get_FingerNum(void)
{
return CST816_IIC_ReadREG(FingerNum);
}
/*
*********************************************************************************************************
* 函 数 名: CST816_IIC_ReadREG
* 功能说明: 读取触摸屏单个寄存器的数据
* 形 参:reg:寄存器地址
* 返 回 值: 返回寄存器存储的数据
*********************************************************************************************************
*/
uint8_t CST816_IIC_ReadREG(uint8_t addr)
{
return IIC_Read_One_Byte(&CST816_dev,Device_Addr,addr);
}
unsigned char IIC_Read_One_Byte(iic_bus_t *bus, uint8_t daddr,uint8_t reg)
{
unsigned char dat;
IICStart(bus);
IICSendByte(bus,daddr<<1);
IICWaitAck(bus);
IICSendByte(bus,reg);
IICWaitAck(bus);
IICStart(bus);
IICSendByte(bus,(daddr<<1)+1);
IICWaitAck(bus);
dat = IICReceiveByte(bus);
IICSendNotAck(bus);
IICStop(bus);
return dat;
}