【esp32&lvgl】-2.6 #lvgl-多页面(screen)设定/切换

目录

一、前言

二、实现原理

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的界面把用到的字体大小激活
【esp32&lvgl】-2.6 #lvgl-多页面(screen)设定/切换_第1张图片 图一-ESP-IDF界面内激活字符大小

 另外为了便于界面内各控件的加载,记得把所有需要与输入设备绑定的控件放到一个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

Objects(对象) — 百问网LVGL中文教程文档 文档 (100ask.net)

你可能感兴趣的:(lvgl,嵌入式硬件)