基于LVGL的界面管理机制

一、LVGL介绍:
lvgl是一款轻量级的嵌入式开源界面代码,广泛用于图形化的界面操作,控件丰富。

二、资源介绍:
1.lvgl资源连接,从GitHub上可以直接搜索lvgl,可以搜索到各个版本的资源连接,连接如下:https://github.com/lvgl/lvgl
我使用的不是最新的版本,7.11.zip是我挑选的历史版本
在这里插入图片描述lvgl源码工程如图所示:源码压缩包

在这里插入图片描述在分支里选择你所需要的工程包
在这里插入图片描述
找到文件夹里的HTML,会有lvgl的所有操作
在这里插入图片描述
包括移植、控件的使用等等一系列操作。关于移植后续补充,其实没啥难度,很多地方都有介绍

2、lvgl字体转换
链接如下:https://lvgl.io/tools/fontconverter
在这里插入图片描述
你本地的字库里仿宋,微软雅黑等 ttf后缀的文件
在这里插入图片描述

在这里插入图片描述
至此就完成了汉字的生成,后续使用就可以直接用了

3、lvgl图片转换
链接如下:https://lvgl.io/tools/imageconverter
在这里插入图片描述
至此图片也可以生成了,用的时候declare一下图片即可

三、界面管理机制介绍:
基于lvgl的实现,为了方便页面的管理和实现,使用了面向对象的方式进行界面的管理和实现,方便后续作为模板化生成界面,新增界面和维护界面非常方便
1、相关技术介绍
1.1、堆栈
我们界面总共有100个左右的界面,所以界面句柄的管理使用了堆栈进行页面句柄的入栈出栈操作,源码如下

/*定义数据结构*/
#define pt_stack(T,size)\
	struct{\
		T buf[size];\
		int top;\
	}
/*初始化堆栈*/	
#define pt_stack_init()\
{.top = -1}	

#define pt_stack_len(s) (sizeof((s)->buf)/sizeof((s)->buf[0]))//<计算堆栈长度
#define pt_stack_cap(s) ((s)->top + 1)//<计算堆栈容量
#define pt_stack_empty(s) ((s)->top == -1)//<判断堆栈是否为空
#define pt_stack_reset(s) ((s)->top == -1)//<复位
#define pt_stack_full(s) ((s)->top == pt_stack_len(s) - 1)//<判断队列是否满

#define pt_stack_push(s,el)\
	(!pt_stack_empty(s) && ((s)->buf[++(s)->top] = (el),1))//<入栈
#define pt_stack_pop(s)\
	(pt_stack_empty(s) ? NULL: (s)->buf[(s)->top--])//<出栈	
#define pt_stack_peek(s)\
	(pt_stack_empty(s) ? NULL : (s)->buf[(s)->top]))//<取出栈顶元素

页面数据结构定义page.h

struct _PAGE;
struct _lv_obj_t;
struct _lv_group_t;
/**
 *@brief:页面类型
 */
typedef enum{
	GUI_NONE_PAGE_TYPE = 0,//<页面无类型
	/*常驻界面-不需要删除的*/
	LOCK_SCREEN_PAGE_TYPE,//<锁屏页
	MAIN_PAGE_TYPE,//<主运行页
	MENU_PAGE_TYPE,//<菜单页
	/*动态页面,退出后注销的,不常用的页面资源*/
}PAGE_TYPE

/**
 *@brief:页面存储类型
 */
typedef enum{
	PERMANENT_PAGE_TYPE,	//<常驻页面,静态创建,不删除
	DYNAMIC_PAGE_TYPE		//<动态页面,动态创建以及删除,有页面栈进行维护
}

/**
 *@brief:页面公共回调函数
 */
typedef struct{
	int (*tp_gui_create)(struct _PAGE *self);				//<创建页面
	int (*tp_gui_show)(struct _PAGE *self);					//<显示页面
	int (*tp_gui_hidden)(struct _PAGE *self);				//<隐藏页面
	int (*tp_gui_create_timer_task)(struct _PAGE *self);	//<创建页面后初始化
	int (*tp_gui_delete_timer_task)(struct _PAGE *self);	//<删除页面前清理
	int (*tp_gui_cleanup)(struct _PAGE *self);				//<清理页面
	int (*tp_gui_delete)(struct _PAGE *self);				//<删除页面
	int (*tp_gui_dispatch_logic_key)(struct _PAGE *self,uint8_t logic_key,uint8_t *lvgl_key);//<按键转换
}PAGE_VTABLE;

/**
 *@brief:定义页面数据结构
 */
