目录
一、前言
二、实现原理
2.1 各个screen的定义及初始化
2.2 各个screen内的内容绘制
2.3 页面切换的events_handler
三、代码实现
3.1 PageManage库
3.2 Page_mainMenu库(界面)
3.3 Page_wifi库(界面)
参考文献
一、前言
利用lvgl框架绘制GUI免不了需要实现多个页面的切换,毕竟把所有功能和接口都放在一页上有些不太优雅,而且对于嵌入式硬件的小屏幕也有些过于困难。因此这里就需要实现多个页面(或者说lvgl里的screen)及其互相切换。
二、实现原理
在lvgl中实现多个页面间的切换有多种方案,已经有大佬简单总结过:
- 通过LV_OBJ_FLAG_HIDDEN隐藏属性实现页面切换
- 在回调函数中,创建新窗口并删除旧窗口
- 使用函数lv_scr_load_anim和lv_scr_load加载和切换屏幕
在本文中,个人使用了第三种方案,但是相比较于上述文章中大佬的代码有一些改动,参考了lvgl官方的lv_demo_keypad_encoder代码,将各个环节的耦合性降低,模块化程度提升方便后续拓展。 这一方案主要包括三个部分:
- 各个screen的定义及初始化
- 各个screen内的内容绘制
- 页面切换的events_handler
2.1 各个screen的定义及初始化
在lvgl中screen就是没有父对象的特殊obj,可以通过
lv_obj_t * scr1 = lv_obj_create(NULL);
创建。基于此,我们可以创建多块屏幕作为不同界面的容器。当然由于没有通过默认的屏幕lv_scr_act()作为父类继承屏幕的属性,这里需要详细设定屏幕的属性(主要是屏幕的尺寸等)。此外在完成各screen的定义后记得把默认界面及其内的各控件加载上(指将各控件加入group和输入设备联系上)。
static void scrInit(){
Scr_mainMenu = lv_obj_create(NULL);
lv_obj_clean(Scr_mainMenu);
lv_obj_remove_style_all(Scr_mainMenu);
lv_obj_set_style_bg_opa(Scr_mainMenu, LV_OPA_COVER, 0);//0不透明
lv_obj_set_style_bg_color(Scr_mainMenu, lv_color_black(), 0);
lv_obj_set_size(Scr_mainMenu, LV_HOR_RES, LV_VER_RES);
Scr_wifi = lv_obj_create(NULL);
lv_obj_clean(Scr_wifi);
lv_obj_remove_style_all(Scr_wifi);
lv_obj_set_style_bg_opa(Scr_wifi, LV_OPA_COVER, 0);//0不透明
lv_obj_set_style_bg_color(Scr_wifi, lv_color_black(), 0);
lv_obj_set_size(Scr_wifi, LV_HOR_RES, LV_VER_RES);
lv_scr_load(Scr_mainMenu);
objsLoad(Scr_mainMenu);
}
screen的加载只需要使用lv_scr_load即可实现加载界面。但是对于界面内各控件的加载就需要专门写一个函数来实现了,即objsLoad函数。这个函数可以调用个界面内的控件组成的struct,循环加入group,具体如何实现这一struct可以参看2.2.
static void objsLoad(lv_obj_t * Scr){
size_t size = 0;
lv_obj_t ** objs = NULL;
if(Scr == getScr_wifi()){
size = sizeof(wifi_objs);
objs = (lv_obj_t**) &wifi_objs;
}
if(Scr == getScr_mainMenu()){
size = sizeof(mainMenu_objs);
objs = (lv_obj_t**) &mainMenu_objs;
}
for(uint32_t i = 0; i < size / sizeof(lv_obj_t *); i++) {
if(objs[i] == NULL) continue;
lv_group_add_obj(group_Default, objs[i]);
}
}
2.2 各个screen内的内容绘制
内容绘制就比较容易理解了,按照界面上所需的内容依次绘制即可。这里根据我的使用需求我在界面1画了几个按钮,其中一个界面是进入另一个界面的入口。详细代码看第三章,这里不再赘述。简单说两个绘制界面中遇到的问题:
- 各style最好在创建obj之前一并进行设定好,否则可能会出现style显示错误的情况
- label 上的字体(包括符号)大小需要去espidf的界面把用到的字体大小激活
另外为了便于界面内各控件的加载,记得把所有需要与输入设备绑定的控件放到一个struct内便于调用,这里参考了lvgl的官方lv_demo_keypad_encoder代码。
struct {
lv_obj_t * btn_wifi;
lv_obj_t * btn_setting;
lv_obj_t * btn_crctrl;
}mainMenu_objs; //需要后续操作的界面内的控件
2.3 页面切换的events_handler
页面切换的管理通过一个回调函数实现:
void event_handler_swtichPage(lv_event_t * e){
lv_obj_t * next_page = lv_event_get_user_data(e);
lv_group_remove_all_objs(group_Default);
lv_scr_load_anim(next_page, LV_SCR_LOAD_ANIM_FADE_ON, 10, 0, false);
objsLoad(next_page);
}
先把group内的各个组件删去,然后加载目标界面(通过传参方式实现)然后把目标界面的各个组件加载进group内。 传参的方式如下:
lv_obj_add_event_cb(mainMenu_objs.btn_wifi, event_handler_swtichPage, LV_EVENT_CLICKED, Scr_wifi);
三、代码实现
3.1 PageManage库
包含各个页面的设定及初始化,页面切换的回调函数。
PageManage.h
#ifndef PAGEMANAGE_H
#define PAGEMANAGE_H
/*********************
* INCLUDES
*********************/
#include "lvgl.h"
#include "PageManage.h"
#include "Page_wifi.h"
#include "Page_mainMenu.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_obj_t *Scr_mainMenu;
lv_obj_t *Scr_wifi;
void PageManage_Init();
void event_handler_swtichPage(lv_event_t * e);
/**********************
* MACROS
**********************/
#endif
PageManage.c
/*********************
* INCLUDES
*********************/
#include "PageManage.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void scrInit();
static void objsLoad(lv_obj_t * Scr);//???通过设定自定义类或者什么统一页面的管理
/**********************
* STATIC VARIABLES
**********************/
static lv_group_t* group_Default;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void PageManage_Init(){
group_Default = lv_group_get_default();
Scr_mainMenu = lv_obj_create(NULL);
Scr_wifi = lv_obj_create(NULL);
scrInit();
}
void event_handler_swtichPage(lv_event_t * e){
lv_obj_t * next_page = lv_event_get_user_data(e);
lv_group_remove_all_objs(group_Default);
lv_scr_load_anim(next_page, LV_SCR_LOAD_ANIM_FADE_ON, 10, 0, false);
objsLoad(next_page);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void scrInit(){
lv_obj_clean(Scr_mainMenu);
lv_obj_remove_style_all(Scr_mainMenu);
lv_obj_set_style_bg_opa(Scr_mainMenu, LV_OPA_COVER, 0);//0不透明
lv_obj_set_style_bg_color(Scr_mainMenu, lv_color_black(), 0);
lv_obj_set_size(Scr_mainMenu, LV_HOR_RES, LV_VER_RES);
lv_obj_clean(Scr_wifi);
lv_obj_remove_style_all(Scr_wifi);
lv_obj_set_style_bg_opa(Scr_wifi, LV_OPA_COVER, 0);//0不透明
lv_obj_set_style_bg_color(Scr_wifi, lv_color_black(), 0);
lv_obj_set_size(Scr_wifi, LV_HOR_RES, LV_VER_RES);
lv_scr_load(Scr_mainMenu);
objsLoad(Scr_mainMenu);
}
static void objsLoad(lv_obj_t * Scr){
size_t size = 0;
lv_obj_t ** objs = NULL;
if(Scr == Scr_wifi){
size = sizeof(wifi_objs);
objs = (lv_obj_t**) &wifi_objs;
}
if(Scr == Scr_mainMenu){
size = sizeof(mainMenu_objs);
objs = (lv_obj_t**) &mainMenu_objs;
}
for(uint32_t i = 0; i < size / sizeof(lv_obj_t *); i++) {
if(objs[i] == NULL) continue;
lv_group_add_obj(group_Default, objs[i]);
}
}
3.2 Page_mainMenu库(界面)
Page_mainMenu.h
#ifndef PAGE_MAINMENU_H
#define PAGE_MAINMENU_H
/*********************
* INCLUDES
*********************/
#include "lvgl.h"
#include "config.h"
#include "PageManage.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
struct {
lv_obj_t * btn_wifi;
lv_obj_t * btn_setting;
lv_obj_t * btn_crctrl;
}mainMenu_objs; //需要后续操作的界面内的控件
/**
* @brief 导入mainMenu界面
*
*/
void mainMenu();
/**********************
* MACROS
**********************/
#endif
Page_mainMenu.c
/*********************
* INCLUDES
*********************/
#include "Page_mainMenu.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* @brief 初始化main menu用到的lv_style
*
*/
static void styleInit();
/**********************
* STATIC VARIABLES
**********************/
/*lv_style*/
static lv_style_t style_btn_wifi;
static lv_style_t style_btn_wifi_focused;
static lv_style_t style_btn_wifi_pressed;
static lv_style_t style_btn_setting;
static lv_style_t style_btn_setting_focused;
static lv_style_t style_btn_setting_pressed;
static lv_style_t style_btn_crctrl;
static lv_style_t style_btn_crctrl_focused;
static lv_style_t style_btn_crctrl_pressed;
/*lv_opa*/
static lv_opa_t opa_default = LV_OPA_COVER;
static lv_opa_t opa_focused = LV_OPA_60;
static lv_opa_t opa_pressed = LV_OPA_20;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void mainMenu(){
/*style初始化*/
styleInit();
/*屏幕*/
lv_obj_t *scr = Scr_mainMenu;
/*logo*/
lv_obj_t *img1 = lv_img_create(scr);
lv_img_set_src(img1,"S:/image/tju_120.bin");
lv_obj_align(img1, LV_ALIGN_TOP_MID, 0, 30);
/*buton*/
//-----create buton-wifi-----//
//创建btn对象
mainMenu_objs.btn_wifi = lv_btn_create(scr);
lv_obj_remove_style_all(mainMenu_objs.btn_wifi);
//修改尺寸&位置
lv_obj_set_size(mainMenu_objs.btn_wifi, 75, 75);
lv_obj_align(mainMenu_objs.btn_wifi, LV_ALIGN_BOTTOM_LEFT, 5, -5);
//把style附加到btn对象
lv_obj_add_style(mainMenu_objs.btn_wifi, &style_btn_wifi,LV_STATE_DEFAULT);
lv_obj_add_style(mainMenu_objs.btn_wifi, &style_btn_wifi_focused,LV_STATE_FOCUSED);
lv_obj_add_style(mainMenu_objs.btn_wifi, &style_btn_wifi_pressed,LV_STATE_PRESSED);
//增加label
lv_obj_t *wifi_label = lv_label_create(mainMenu_objs.btn_wifi);
lv_label_set_text(wifi_label, LV_SYMBOL_WIFI);
lv_obj_set_style_text_font(wifi_label, &lv_font_montserrat_30, 0);
lv_obj_set_style_text_color(wifi_label, lv_color_white(), 0);
lv_obj_center(wifi_label);
//按键触发事件
lv_obj_add_event_cb(mainMenu_objs.btn_wifi, event_handler_swtichPage, LV_EVENT_CLICKED, Scr_wifi);
//-----create setting btn-----//
//创建btn对象
mainMenu_objs.btn_setting = lv_btn_create(scr);
lv_obj_remove_style_all(mainMenu_objs.btn_setting);
//修改尺寸&位置
lv_obj_set_size(mainMenu_objs.btn_setting, 75, 75);
lv_obj_align(mainMenu_objs.btn_setting, LV_ALIGN_BOTTOM_RIGHT, -5, -5);
//把style附加到btn对象
lv_obj_add_style(mainMenu_objs.btn_setting, &style_btn_setting,LV_STATE_DEFAULT);
lv_obj_add_style(mainMenu_objs.btn_setting, &style_btn_setting_focused,LV_STATE_FOCUSED);
lv_obj_add_style(mainMenu_objs.btn_setting, &style_btn_setting_pressed,LV_STATE_PRESSED);
//增加label
lv_obj_t *setting_label = lv_label_create(mainMenu_objs.btn_setting);
lv_label_set_text(setting_label , LV_SYMBOL_SETTINGS);
lv_obj_set_style_text_font(setting_label , &lv_font_montserrat_30, 0);
lv_obj_set_style_text_color(setting_label , lv_color_white(), 0);
lv_obj_center(setting_label);
//-----create crctrl btn-----/
//创建btn对象
mainMenu_objs.btn_crctrl = lv_btn_create(scr);
lv_obj_remove_style_all(mainMenu_objs.btn_crctrl);
//修改尺寸&位置
lv_obj_set_size(mainMenu_objs.btn_crctrl, 160, 60);
lv_obj_align(mainMenu_objs.btn_crctrl, LV_ALIGN_CENTER, 0, 40);
//把style附加到btn对象
lv_obj_add_style(mainMenu_objs.btn_crctrl, &style_btn_crctrl,LV_STATE_DEFAULT);
lv_obj_add_style(mainMenu_objs.btn_crctrl, &style_btn_crctrl_focused,LV_STATE_FOCUSED);
lv_obj_add_style(mainMenu_objs.btn_crctrl, &style_btn_crctrl_pressed,LV_STATE_PRESSED);
//增加label
lv_obj_t *crctrl_label = lv_label_create(mainMenu_objs.btn_crctrl);
lv_label_set_text(crctrl_label , "CRCTRL");
lv_obj_set_style_text_font(crctrl_label , &lv_font_montserrat_30, 0);
lv_obj_set_style_text_color(crctrl_label , lv_color_white(), 0);
lv_obj_center(crctrl_label);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void styleInit(){
//-----style-btn-wifi-----//
//style-默认
lv_style_init(&style_btn_wifi);
lv_style_set_radius(&style_btn_wifi,10);
lv_style_set_bg_color(&style_btn_wifi,lv_color_hex(MY_COLOR_WIFI_BTN));
lv_style_set_bg_opa(&style_btn_wifi,opa_default);
//style-选中
lv_style_init(&style_btn_wifi_focused);
lv_style_set_radius(&style_btn_wifi_focused,10);
lv_style_set_bg_color(&style_btn_wifi_focused,lv_color_hex(MY_COLOR_WIFI_BTN));
lv_style_set_bg_opa(&style_btn_wifi_focused,opa_focused);
//style-点击
lv_style_init(&style_btn_wifi_pressed);
lv_style_set_radius(&style_btn_wifi_pressed,10);
lv_style_set_bg_color(&style_btn_wifi_pressed,lv_color_hex(MY_COLOR_WIFI_BTN));
lv_style_set_bg_opa(&style_btn_wifi_pressed,opa_pressed);
//-----style-btn-setting-----//
//style-默认
lv_style_init(&style_btn_setting);
lv_style_set_radius(&style_btn_setting,10);
lv_style_set_bg_color(&style_btn_setting,lv_color_hex(MY_COLOR_SETTING_BTN));
lv_style_set_bg_opa(&style_btn_setting,opa_default);
//style-选中
lv_style_init(&style_btn_setting_focused);
lv_style_set_radius(&style_btn_setting_focused,10);
lv_style_set_bg_color(&style_btn_setting_focused,lv_color_hex(MY_COLOR_SETTING_BTN));
lv_style_set_bg_opa(&style_btn_setting_focused,opa_focused);
//style-点击
lv_style_init(&style_btn_setting_pressed);
lv_style_set_radius(&style_btn_setting_pressed,10);
lv_style_set_bg_color(&style_btn_setting_pressed,lv_color_hex(MY_COLOR_SETTING_BTN));
lv_style_set_bg_opa(&style_btn_setting_pressed,opa_pressed);
//-----style-btn-crctrl-----//
//style-默认
lv_style_init(&style_btn_crctrl);
lv_style_set_radius(&style_btn_crctrl,10);
lv_style_set_bg_color(&style_btn_crctrl,lv_color_hex(MY_COLOR_CRCTRL_BTN));
lv_style_set_bg_opa(&style_btn_crctrl,opa_default);
//style-选中
lv_style_init(&style_btn_crctrl_focused);
lv_style_set_radius(&style_btn_crctrl_focused,10);
lv_style_set_bg_color(&style_btn_crctrl_focused,lv_color_hex(MY_COLOR_CRCTRL_BTN));
lv_style_set_bg_opa(&style_btn_crctrl_focused,opa_focused);
//style-点击
lv_style_init(&style_btn_crctrl_pressed);
lv_style_set_radius(&style_btn_crctrl_pressed,10);
lv_style_set_bg_color(&style_btn_crctrl_pressed,lv_color_hex(MY_COLOR_CRCTRL_BTN));
lv_style_set_bg_opa(&style_btn_crctrl_pressed,opa_pressed);
}
3.3 Page_wifi库(界面)
Page_wifi.h
#ifndef PAGE_WIFI_H
#define PAGE_WIFI_H
/*********************
* INCLUDES
*********************/
#include "lvgl.h"
#include "config.h"
#include "PageManage.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
struct {
lv_obj_t * btn_back;
}wifi_objs;
/**
* @brief 导入mainMenu界面
*
*/
void wifiPage_Init();
/**********************
* MACROS
**********************/
#endif
Page_wifi.c
/*********************
* INCLUDES
*********************/
#include "Page_wifi.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**
* @brief 初始化main menu用到的lv_style
*
*/
static void styleInit();
/**********************
* STATIC VARIABLES
**********************/
/*lv_style*/
static lv_style_t style_btn_back;
static lv_style_t style_btn_back_focused;
static lv_style_t style_btn_back_pressed;
/*lv_opa*/
static lv_opa_t opa_default = LV_OPA_COVER;
static lv_opa_t opa_focused = LV_OPA_60;
static lv_opa_t opa_pressed = LV_OPA_20;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void wifiPage_Init(){
/*style初始化*/
styleInit();
/*屏幕*/
lv_obj_t *scr = Scr_wifi;
/*buton*/
//-----create buton-wifi-----//
//创建btn对象
wifi_objs.btn_back = lv_btn_create(scr);
lv_obj_remove_style_all(wifi_objs.btn_back);
//修改尺寸&位置
lv_obj_set_size(wifi_objs.btn_back, 75, 75);
lv_obj_align(wifi_objs.btn_back, LV_ALIGN_CENTER, 0, 0);
//把style附加到btn对象
lv_obj_add_style(wifi_objs.btn_back, &style_btn_back,LV_STATE_DEFAULT);
lv_obj_add_style(wifi_objs.btn_back, &style_btn_back_focused,LV_STATE_FOCUSED);
lv_obj_add_style(wifi_objs.btn_back, &style_btn_back_pressed,LV_STATE_PRESSED);
//增加label
lv_obj_t *back_label = lv_label_create(wifi_objs.btn_back);
lv_label_set_text(back_label, LV_SYMBOL_LEFT);
lv_obj_set_style_text_font(back_label, &lv_font_montserrat_30, 0);
lv_obj_set_style_text_color(back_label, lv_color_white(), 0);
lv_obj_center(back_label);
//按键触发事件
lv_obj_add_event_cb(wifi_objs.btn_back, event_handler_swtichPage, LV_EVENT_CLICKED, Scr_mainMenu);
}
/**********************
* STATIC FUNCTIONS
**********************/
static void styleInit(){
//-----style-btn-back-----//
//style-默认
lv_style_init(&style_btn_back);
lv_style_set_radius(&style_btn_back,10);
lv_style_set_bg_color(&style_btn_back,lv_color_hex(MY_COLOR_WIFI_BTN));
lv_style_set_bg_opa(&style_btn_back,opa_default);
//style-选中
lv_style_init(&style_btn_back_focused);
lv_style_set_radius(&style_btn_back_focused,10);
lv_style_set_bg_color(&style_btn_back_focused,lv_color_hex(MY_COLOR_WIFI_BTN));
lv_style_set_bg_opa(&style_btn_back_focused,opa_focused);
//style-点击
lv_style_init(&style_btn_back_pressed);
lv_style_set_radius(&style_btn_back_pressed,10);
lv_style_set_bg_color(&style_btn_back_pressed,lv_color_hex(MY_COLOR_WIFI_BTN));
lv_style_set_bg_opa(&style_btn_back_pressed,opa_pressed);
}
参考文献
(25条消息) LVGL V8应用——通过按键切换页面_临界msp的博客-CSDN博客_lvgl页面切换
https://github.com/lvgl/lv_demos/tree/3f41b7d18ec0bf55edb80058c7486727bbbd8a95/src/lv_demo_keypad_encoder文章来源:https://www.toymoban.com/news/detail-782682.html
Objects(对象) — 百问网LVGL中文教程文档 文档 (100ask.net)文章来源地址https://www.toymoban.com/news/detail-782682.html
到了这里,关于【esp32&lvgl】-2.6 #lvgl-多页面(screen)设定/切换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!