多级菜单编程实现
1)菜单结构
菜单结构采用树状结构,由若干个节点组成多级菜单。每个节点由当前层级节点数、标题,操作函数、父节点和子节点构成,如下代码所示。
typedef struct Menu{
uint8_t MenuNodes; //当前层节点数目
char *MenuName; //菜单标题
void (*func)(uint16_t key); //节点函数
const struct Menu *Parent; //父节点
const struct Menu *Child; //子节点
}Menu_typedef;
2)菜单定义
这里我们实现一个多媒体管理菜单
【我的音乐】
----【经典音乐】
--------【曲目1】
--------【曲目2】
----【流行音乐】
【我的文档】
----【电子书】
----【说明书】
代码实现如下:
const Menu_typedef MainMenu[2] = { //一级菜单
{2, "我的音乐", NullSubs, NULL, MusicMenu},
{2, "我的文档", NullSubs, NULL, DocMenu}
};
const Menu_typedef MusicMenu[2] = { //二级菜单
{2, "经典音乐", NullSubs, MainMenu, Music_1_Menu},
{2, "流行音乐", NullSubs, MainMenu, NULL}
};
const Menu_typedef DocMenu[2] = { //二级菜单
{2, "电子书", NullSubs, MainMenu, Doc_1_Menu},
{2, "说明书", NullSubs, MainMenu, NULL}
};
const Menu_typedef Music_1_Menu[2] = { //三级菜单
{2, "曲目1", MusicPlayer, MusicMenu, NULL},
{2, "曲目2", MusicPlayer, MusicMenu, NULL},
};
const Menu_typedef Doc_1_Menu[1] = { //三级菜单
{2, "语文", Reader, DocMenu, NULL}
};
定义一个节点,用来保存当前节点信息,初始化指向主菜单
Menu_typedef *MenuPoint = (Menu_typedef*)MainMenu;
char UserChoose = 0; //当前层级节点序号
定义一个空函数,补充没有操作的节点。
void NullSubs(uint16_t key)
{
}
实现音乐播放函数(省略)
void MusicPlayer(uint16_t key)
{
switch(UserChoose){
case 0:
//play music 1
break;
case 1:
//play music 2
break;
}
}
实现文档阅读器(省略)
void MusicPlayer(uint16_t key)
{
if(key == )
switch(UserChoose){
case 0:
//语文
break;
}
}
3)菜单操作
菜单操作由以下功能组成:上、下、返回、确定。
菜单任务的入口函数为按键操作值,根据用户的输入设备传入
/**
* @brief 菜单选择.
* @Note
* @param keyvalue 输入按键
* @retval None
*/
void MenuTask(uint16_t Keyvalue)
{
if(OperationFunc == 0)
{
if(Keyvalue)
{
switch(Keyvalue)
{
case UP: //向上
UserChoose--;
if (UserChoose < 0)
{
UserChoose = MenuPoint->MenuNodes - 1; //跳转到最后一项
}
break;
case ESC: // 返回
OperationFunc = 0;
if (MenuPoint[UserChoose].Parent != NULL) //父级是否存??
{
MenuPoint = (Menu_typedef*)MenuPoint[UserChoose].Parent;
UserChoose = 0;
}
break;
case DOWN: //向下
UserChoose ++;
if (UserChoose > MenuPoint->MenuNodes - 1)
{
UserChoose = 0; //跳转到子级菜单的第一项
}
break;
case ENTER: // ENTER
if (MenuPoint[UserChoose].func != NullSubs) //判断是否有行为函数
{
Flag.OperationFunc = 1; //启动函数操作标志
layernumber++;
}
else if (MenuPoint[UserChoose].Child != NULL) //判断子级是否为空
{
MenuPoint = (Menu_typedef*)MenuPoint[UserChoose].Child; //进入子级
UserChoose = 0; //跳转到子级菜单的第一
}
break;
}
}
Keyvalue = 0; //清零,防止直接ENTER
Dipstr = MenuPoint[UserChoose].MenuName; //保存当前菜单标题,输出到显示设备
}
if(OperationFunc == 1)
{
(*MenuPoint[UserChoose].func)(Keyvalue); //执行函数操作
}
}
4)结语
这个树状菜单结构具备较好的扩展性和移植性,可以很方便的移植到其他平台,而且结构使用稳定,有问题请留言。