typedef struct _PAGE{
	PAGE_VTABLE v_table;			//<页面公共回调函数
	PAGE_TYPE type;					//<页面类型
	PAGE_MEMORY_TYPE mem_type;		//<页面存储类型
	uint8_t register_flag;			//<注册标记
	uint8_t init_flag;				//<初始化标记
	uint8_t minimal_permission;		//<页面最低需求权限
	uint8_t page_building;			//<1:功能开发中
	/*lvgl核心控件*/
	struct _lv_obj_t *root;			//<根容器
	struct _lv_group_t *active_group//<活跃焦点组
	/*页脚和页眉*/
	PAGE_HEADER *active_header;
	PAGE_FOOTER *active_footer;	
}PAGE; 

页面初始化相关函数page.c

/**
 *@brief:页面初始化
 */
int page_init(PAGE *self)
{
	if(self == NULL)
		return -1;
	memset(self,0,sizeof(PAGE));
	self->v_table.tp_gui_show = __default_gui_show;
	self->v_table.tp_gui_hidden = __default_gui_hidden;
	self->v_table.tp_gui_delete = __default_gui_delete;
}


int default_gui_show(PAGE *page)
{
	__default_gui_show(page);
}
/**
 *@brief:默认显示界面
 */
static int __default_gui_show(PAGE *self)
{
	if(self == NULL)
		return -1;
	/*1.统一显示和刷新头部组件*/
	if(self->active_header && self->active_header->v_table.tp_show){
		self->active_header->v_table.tp_show(self->active_header);
	}
	if(self->active_header && self->active_header->v_table.tp_flush){
		self->active_header->v_table.tp_flush(self->active_header,NULL);
	}
	/*2.统一显示和刷新底部组件*/
	if(self->active_footer && self->active_footer->v_table.tp_show){
		self->active_footer->v_table.tp_show(self->active_footer);
	}
	if(self->active_footer && self->active_footer->v_table.tp_flush){
		self->active_footer->v_table.tp_flush(self->active_footer,NULL);
	}
	/*3.统一创建定时任务*/
	if(self->v_table.tp_gui_create_timer_task && self->init_flag){
		self->v_table.tp_gui_create_timer_task(self);
	}
	/*4.创建焦点组*/
	if(self->init_flag && self->root){
		lv_obj_set_hidden(self->root,false);
		lv_scr_load(self->root);
		if(self->active_group){
			lvgl_task_register_group(self->active_group);
		}
	}
	return 0;
}
/**
 *@brief:默认界面隐藏
 */
static int __default_gui_hidden(PAGE *self)
{
	int ret = -1;
	if(self == NULL)
		return -2;
	/*1.统一控制header组件*/
	if(self->active_header && self->active_header->v_table.tp_hidden){
		self->active_header->v_table.tp_hidden(self->active_header);
	}
	/*2.统一控制footer组件*/
	if(self->active_footer && self->active_footer->v_table.tp_hidden){
		self->active_footer->v_table.tp_hidden(self->active_footer);
	}	
	/*3.统一清除定时任务*/
	if(self->v_table.tp_gui_delete_timer_task){
		self->v_table.tp_gui_delete_timer_task(self);
	}
	/*4.清除焦点组*/
	lvgl_task_clear_group();
	
	if(self->root){
		lv_obj_set_hidden(self->root,true);
		ret = 0;
	}

}

/**
 *@brief:默认界面删除
 */
static int __default_gui_delete(PAGE *self)
{
	int ret = -1;
	if(self == NULL)
		return -2;
	/*1.统一控制header组件*/
	if(self->active_header && self->active_header->v_table.tp_delete_timer_task){
		self->active_header->v_table.tp_delete_timer_task(self->active_header);
	}
	/*2.统一控制footer组件*/
	if(self->active_footer && self->active_footer->v_table.tp_delete_timer_task){
		self->active_footer->v_table.tp_delete_timer_task(self->active_footer);
	}	
	/*3.统一清除定时任务*/
	if(self->v_table.tp_gui_delete_timer_task){
		self->v_table.tp_gui_delete_timer_task(self);
	}
	/*4.清除焦点组*/
	lvgl_task_clear_group();
	
	/*5.删除界面*/
	if(self->root){
		lv_obj_del_async(self->root);
		self->root = NULL;
	}
	return 0;
}

页面栈结构定义page_mgr.c

typedef pt_stack(PAGE*,PAGE_MGR_STACK_SZIE) LV_STACK; //<定义页面栈结构
static LV_STACK page_stack = pt_stack_init();			//<页面栈-实体
static uint8_t __page_mgr_init_flag = 0;			//<页面管理器-初始化标志
static int __page_size = 0;							//<页面数量,总共页面的数量
static PAGE *__pages[PAGE_MGR_STACK_SIZE];			//<页面缓冲区,二级指针用于存放页面栈数据
static int __page_register(void);					//<页面注册管理

/**
 *@brief:页面注册管理初始化
 */
