嵌入式:简单的UI框架

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等)把这个系统接入进行,并实现窗口切换的动画(覆盖、移动、跳转、回弹等)。

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值