1:UI框架简介
除了服务框架外,我们还需要对外显示UI,所以我们就需要一个UI的框架,跟服务框架一样,不用这个UI框架我们也是可以实现,但是这样每个人写的UI都会有差异,需要的事件,数据都是独立的,重复的代码也多。但是有UI框架之后就比较方便;例如每个界面都是识别到按键(以手表为例子)有框架直接 case:SINGLE_CLICK,即可,需要什么事件叫框架帮忙统一添加处理即可。除了负责事件的分发、还有就是UI界面的切换、动效等。
1.1:UI框架简流程
图1.1.1
如图1.1.1所示UI框架就是负责分发UI的事件,然后负责显示,UI直接的切换动画。UI的事件分发跟服务框架的设计是一样的、UI的显示需要底层去实现有用mipi、qspi、spi的接口的显示器。动画的切换的话需要自己进行代码逻辑的编写,还得引入第三方的算法如动画曲线(贝塞尔曲线)。
2:代码
2.1 窗口的数据结构:
typedef struct _gui_win_t {
#if GUI_WIN_USE_GRAPHIC_ENGINE == GUI_WIN_GRAPHIC_ENGINE_GUIX
GX_WIDGET *win; /** window handle */
GX_STUDIO_WIDGET *studio_win; /** studio window handle */
GUI_WIN_REG_TYPE_E reg_type; /** registration type */
#endif
GUI_WIN_PAGE_PRIO_E win_prio; /** page priority */
char *win_name; /** window name */
uint8_t win_type; /** window type */
uint8_t notify_disp; /** notify display type */
/** window state */
uint32_t disp_sec; /** display time */
uint32_t idel_exit_sec; /** idle exit time */
uint32_t (*init)(struct _gui_win_t *win); /** window init */
uint32_t (*deinit)(struct _gui_win_t *win); /** window deinit */
uint32_t (*event)(struct _gui_win_t *win, gui_event_t *event); /** window event handler */
} gui_win_t;
这个GUI_WIN_USE_GRAPHIC_ENGINE宏控制着使用着不同的GUI平台。重点就是窗口的init、deinit、event。这个控制了窗口的初始化、反初始化,还有就是给窗口发送的事件函数处理。
_gui_win_t 类型根据不同的GUI平台,是不一样的,可自行进行构造。
2.2 gui 平台初始化
void gui_win_guix_init(void)
{
/* Create all windows */
for (int i = 0; i < GUI_WIN_ID_MAX; i++) {
if (gui_win_table[i].win_name == NULL) {
continue;
}
if (gui_win_table[i].reg_type == GUI_WIN_REG_SOFT_AUTO_REG) {
UINT status = gx_studio_named_widget_create(gui_win_table[i].win_name, GX_NULL, GX_NULL);
if (status != GX_SUCCESS) {
log_e("Create window %s failed(%d)", gui_win_table[i].win_name, status);
return;
}
} else if (gui_win_table[i].reg_type == GUI_WIN_REG_SOFT_MANUAL_REG) {
GX_WIDGET *studio_win = gx_studio_widget_create((GX_BYTE *)gui_win_table[i].win, gui_win_table[i].studio_win, GX_NULL);
if (studio_win == GX_NULL) {
log_e("Create window %s failed(%d)", gui_win_table[i].win_name, status);
return;
}
}
}
/** GUI animation init */
gui_win_guix_anim_init();
}
上述的初始化就是根据不同的UI平台进行创建的,这个可根据实际的来进行。
2.3 窗口配置
const gui_win_t gui_win_table[GUI_WIN_ID_MAX] = {
[GUI_WIN_ID_WELCOME] = {
.win = (GX_WIDGET*)&welcome_screen,
.win_name = "welcome_screen",
.win_type = GUI_WIN_TYPE_APP,
.notify_disp = GUI_WIN_NOTIFY_DISP_NONE,
.disp_sec = GUI_WIN_DISP_SEC_KEEP_BRIGHT,
.idel_exit_sec = GUI_WIN_IDEL_EXIT_SEC_KEEP_NOT_QUIT,
.init = gui_win_welcome_init,
.deinit = gui_win_welcome_deinit,
.event = gui_win_welcome_event,
},
};
.win:根据不同的GUI平台,类型都是不一样的,但是都是可以当做一个obj的对象。
.init = gui_win_welcome_init
.deinit = gui_win_welcome_deinit
.event = gui_win_welcome_event
上述的这个三个配置是建议都要配置的,不要填NULL,如果选用的GUI平台自带了的话,那就可以不用写,没有的话就要填写。
.win_name
.win_type
.notify_disp
.disp_sec
.idel_exit_sec
上述这几个参数可进行选配的不是必选的。
除了上述的这几个配置,还可以引入APP的概念,一个功能有可能会有几个界面,当退出一个功能的时候,只需要发起退出某个“APP”就可以把相关联的win id都退了。
窗口的注册还可以写成一个宏,在自己的win screen 里面进行注册,窗口就不需要修改同一个文件,就像rtos创建任务一样。
2.4 进入窗口
uint32_t gui_win_enter(GUI_WIN_ID win_id, GUI_WIN_ANIM_TYPE_E anim)
{
GUI_WIN_ID cur_id = gui_win_get_active_win_id();
GUI_WIN_TYPE_E cur_win_type = gui_win_table[cur_id].win_type;
if (win_id >= GUI_WIN_ID_MAX) {
log_e("win id is invalid");
return GUI_WIN_RET_PARAM_ERR;
}
if (cur_id == win_id) {
log_w("win %d is already active", win_id);
return GUI_WIN_RET_OK;
}
log_d("cur win prio: %d, win prio: %d", gui_win_table[cur_id].win_prio,
gui_win_table[win_id].win_prio);
/** Check the window priority */
if (g_win_manager.stack_depth >= 1 &&
gui_win_table[cur_id].win_prio > gui_win_table[win_id].win_prio) {
if (gui_win_table[win_id].init) {
gui_win_table[win_id].init(gui_win_id_get_win(win_id));
}
/***********************Add your own code*****************************/
...
} else {
/** Enter the page to call the init function of the page */
if (gui_win_table[win_id].init) {
gui_win_table[win_id].init(gui_win_id_get_win(win_id));
}
/***********************Add your own code*****************************/
...
#if GUI_WIN_USE_GRAPHIC_ENGINE == GUI_WIN_GRAPHIC_ENGINE_GUIX
/** Add a window to the window stack list */
#endif
}
return GUI_WIN_RET_OK;
}
2.5 退出窗口
uint32_t gui_win_exit(GUI_WIN_ID win_id, GUI_WIN_ANIM_TYPE_E anim)
{
if (win_id >= GUI_WIN_ID_MAX) {
log_e("win id is invalid");
return GUI_WIN_RET_PARAM_ERR;
}
/***********************Add your own code*****************************/
...
#if GUI_WIN_USE_GRAPHIC_ENGINE == GUI_WIN_GRAPHIC_ENGINE_GUIX
if (g_win_manager.stack_depth == 0) {
log_w("stack depth is 0");
return GUI_WIN_RET_PARAM_ERR;
}
/** Remove a window from the window stack list */
gui_list_stack_remove_win(win_id);
/** Exit the page and call the deinit function of the page */
if (gui_win_table[win_id].deinit) {
gui_win_table[win_id].deinit(gui_win_id_get_win(win_id));
}
#endif
return GUI_WIN_RET_OK;
}
3:结论
上述的只是提供了一个简单的窗口管理思路,统一管理系统事件的发送到顶层的窗口、进入退出的函数处理。这个思路跟另一篇文章简单的server框架差不多。
UI框架的还有一个重要的核心就是使用的UI系统的适配(lvgl等)把这个系统接入进行,并实现窗口切换的动画(覆盖、移动、跳转、回弹等)。