int page_mgr_init(void)
{
	if(__page_mgr_init_flag == 0){
		__page_size  = 0;										//< 页面管理数量0
		memset(__pages,0,PAGE_MGR_STACK_SIZE * sizeof(PAGE));	//< 初始化为所有页面栈为0
		__page_mgr_init_flag = 1;
		__page_register();//<页面注册管理
	}
	return 0;
}

/**
 *@brief:通过页面类型查找页面
 *@param:PAGE_TYPE 页面类型
 *@ret:PAGE:当前活跃页
 */
PAGE *page_mgr_lookup_by_type(PAGE_TYPE type)
{
	PAGE *page = NULL;
	for(int i = 0; i < PAGE_MGR_STACK_SIZE; i++){
		page = __pages[i];
		if((page->register_flag) == 1 && (page->type) == type){
			return page;
		}
	}
	return NULL;
}
/**
 *@brief:查找栈顶页面
 *@ret:PAGE 栈顶页面
 */
PAGE *page_mgr_lookup_active_page(void)
{
	return page_mgr_lookup_check_active_page(GUI_NONE_PAGE_TYPE);
}

/**
 *@brief:根据类型查找栈顶页面,主动调用的,在当前页面取出页面的句柄
 *@param:PAGE_TYPE 页面类型
 *@ret:返回栈顶页面
 */
PAGE *page_mgr_lookup_check_active_page(PAGE_TYPE type)
{
	PAGE **page = pt_stack_peek(&page_stack);
	if(__page_mgr_init_flag == 0)
		return NULL;
	if(*page == NULL)
		return NULL;
	if(type != GUI_NONE_PAGE_TYPE){
		if((*page)->type != type){
			return NULL;
		}
	}
	return *page;
}
/**
 *@brief:打开一个新页面
 *@ret:返回码
 */
int page_mgr_open_new(PAGE_TYPE type)
{
	int ret = -1;
	PAGE *page = page_mgr_lookup_by_type(type);
	if(page == NULL){
		return -1;
	}
	PAGE **cur_page = NULL;
	cur_page = pt_stack_peek(&page_stack);					//<取当前栈顶页面
	if(cur_page){
		if((*cur_page)->type == type){						//<表示栈顶页面就是当前页面,可以直接不打开,页面去重
			return -2;
		}
	}
	if(page->init_flag == 0){								//<如果页面不存在,创建一个新的页面
		if(page->v_table.tp_gui_create){
			ret = page->v_table.tp_gui_create(page);		//<创建一个新的页面
		}
	}else{													//<不需要动态创建
		ret = 0;
	}
	if(ret == 0){											//表示可以显示
		page->init_flag = 1;
		if(cur_page && (*cur_page)->v_table.tp_gui_hidden){	//<当前栈顶旧页面隐藏
			ret = (*cur_page)->v_table.tp_gui_hidden(*cur_page);
		}
		pt_stack_push(&page_stack,page);					//<新页面入栈
		
		if(page->v_table.tp_gui_show){						//<新界面显示
			ret = page->v_table.tp_gui_show(page);
		}
	}
	return ret;
}
/**
 *@brief:返回上一个界面
 *@ret:状态返回值
 */
int page_mgr_back_open(void)
{
	int ret = 0;
	PAGE **cur_page = NULL;
	PAGE **pre_page = NULL;
	if(__page_mgr_init_flag == 0){//<如果没有页面初始化,直接返回
		return -1;
	}
	taskENTER_CRITICAL();
	/*取出来一个栈页面*/
	cur_page = pt_stack_pop(&page_stack);
	if(cur_page == NULL){
		ret = -1;
		goto clean;
	}
	/*取出当前栈顶元素*/
	pre_page = pt_stack_peek(&page_stack);
	if(pre_page == NULL){
		ret = -2;
		goto clean;
	}
	/*判断当前取出来的页面是常驻的还是动态的,静态的就隐藏,动态的就删除*/
	if((*cur_page)->mem_type == PERMANENT_PAGE_TYPE){//<如果是常驻界面隐藏掉
		if((*cur_page)->v_table.tp_gui_hidden){//<如果有隐藏回调,直接隐藏
			(*cur_page)->v_table.tp_gui_hidden(*cur_page);
		}
	}else{//<动态页面直接删除掉
		if((*cur_page)->v_table.tp_gui_cleanup){
			(*cur_page)->v_table.tp_gui_cleanup(*cur_page);
		}
		if((*cur_page)->v_table.tp_gui_delete){
			(*cur_page)->v_table.tp_gui_delete(*cur_page);
		}
		(*cur_page)->init_flag = 0;//<删除了页面
	}
	if((*pre_page)->v_table.tp_gui_show){
		(*pre_page)->v_table.tp_gui_show(*pre_page);
	}
clean:	
	taskEXIT_CRITICAL();
	return ret;
}


/**
 *@brief:跳转到指定类型的页面,从页面管理栈中查找
 *@param:type页面类型
 *@ret: 0成功 其他:失败
 */
