屏幕显示多级菜单

📅 2026/7/23 8:11:23 👁️ 阅读次数 📝 编程学习
屏幕显示多级菜单

多次菜单的定义与实现

结构体定义

typedef struct disp_menu_s { const char *name; // 菜单名 const char *desc; // 当前菜单描述 struct disp_menu_s *supmenus; // 上级菜单列表 size_t supmenu_count; // 上级菜单数量 struct disp_menu_s *submenus; // 子菜单列表 size_t submenu_count; // 子菜单数量 esp_err_t (*action)(void *arg); // 菜单执行函数 void *arg; // 函数执行参数 } disp_menu_t;

定义菜单的菜单名和描述,指明上级菜单的位置,上次菜单同级的菜单数量,下级菜单列表的位置,与下级菜单数量,当前的菜单是否关联回调执行函数,与回调函数的入参参数

具体的菜单声明

  • 以小车遥控器代码示例
disp_menu_t check_menu[] = { {"bond_disp", "bond display", settings_menu, 3, NULL, 0, NULL, NULL}, }; disp_menu_t settings_menu[] = { {"check_disp", "check display", main_menu, 2, check_menu, 1, check_devices, NULL}, {"unbond_disp", "unbond display", main_menu, 2, NULL, 0, unbind_device, NULL}, {"reset", "reset device", main_menu, 2, NULL, 0, NULL, NULL}, }; disp_menu_t main_menu[] = { {"default_disp", "default display", NULL, 0, NULL, 0, NULL, NULL}, {"setting_disp", "setting display", NULL, 0, settings_menu, 3, NULL, NULL}, };

其中check_menu下的bond_disp菜单存在子菜单,但是当前赋值为空(NULL, 0),具体的分配区域需要在进入check_menu菜单时通过check_devices去实现内容分配和指针重新指向一个有数据的地址并修改具体的数据

  • 具体的后续菜单的分配与修改
for (int i = 0; i < cJSON_GetObjectItem(root, "device_len")->valueint; i++) { cJSON *item = cJSON_GetArrayItem(cJSON_GetObjectItem(root, "data"), i); if (item) { cJSON *idx = cJSON_GetObjectItem(item, "idx"); cJSON *name = cJSON_GetObjectItem(item, "name"); cJSON *mac = cJSON_GetObjectItem(item, "mac"); dev_list[i].idx = idx->valueint; itoa(dev_list[i].idx, dev_list[i].idxs, 10); sprintf(dev_list[i].name, name->valuestring); sprintf(dev_list[i].mac, mac->valuestring); dev_list_menu[i].name = dev_list[i].mac; dev_list_menu[i].desc = dev_list[i].name; dev_list_menu[i].supmenus = check_menu; dev_list_menu[i].supmenu_count = 3; dev_list_menu[i].submenus = NULL; dev_list_menu[i].submenu_count = 0; dev_list_menu[i].action = bind_device; dev_list_menu[i].arg = dev_list[i].mac; } } check_menu[0].submenus = dev_list_menu; check_menu[0].submenu_count = cJSON_GetObjectItem(root, "device_len")->valueint;

实现了对空菜单的修改与重新赋值

对菜单路径的访问

  • 当前菜单所在位置的记录
disp_menu_t *curr_menu = main_menu; int curr_menu_len = 2; int cursor = 0;
  • curr_menu指明当前所在菜单位置
  • curr_menu_len指明当前菜单的子菜单目录长度
  • cursor指明当前选中了哪个子菜单
  • 进入下一级菜单
if (evt.key_val.key_fsm_finished) { if (curr_menu[cursor].action != NULL) { if (curr_menu[cursor].action(curr_menu[cursor].arg) != ESP_OK) { ESP_LOGI(__func__, "menu: %s action false", curr_menu[cursor].desc); } else { ESP_LOGI(__func__, "menu: %s action true", curr_menu[cursor].desc); } } if (curr_menu[cursor].submenus != NULL) { curr_menu_len = curr_menu[cursor].submenu_count; curr_menu = curr_menu[cursor].submenus; cursor = 0; } }
  1. 首先判断是否需要执行回调函数
  2. 切换进入下级目录

**需要确保函数对目录的进入不会有影响,否则应该调整函数执行失败的策略**

  • 返回上一级菜单
if (evt.key_val.key_fsm_finished) { if (curr_menu->supmenus != NULL) { cursor = 0; curr_menu_len = curr_menu[cursor].supmenu_count; curr_menu = curr_menu[cursor].supmenus; } else // joycon defalut display { disp_mode = 0; cursor = 0; curr_menu_len = 2; curr_menu = main_menu; } }

通过当前菜单列表的父指针获取当前列表,并且父指针的菜单结构体中存在当前菜单列表的数量,可以准确的获取菜单列表长度