輕量級多級菜單控制框架(C語言)
掃描二維碼
隨時隨地手機(jī)看文章
前言
作為嵌入式軟件開發(fā),可能經(jīng)常會使用命令行或者顯示屏等設(shè)備實現(xiàn)人機(jī)交互的功能,功能中通常情況都包含 UI 菜單設(shè)計;對于復(fù)雜的UI設(shè)計,可能最多優(yōu)先考慮的是使用開源的GUI庫。
但是GUI使用起來復(fù)雜,在簡單的UI設(shè)計中則臃腫或者較難實現(xiàn)(比如OLED這種);基于這種情況,很多開發(fā)人員都會有自己的菜單框架模塊,避免重復(fù)造輪子,網(wǎng)上有很多這種菜單框架的代碼,但是大多耦合性太強(qiáng)。
代碼層面上大部分都耦合了按鍵和不同平臺(不同尺寸的OLED)等模塊;并無法獨立出來適配不同的菜單設(shè)計。
而多級菜單的設(shè)計也使得上層軟件被迫耦合,比如一張表包含了多級菜單內(nèi)容等。
基于以上種種痛點,本文介紹一個耦合性低,完全可移植的輕量級菜單框架,菜單顯示風(fēng)格和顯示平臺完全由自己根據(jù)需求設(shè)計,而菜單操作統(tǒng)一由菜單框架處理即可,提高程序的移植性。
特點
主要特點就是耦合性低,移植無需修改。且不和任何模塊耦合,同時對于上層軟件設(shè)計,也可以做到解耦實現(xiàn)。
可以為不同菜單設(shè)計不同的顯示風(fēng)格。
介紹
多級菜單
同級菜單以數(shù)組的方式體現(xiàn),父菜單和子菜單的關(guān)聯(lián)則使用鏈表實現(xiàn)。
數(shù)組元素內(nèi)容有:
菜單選項字符串描述(多語種可設(shè)置)
菜單選項進(jìn)入回調(diào)函數(shù):當(dāng)前菜單選項進(jìn)入時(從父菜單進(jìn)入)需要執(zhí)行一次的函數(shù)
菜單選項退出回調(diào)函數(shù):當(dāng)前菜單選項進(jìn)入后退出時(退出至父菜單)需要執(zhí)行一次的函數(shù)
菜單選項重加載回調(diào)函數(shù):當(dāng)前菜單選項每次加載時(從父菜單進(jìn)入或子菜單退出)需要執(zhí)行一次的函數(shù)
菜單選項周期調(diào)度回調(diào)函數(shù):當(dāng)前菜單選項的周期調(diào)度函數(shù)
菜單選項的擴(kuò)展數(shù)據(jù)
鏈表內(nèi)存可以選擇采用動態(tài)內(nèi)存分配或者數(shù)組實現(xiàn)
方便對不同菜單界面功能解耦
大部分菜單采用的都是數(shù)組中包含了所有不同級別的菜單選項內(nèi)容實現(xiàn),無法做到很好的解耦方式;
該模塊通過動態(tài)綁定子菜單和鏈表的方式可以達(dá)到較好的解耦狀態(tài)
顯示效果
該框架只負(fù)責(zé)菜單選項控制操作,不負(fù)責(zé)在圖像界面顯示效果,需要在對應(yīng)的回調(diào)函數(shù)中實現(xiàn)菜單顯示效果。
設(shè)置對應(yīng)的效果顯示函數(shù),即可為不同的菜單設(shè)置不同的菜單顯示效果,比如圖標(biāo)式、列表式或右側(cè)彈窗式等。
可以在不同顯示平臺體現(xiàn),比如LCD、OLED或終端界面等。
可擴(kuò)展
每級菜單選項都可以設(shè)置自定義數(shù)據(jù),用來實現(xiàn)更多的菜單操作或者顯示效果等。
不同級別的菜單可以設(shè)置自定義數(shù)據(jù)(比如菜單選項隱藏/圖標(biāo)數(shù)據(jù)等)
可配置
| 配置選項 | 描述 |
|---|---|
| _COT_MENU_USE_MALLOC_ | 定義則采用 malloc/free 的方式實現(xiàn)多級菜單, 否則通過數(shù)組的形式 |
| _COT_MENU_USE_SHORTCUT_ | 定義則啟用快捷菜單選項進(jìn)入功能 |
| COT_MENU_MAX_DEPTH | 多級菜單深度 |
| COT_MENU_MAX_NUM | 菜單支持的最大選項數(shù)目 |
| COT_MENU_SUPPORT_LANGUAGE | 菜單支持的語種數(shù)目 |
功能多樣化
√多語種。
支持菜單選項多語種切換,至少設(shè)置一種語言
多語種除了該方式,還可以使用多語種配置數(shù)據(jù)實現(xiàn),比如鍵值對,鍵作為菜單選項字符串體現(xiàn)
√支持快速進(jìn)入指定菜單界面。
可以通過相對選項索引或者絕對選項索引路徑實現(xiàn)
√可以實現(xiàn)有限界面內(nèi)顯示少量的菜單選項內(nèi)容。
有現(xiàn)成的函數(shù)可用,無需擔(dān)心使用不同尺寸重新實現(xiàn)菜單選項部分可見
使用說明
菜單初始化和使用
// 定義菜單信息,函數(shù)由主菜單模塊定義并提供static cotMainMenuCfg_t sg_tMainMenu = {{"主菜單", "Main Menu"}, Hmi_EnterMainHmi, NULL, NULL, NULL}; int main(void){ cotMenu_Init(&sg_tMainMenu); while (1) { ... if (timeFlag) { timeFlag = 0; cotMenu_Task(); // 周期調(diào)度 } }}
主菜單定義和綁定
定義一個主菜單選項內(nèi)容、主菜單顯示效果函數(shù)和主菜單進(jìn)入函數(shù)等
// 擴(kuò)展數(shù)據(jù)為圖標(biāo)文件名字cotMenuList_t sg_MainMenuTable[] = { {{"音樂", "Music"}, Hmi_MusicEnter, Hmi_MusicExit, Hmi_MusicLoad, Hmi_MusicTask, "music"}, {{"視頻", "Video"}, NULL, Hmi_VideoExit, Hmi_VideoLoad, Hmi_VideoTask, "video"}, {{"攝像機(jī)", "Camera"}, Hmi_CameraEnter, Hmi_CameraExit, Hmi_CameraLoad, Hmi_CameraTask, "camera"}, {{"設(shè)置", "Setting"}, Hmi_SetEnter, Hmi_SetExit, Hmi_SetLoad, Hmi_SetTask, "setting"},}; /* 主菜單顯示效果 */static void ShowMainMenu(cotMenuShow_t *ptShowInfo){ char *pszSelectDesc = ptShowInfo->pszItemsDesc[ptShowInfo->selectItem]; oledsize_t idx = (128 - 6 * strlen(pszSelectDesc)) / 2; cotOled_DrawGraphic(40, 0, (const char *)ptShowInfo->pItemsExData[ptShowInfo->selectItem], 1); cotOled_SetText(0, 50, " ", 0, FONT_12X12); cotOled_SetText(idx, 50, pszSelectDesc, 0, FONT_12X12);} void Hmi_EnterMainHmi(void){ cotMenu_Bind(sg_MainMenuTable, COT_GET_MENU_NUM(sg_MainMenuTable), ShowMainMenu);}
子菜單定義和綁定
如果菜單選項有子菜單,則該菜單選項調(diào)用cotMenu_Enter,進(jìn)入回調(diào)函數(shù)不能為NULL,且該回調(diào)函數(shù)需調(diào)用cotMenu_Bind進(jìn)行綁定
/* 設(shè)置的子菜單內(nèi)容 */cotMenuList_t sg_SetMenuTable[] = { {{"語言", "Language"}, NULL, NULL, NULL, OnLanguageFunction, NULL}, {{"藍(lán)牙", "Bluetooth"}, NULL, NULL, NULL, OnBluetoothFunction, NULL}, {{"電池", "Battery"}, NULL, NULL, NULL, OnBatteryFunction, NULL}, {{"儲存", "Store"}, NULL, NULL, NULL, OnStorageFunction, NULL}, {{"更多", "More"}, Hmi_MoreSetEnter, Hmi_MoreSetExit, Hmi_MoreSetLoad, Hmi_MoreSetTask, NULL},}; /* 設(shè)置菜單顯示效果 */static void ShowSetMenu(cotMenuShow_t *ptShowInfo){ uint8_t showNum = 3; menusize_t tmpselect; cotMenu_LimitShowListNum(ptShowInfo, &showNum); printf("\e[0;30;46m ------------- %s ------------- \e[0m\n", ptShowInfo->pszDesc); for (int i = 0; i < showNum; i++) { tmpselect = i + ptShowInfo->showBaseItem; if (tmpselect == ptShowInfo->selectItem) { printf("\e[0;30;47m %d. %-16s\e[0m |\n", tmpselect + 1, ptShowInfo->pszItemsDesc[tmpselect]); } else { printf("\e[7;30;47m %d. %-16s\e[0m |\n", tmpselect + 1, ptShowInfo->pszItemsDesc[tmpselect]); } }} void Hmi_SetEnter(void){ // 進(jìn)入設(shè)置選項后綁定子菜單,同時為當(dāng)前綁定的菜單設(shè)置顯示效果函數(shù) cotMenu_Bind(sg_SetMenuTable, COT_GET_MENU_NUM(sg_SetMenuTable), ShowSetMenu); }
菜單控制
通過調(diào)用相關(guān)函數(shù)實現(xiàn)菜單選項選擇、進(jìn)入、退出等
// 需要先進(jìn)入主菜單cotMenu_MainEnter(); // 選擇上一個,支持循環(huán)選擇(即第一個可跳轉(zhuǎn)到最后一個)cotMenu_SelectPrevious(true); // 選擇下一個,不支持循環(huán)選擇(即最后一個不可跳轉(zhuǎn)到第一個)cotMenu_SelectNext(false); // 進(jìn)入,會執(zhí)行菜單選項的 pfnEnterCallFun 回調(diào)函數(shù)cotMenu_Enter(); // 退出,會執(zhí)行父菜單該選項的 pfnExitCallFun 回調(diào)函數(shù),并在退出后父菜單選項列表復(fù)位從頭選擇cotMenu_Exit(true);
Demo顯示效果
示例代碼采用的平臺是命令行輸出輸入顯示效果
demo中提供了如何實現(xiàn)圖形菜單(主菜單有點粗糙)、普通列表菜單、右側(cè)彈窗菜單(更多設(shè)置)等效果演示,菜單樣式可自由擴(kuò)展,足夠自由;快捷菜單操作、中英文切換演示。(windows中編譯需要將 demo.c轉(zhuǎn) GBK 編碼,Linux 轉(zhuǎn) utf8 編碼,不然可能出現(xiàn)漢字亂碼的問題)
以下是通過單片機(jī)驅(qū)動 OLED 顯示的菜單界面顯示效果
下載鏈接
下載鏈接(點擊閱讀原文),或更新內(nèi)容可看:
https://gitee.com/cot_package/cot_menu





