需求:
根据用户的四个按键的(上下左右)显示对应的内容,左右键切换显示项目名称,上下键切换切换该项目的具体信息条目。另外有一个无符号整型变量上的每一位用于控制项目的显示与否,该位为1则显示对应项目,反之则不显示该项目。
组合按键采用面向对象的思想,加上数据结构中的双向链表,这样的组合按键需求十分容易实现。
下面是对上述需求的模型化:
Linux终端用于显示,键盘上的w、s、a、d键和箭头符号分别表示上下左右按键。左右按键用于切换显示附近的银行名称,上下键用于切换显示该银行的具体信息,如地址、当前排队人数等。另外show_ctl变量对应的位用于控制对应的银行是否显示。
编码如下:
#include <stdio.h>
#define KEY_UP 192
#define KEY_DOWN 193
#define KEY_LEFT 195
#define KEY_RIGHT 194
//封装显示结构体
typedef void (*SHOW_FUN)(void);
typedef struct _tag_bank
{
struct _tag_bank* pre;
struct _tag_bank* next;
SHOW_FUN showFunc[10];
size_t subScrMaxIndex;
size_t subScrIndex;
}bankItem_t;
bankItem_t *current_bankItem = NULL;
void agricultural_bank_func1() { printf("------------ 中国农业银行 ------------ \n");}
void agricultural_bank_func2(){ printf("1. 地址: xx街道xxx号\n");}
void agricultural_bank_func3(){ printf("2. 当前排队人数: 5人\n"); }
void agricultural_bank_func4() { printf("3. 上班时间: 9:30 ~ 17:30\n"); }
void icbc_bank_func1() { printf("-------------- 工商银行 --------------\n"); }
void icbc_bank_func2() { printf("1. 地址: yy街道yyy号\n"); }
void icbc_bank_func3() { printf("2. 当前排队人数: 6人\n"); }
void icbc_bank_func4() { printf("3. 上班时间: 10:00 ~ 17:30\n"); }
void china_bank_func1() { printf("-------------- 中国银行 --------------\n"); }
void china_bank_func2() { printf("1. 地址: cc街道cc号\n"); }
void china_bank_func3() { printf("2. 当前排队人数: 8人\n"); }
void china_bank_func4() { printf("3. 上班时间: 10:00 ~ 17:00\n"); }
void construction_bank_func1() { printf("-------------- 建设银行 --------------\n"); }
void construction_bank_func2() { printf("1. 地址: zz街道zzz号\n"); }
void construction_bank_func3() { printf("2. 当前排队人数: 4人\n"); }
void construction_bank_func4() { printf("3. 上班时间: 9:00 ~ 17:00\n"); }
bankItem_t bank1 = {0, 0, {agricultural_bank_func1, agricultural_bank_func2, agricultural_bank_func3, agricultural_bank_func4}, 3, 0};
bankItem_t bank2 = {0, 0, {icbc_bank_func1, icbc_bank_func2, icbc_bank_func3, agricultural_bank_func4}, 3, 0};
bankItem_t bank3 = {0, 0, {china_bank_func1, china_bank_func2, china_bank_func3, china_bank_func4}, 3, 0};
bankItem_t bank4 = {0, 0, {construction_bank_func1, construction_bank_func2, construction_bank_func3, construction_bank_func4}, 3, 0};
//用于控制哪个银行是否显示 bit[0]-农业,bit[1]-工商,bit[2]-中国,bit[3]-建设
unsigned char show_ctl = 0x02;
static bankItem_t* _show_ctl_arr[] = {&bank1, &bank2, &bank3, &bank4};
static char _check_show(unsigned char num)
{
if ((show_ctl >> num) & 0x01)
return 1;
return 0;
}
//初始化显示条目的顺序
int struct_init()
{
char i = 0, ret, cnt = 0;
bankItem_t* tail = NULL;
for (i = 0; i < 4; i++)
{
ret = _check_show(i);
if (ret)
{
//以下是双向链表的插入操作
if (current_bankItem == NULL)
{
_show_ctl_arr[i]->pre = _show_ctl_arr[i];
_show_ctl_arr[i]->next = _show_ctl_arr[i];
current_bankItem = _show_ctl_arr[i];
}
else
{
_show_ctl_arr[i]->next = current_bankItem;
_show_ctl_arr[i]->pre = tail;
tail->next = _show_ctl_arr[i];
current_bankItem->pre = _show_ctl_arr[i];
}
tail = _show_ctl_arr[i];
cnt++;
}
}
return cnt;
}
//获取键盘数据
int static _get_input(void)
{
char buf[8];
int ret;
ret = read(0, buf, 8); //0是标准输入的fd
if (ret == 1) //用于输入的是w/s/a/d
return buf[0];
else if (ret == 3 && buf[0] == 27 && buf[1] == 91) //用于输入的是箭头符号
return 127 + buf[2]; //小于127的数都和键盘上按键有对应关系
}
int main(void)
{
int c;
int ret;
system("clear");
if (!(ret = struct_init())) //struct_init()根据select_ctl变量初始化显示条目的顺序
{
printf("\t[附近没有银行(%#x)]\n\n", show_ctl);
return 0;
}
printf("\t[附近有%d家银行(%#x)]\n\n", ret, show_ctl);
//关闭终端的输入回显
system("stty -echo -icanon");
printf("当前位于:\n");
current_bankItem->showFunc[current_bankItem->subScrIndex]();
printf("[请按左右键显示附近银行,上下键显示当前银行信息] \n");
while (1)
{
c = _get_input();
if (c == 'a' || c == KEY_LEFT)
{
current_bankItem = current_bankItem->pre;
current_bankItem->subScrIndex = 0;
}
else if (c == 'd' || c == KEY_RIGHT)
{
current_bankItem = current_bankItem->next;
current_bankItem->subScrIndex = 0;
}
else if (c == 'w' || c == KEY_UP)
{
if (current_bankItem->subScrIndex < current_bankItem->subScrMaxIndex)
current_bankItem->subScrIndex++;
}
else if (c == 's' || c == KEY_DOWN)
{
if (current_bankItem->subScrIndex > 1)
current_bankItem->subScrIndex--;
else
current_bankItem->subScrIndex = 1;
}
else
{
printf("输入有误,请重新输入\n");
continue;
}
current_bankItem->showFunc[current_bankItem->subScrIndex]();
}
//打开终端的输入回显
system("stty echo icanon");
return 0;
}
运行结果:
比起用面向过程的方法实现,用面向对象思想和函数指针来设计按键应用程序,最大的好处在于其可扩展性,思路也清晰得多。