|
楼主 |
发表于 2012-12-19 23:02:02
|
显示全部楼层
三、我的总结
菜单类型不同,对按键的响应不同。比如,某些菜单“确定键”进入子菜单,其它则执行某种功能(保存啦,退出啦,时间设置啦,输入参数啦,显示某些信息啦)。尤其是当按键少的时候,按键必须具有复用功能,问题是,怎么让程序知道此时此地“确定”是进入子菜单,它时它地“确定”是执行某个特殊的功能函数。(当按键多的时候,不通过菜单选择,通过按键,直接执行某种功能函数,这个本程序没有涉及到)
为了设计一个结构、流程清晰的界面,首先分析菜单的行为,对菜单进行分类:
1st. 选择型菜单:当然就是菜单间的切换了
2nd. 功能型菜单:
i. 功能1型,执行某个操作
ii. 功能2型,允许用户输入信息或设置(如时间设置)
iii. 功能3型,单向不受控的显示窗口\\菜单(系统信息、帮助信息等)
然后建立菜单信息的结构:
typedef struct menu
{
uint8 ID; /*menu的唯一标识码*/
MenuInfo item[10]; /*结构体数组,本级menu包含的menuitem信息:显示位置,名称*/
uint8 items_num; /*本级menu的menuitem个数*/
/*和其它menu的关系,由程序完成,初始化NULL即可*/
struct menu *father, /*父辈menu*/
*son, /*长子menu*/
*obrother, /*兄menu elderbrother*/
*ybrother; /*弟menu youngerbrother*/
uint8 item_sn_cur; /*当前menuitem序号(serial number),0~(itemnum-1)*/
uint8 menu_type; /*menu类型*/
} Menu;
虽然这个二叉树的菜单设计,看上去和第2个版本多叉树类似,仍需要建立菜单节点之间的父子兄弟的关系。但这只是貌似相同,这2个有很大的区别。上一张图形象地显示了2者间的区别,更详细的见论文《基于节点编号的通用树状菜单设计方法与实现》中的图6,7。
多叉树菜单间的切换在第2个版本中有讲,它需要手动建立所有父子兄弟间的链接关系。
二叉树也需要建立这种关系,但这是通过一种ID编号规则(详见论文《基于节点》3(6))建立的,程序根据确立的ID编号规则初始化节点链接关系。显而易见的好处是,当我们需要增删节点或者改变节点位置时,只需要重新分配这个惟一的ID号即可。程序根据当前节点(菜单)的ID号,就可以寻找 到它的父子兄弟菜单,具体方法是,所有的菜单存在数组内的,通过for循环匹配ID,匹配即为当前菜单,当前菜单->父菜单,就找到了它的父菜单。
因此,本菜单的关键是,根据我们自己定义的ID编号规则,建立这样一个二叉树的菜单结构;然后,根据菜单类型,确立对按键响应的整个程序流程。
《基于节点》提出的程序流程是:
While (1)
{
键值 = 获取按键值的函数( );
当前菜单 = 菜单选择函数(当前菜单,键值);
if (当前菜单 == 选择型菜单)
continue;
switch (当前菜单->ID) /* 功能型菜单代码调用 */
{
case ID_1:
功能函数 ID_1();
break;
case ID_2:
……
}
}
菜单选择函数(当前菜单, 键值)
{
swithc (键值)
{
case 上:当前菜单 = 当前菜单->兄; break;
case 下:当前菜单 = 当前菜单->弟; break;
case 确定:当前菜单 = 当前菜单->父; break;
case 返回:当前菜单 = 当前菜单->子; break;
}
return (当前菜单);
}
清晰,明了,易维护,易移植。
但是,我需要设置时间,我只有4个按键,因此,上下键必须复用,上述流程会有冲突。
我的改进是:
While (1)
{
键值 = 获取按键值的函数( );
当前菜单 = 菜单界面执行函数(当前菜单,键值);
键值 = 空; /* 避免无键按下时,程序重复执行上次操作 */
}
菜单界面执行函数(当前菜单,键值)
{
switch (菜单类型 )
{
case 选择型菜单:
当前菜单 = 菜单选择函数(当前菜单,键值); break; /* 此函数和上一个流程的函数一样 */
case 功能1型(执行某个操作):
当前菜单 = 功能型菜单代码调用; break;
case 功能2型(允许用户输入信息或设置(如时间设置)):
当前菜单 = 对应的功能函数; break;
case 功能3型(单向不受控的显示窗口\\菜单, 系统信息、帮助信息等):
当前菜单 = 对应的功能函数; break;
}
return (当前菜单);
}
整个框架搭好之后,需要考虑细节,这些细节很容易在上述框架中实现:
1. 屏幕刷新,有键按下时刷新界面,避免屏幕的闪烁;数据的更新通过不断写入的方式而非清屏刷新;
2. 当前菜单的维护,分析所有可能改变当前菜单的函数,通过函数参数传递和返回值的方式,尽量避免使用全局变量;
3. 通过ID编号规则初始化节点间的链接关系,必然有垃圾链接,比如,主菜单无父节点,最底层菜单无子节点,如何避免在这些地方跑飞;
4. 对于多级菜单,父子菜单切换时高亮或反显的维护问题;
5. 如何实现一.3.(5)(6)的扩展功能。 |
|