[技术问答] 嵌入式应用软件人机界面开发的菜单框架编写

[复制链接]
3497|15
 楼主| kmzuaz 发表于 2025-3-27 21:00 | 显示全部楼层 |阅读模式



    一般来说我们的产品都有按键,按键用来操作相应的界面,那就会有相应的界面处理方法,以下有两种结构的编写:
A类编写



  1. 1//针对左键的处理函数,传入的参数为当前页面
  2. 2void left_key_process(int current_page)
  3. 3{
  4. 4    switch(current_page)
  5. 5    {
  6. 6        case MAIN_PAGE:
  7. 7            //针对main_page的左键处理
  8. 8             main_page_left_key_process();
  9. 9            break ;
  10. 10        case SETTING_PAGE:
  11. 11            //针对setting_page的左键处理
  12. 12            setting_page_left_key_process();
  13. 13             break ;
  14. 14        case LOG_PAGE:
  15. 15            //针对log_page的左键处理
  16. 16            log_page_left_key_process();
  17. 17            break ;
  18. 18        case LANGUAGE_PAGE:
  19. 19            //针对language_page的左键处理
  20. 20            language_page_left_key_process();
  21. 21            break ;
  22. 22             ....
  23. 23    }
  24. 24}
  25. 25
  26. 26//针对右键的处理函数,传入的参数为当前页面
  27. 27void right_key_process(int current_page)
  28. 28{
  29. 29    //....
  30. 30}
  31. 31//针对确认键的处理函数,传入的参数为当前页面
  32. 32void enter_key_process(int current_page)
  33. 33{
  34. 34    //....
  35. 35}
  36. 36//UI页面处理函数,传入的参数为当前的键值
  37. 37void UI_Page_Process(int KeyValue)
  38. 38{
  39. 39    switch(KeyValue)
  40. 40    {
  41. 41        case LEFT_KEY :
  42. 42             left_key_process(KeyValue);
  43. 43             break ;
  44. 44
  45. 45        case RIGHT_KEY:
  46. 46             right_key_process(KeyValue);
  47. 47             break ;
  48. 48        case ENTER_KEY:
  49. 49             enter_key_process(KeyValue);
  50. 50             break ;
  51. 51        ...
  52. 52    }
  53. 53}



   A类编写,我可以起个名字叫直男式编写,逻辑没有错,也能正常操作相应的页面,没有问题,可它就是一条线,直! 首先拿到键值,然后操作界面,和我们正常人的思维差不多。但如果代码量剧增,页面众多,每个页面有不同的处理按键,相信A类的编写给后面的人来维护或者增加处理方法人一定会非常抱怨,为啥找个界面处理这么痛苦?    我们再来看看B类,我可以起个名字叫人机接口式编写。首先我们要操作界面,界面就是人机接口,每个不同的界面由对应的按键操作方法,这样看起来,是不是更好维护了?以后,我要往界面添加、删除等相关按键的处理方法,那是不是就更好找了?B类编写




  1. 1//主页面处理
  2. 2void main_page_process(int KeyValue)
  3. 3{
  4. 4    switch(KeyValue)
  5. 5    {
  6. 6        case LEFT_KEY:
  7. 7             //针对main_page的左键处理
  8. 8             break ;
  9. 9        case RIGHT_KEY:
  10. 10             //针对main_page的右键处理
  11. 11             break ;
  12. 12        case ENTER_KEY:
  13. 13             //针对main_page的Enter键处理
  14. 14             break ;
  15. 15        case BACK_KEY:
  16. 16             //针对main_page的back键处理
  17. 17             break ;
  18. 18        ...
  19. 19    }
  20. 20}
  21. 21//设置页面处理
  22. 22void setting_page_process(int KeyValue)
  23. 23{
  24. 24    switch(KeyValue)
  25. 25    {
  26. 26        case LEFT_KEY:
  27. 27             ...
  28. 28             break ;
  29. 29        case RIGHT_KEY:
  30. 30             ...
  31. 31             break ;
  32. 32        case ENTER_KEY:
  33. 33             ...
  34. 34             break ;
  35. 35        case BACK_KEY:
  36. 36             ...
  37. 37             break ;
  38. 38        ...
  39. 39    }
  40. 40}
  41. 41//记录页面处理
  42. 42void Log_page_process(int KeyValue)
  43. 43{
  44. 44    switch(KeyValue)
  45. 45    {
  46. 46        case LEFT_KEY:
  47. 47             ...
  48. 48             break ;
  49. 49        case RIGHT_KEY:
  50. 50             ...
  51. 51             break ;
  52. 52        case ENTER_KEY:
  53. 53             ...
  54. 54             break ;
  55. 55        case BACK_KEY:
  56. 56             ...
  57. 57             break ;
  58. 58        ...
  59. 59    }
  60. 60}
  61. 61//UI主页面处理 ,传入键值
  62. 62void UI_Page_Process(int KeyValue)
  63. 63{
  64. 64
  65. 65    switch(current_page)
  66. 66    {
  67. 67        case MAIN_PAGE:
  68. 68             main_page_process(KeyValue);
  69. 69             break ;
  70. 70        case SETTING_PAGE:
  71. 71             setting_page_process(KeyValue);
  72. 72             break ;
  73. 73        case LOG_PAGE:
  74. 74             Log_page_process(KeyValue);
  75. 75             break ;
  76. 76        ....
  77. 77    }
  78. 78}


    虽然说B类看起来更加的易维护,但仍然存在缺陷,那就是一旦菜单项数变多以后,就存在效率低下的问题了,我们有一种更好的解决方法函数跳转表,我们将B类的方式改一下,引入C类编写。