int page_mgr_back_to_target_page(PAGE_TYPE type)
{
	int ret = 0;
	PAGE *page = NULL;
	PAGE **pr_page = NULL;//<寻找页面栈指针
	char page_exist_flag = 0;//<页面类型是否在页面栈中
	if(__page_mgr_init_flag == 0){
		return -1;
	}
	taskENTER_CRITICAL();
	/*1.遍历页面栈中是否存在当前类型的页面*/
	for(uint8_t i = 0; i <= page_stack.top; ++i){
		if(page_stack.buf[i]->type == type){
			page_exist_flag = 1;
			break;
		}
	}
	if(page_exist_flag == 0){//<表示页面不存在.创建一个新页面
		page_mgr_open_new(type);
		ret = 0;
		goto clean;
	}
	/*2.遍历页面栈,清除相关的页面资源直至到指定页面类型栈位置*/
	while((pr_page = pt_stack_pop(&page_stack)) != NULL){
		page = *pr_page;
		if(page->type == type){
			ret = 0;
			break;
		}
		/*2.1 隐藏常驻界面元素*/
		if(page->mem_type == PERMANENT_PAGE_TYPE){
			if(page->v_table.tp_gui_hidden){
				page->v_table.tp_gui_hidden(page);
			}
		}else{
		/*2.2 删除动态界面元素*/
			if(page->v_table.tp_gui_cleanup){
				page->v_table.tp_gui_cleanup(page);
			}
			if(page->v_table.tp_gui_delete){
				page->v_table.tp_gui_delete(page);
			}
		}
	}
	/*3.指定类型页面入栈,显示*/
	if(ret == 0){
		pt_stack_push(&page_stack,page);//<指定页面入栈
		if(page->v_table.tp_gui_show){//<显示
			page->v_table.tp_gui_show(page);
		}
	}
clean:	
	taskEXIT_CRITICAL();
}

/**
 *@brief:跳转到主运行页
 */
int page_mgr_back_to_main_page(void)
{
	return page_mgr_back_to_target_page(MAIN_PAGE_TYPE);
}

/**
 *@brief:跳转到菜单页
 */
int page_mgr_back_to_menu_page(void)
{
	return page_mgr_back_to_target_page(MENU_PAGE_TYPE);
}

/**
 *@brief:跳转到锁屏页
 */
int page_mgr_back_to_lockscreen_page(void)
{
	return page_mgr_back_to_target_page(LOCK_SCREEN_PAGE_TYPE);
}


/**
 *@brief:页面管理分发key
 *@param:
	-#1:logic_key 物理key
	-#2:lvgl_key lvgl key
 */
int page_mgr_dispatch_logic_key(uint8_t logic_key,uint8_t *lvgl_key)
{
	int ret = -1;
	PAGE *page = page_mgr_lookup_active_page();//<取栈顶页面
	if(page == NULL){
		return -2;
	}
	if(page->init_flag && page->v_table.tp_gui_dispatch_logic_key){
		ret = page->v_table.tp_gui_dispatch_logic_key(page,logic_key,lvgl_key);
	}
	return ret;
}

#include "gui_lock_screen_page.h"
//<一系列页面头文件
/**
 *
 *
 */
static int __page_register(void)
{
	/*屏保界面*/
	static LOCK_SCREEN_PAGE __lock_screen_page;
	lock_screen_page_init(&__lock_screen_page);
	__pages[__page_size++] = (PAGE *)&__lock_screen_page;
    // 主运行界面
    static MAIN_PAGE __main_page;
    main_page_init(&__main_page);
    __pages[__page_size++] = (PAGE*)&__main_page;
    
    // 主运行界面
    static MENU_PAGE __menu_page;
    menu_page_init(&__menu_page);
    __pages[__page_size++] = (PAGE*)&__menu_page;	
    
    // 系统查询-组网信息界面          
    DEVICE_RECORD_PAGE *__device_record_page = MALLOC(sizeof(DEVICE_RECORD_PAGE));
    device_record_page_init(__device_record_page);
    __pages[__page_size++] = (PAGE*)__device_record_page;
    
    // 系统查询-接口信息界面              
    INTERFACE_INFO_PAGE *__interface_info_page = MALLOC(sizeof(INTERFACE_INFO_PAGE));
    interface_info_page_init(__interface_info_page);
    __pages[__page_size++] = (PAGE*)__interface_info_page;
    
    // 系统查询-联动程序-菜单界面       
    PROGRAM_MENU_PAGE *__program_menu_page = MALLOC(sizeof(PROGRAM_MENU_PAGE));
    program_menu_page_init(__program_menu_page);
    __pages[__page_size++] = (PAGE*)__program_menu_page;

	//<新增页面即可
}

未完待续...

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值