C类编写



  1. 1#include <iostream>
  2. 2#include <conio.h>
  3. 3using namespace std ;
  4. 4#define NR(x) (sizeof(x)/sizeof(x[0]))
  5. 5
  6. 6int main_page_process();
  7. 7int detect_page_process();
  8. 8int log_page_process();
  9. 9int setting_page_process();
  10. 10
  11. 11//菜单枚举
  12. 12typedef enum
  13. 13{
  14. 14    MAIN_PAGE = 0,
  15. 15    DETECT_PAGE,
  16. 16    LOG_PAGE,
  17. 17    SETTING_PAGE
  18. 18}MENU_INDEX;
  19. 19
  20. 20typedef int(*OPERATE_FUNC)();
  21. 21
  22. 22class MENU
  23. 23{
  24. 24    public:
  25. 25    MENU_INDEX index ;     //菜单索引
  26. 26    OPERATE_FUNC op_func ; //菜单对应的函数
  27. 27};
  28. 28
  29. 29//构造函数跳转表
  30. 30static MENU OP_MENU_STRUCT[] =
  31. 31{
  32. 32    {MAIN_PAGE   ,  main_page_process},
  33. 33    {DETECT_PAGE ,  detect_page_process},
  34. 34    {LOG_PAGE    ,  log_page_process},
  35. 35    {SETTING_PAGE,  setting_page_process},
  36. 36};
  37. 37
  38. 38int Goto_execute(int op)
  39. 39{
  40. 40    if(op >= NR(OP_MENU_STRUCT) || op < 0)
  41. 41        return -1 ;
  42. 42    //根据索引值op,调用相应的函数
  43. 43    return OP_MENU_STRUCT[op].op_func();
  44. 44}
  45. 45
  46. 46int main(int argc , char **argv)
  47. 47{
  48. 48    char ch ;
  49. 49    int menu_index = 0;
  50. 50    while(1)
  51. 51    {
  52. 52        ch = getch();
  53. 53        if(ch == 'a')     //左键
  54. 54           menu_index > 0 ? menu_index-- : menu_index = 0 ;
  55. 55        else if(ch == 'd')//右键
  56. 56           menu_index < NR(OP_MENU_STRUCT) ? menu_index++ : menu_index = NR(OP_MENU_STRUCT);
  57. 57        //执行菜单操作
  58. 58        Goto_execute(menu_index);
  59. 59    }
  60. 60    return 0 ;
  61. 61}
  62. 62
  63. 63int main_page_process()
  64. 64{
  65. 65    cout << "主页面" << endl ;
  66. 66}
  67. 67
  68. 68int detect_page_process()
  69. 69{
  70. 70    cout << "检测页面" << endl ;
  71. 71}
  72. 72
  73. 73int log_page_process()
  74. 74{
  75. 75    cout << "记录页面" << endl ;
  76. 76}
  77. 77
  78. 78int setting_page_process()
  79. 79{
  80. 80    cout << "设置页面" << endl ;
  81. 81}




heisexingqisi 发表于 2025-3-28 14:43 | 显示全部楼层
菜单如何做比较合适呢。那些流畅的菜单都是如何实现的
heisexingqisi 发表于 2025-3-28 14:44 | 显示全部楼层
要讨论采用什么样的结构来实现。
bartonalfred 发表于 2025-4-6 07:14 | 显示全部楼层
使用静态数组或预分配内存池,防止内存碎片和分配失败。
51xlf 发表于 2025-4-8 11:37 | 显示全部楼层
避免过于复杂的嵌套和过多的选项。
louliana 发表于 2025-4-8 13:08 | 显示全部楼层
合理利用嵌入式设备的内存、存储和处理器资源。
macpherson 发表于 2025-4-8 16:16 | 显示全部楼层
限制菜单递归深度,避免任务栈耗尽。
chenci2013 发表于 2025-4-9 21:39 | 显示全部楼层
仅在菜单状态变化时更新屏幕,避免频繁刷新。
weifeng90 发表于 2025-4-9 22:08 来自手机 | 显示全部楼层
把按键当做是一个事件,用事件来驱动操作。
yeates333 发表于 2025-4-10 00:49 | 显示全部楼层
通过宏或配置文件定义菜单项数量、布局等,便于修改。
jackcat 发表于 2025-4-13 21:50 | 显示全部楼层
菜单框架的代码和数据结构应该紧凑高效。
Wxy8030 发表于 2025-4-13 22:56 来自手机 | 显示全部楼层
没看懂C类,键值怎么导入的?与A、B类比的优势在哪里?
mmbs 发表于 2025-4-14 19:46 | 显示全部楼层
选择轻量级图形库              
少女诗篇 发表于 2025-8-28 11:50 | 显示全部楼层
采用结构体 typedef struct {
char* name;
void (handler)();
struct Menu submenu;
} Menu;
神明祷告 发表于 2025-9-9 14:05 | 显示全部楼层
嵌入式菜单框架开发:采用分层设计,底层管显示 / 输入,中间层处理菜单逻辑,上层定义菜单结构。用结构体存菜单属性(名称、子菜单、回调),链表或数组组织层级。实现导航(上 / 下 / 确认)、状态保存,支持动态更新,接口简洁,便于扩展和维护。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

3413

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部