前言
最近看了 电子量产工具 这个项目,本专栏是对该项目的一个总结。
一、UI界面分析
UI 是用户界面(User Interface)的缩写,指的是人与计算机或其他设备进行交互时所使用的界面。用户界面是用户与系统之间的桥梁,提供了一种方式让用户与计算机进行沟通、操作和获取信息。
用户界面可以包括以下几个方面:
- 图形用户界面(GUI)。
- 命令行界面(CLI)
- 触摸界面
- 声音界面
- 虚拟现实界面(VR)和增强现实界面(AR)
我们的UI系统,就是构造各类GUI元素,比如按钮(目前只实现按钮)。
二、结构体描述按钮
/* 函数指针(绘制按键) */ typedef int (*ONDRAW_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff); /* 函数指针(按下按钮) */ typedef int (*ONPRESSED_FUNC)(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent); typedef struct Button { char *name; // 按键 名字 int status; //按键 按下状态 Region tRegion; // 按键的区域 ONDRAW_FUNC OnDraw; //一个 ONDRAW_FUNC 类型的函数指针,用于指向按钮绘制函数 ONPRESSED_FUNC OnPressed; //一个 ONPRESSED_FUNC 类型的函数指针,用于指向按钮按下事件处理函数 }Button, *PButton;
typedef struct Region { //区域包括 左上角坐标, 高, 宽 int iLeftUpX; int iLeftUpY; int iWidth; int iHeigh; }Region, *PRegion;
三、按钮初始化
void InitButton(PButton ptButton, char *name, PRegion ptRegion, ONDRAW_FUNC OnDraw, ONPRESSED_FUNC OnPressed) { ptButton->status = 0; //初始状态为 0 ,未按下 ptButton->name = name; ptButton->tRegion = *ptRegion; // 按钮的区域 ptButton->OnDraw = OnDraw ? OnDraw : DefaultOnDraw; //若 OnDraw 为空,则执行默认绘制函数DefaultOnDraw ptButton->OnPressed = OnPressed ? OnPressed : DefaultOnPressed; //若 OnPressed 为空,则执行默认绘制函数DefaultOnPressed }
四、默认绘制按键事件函数
绘制各个按钮 Button 的底色,文字,并将其刷新到 DispBuff 上
static int DefaultOnDraw(struct Button *ptButton, PDispBuff ptDispBuff) { /* 绘制底色 */ DrawRegion(&ptButton->tRegion, BUTTON_DEFAULT_COLOR); // 红色 0xff0000 /* 居中写文字 */ DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR); //黑色 0x000000 /* flush to lcd/web */ FlushDisplayRegion(&ptButton->tRegion, ptDispBuff); return 0; }
- 绘制底色
void DrawRegion(PRegion ptRegion, unsigned int dwColor) { int x = ptRegion->iLeftUpX; int y = ptRegion->iLeftUpY; int width = ptRegion->iWidth; int heigh = ptRegion->iHeigh; int i,j; for (j = y; j < y + heigh; j++) { for (i = x; i < x + width; i++) PutPixel(i, j, dwColor); //描点函数 } }
- 居中写文字
在 一定的区域 Region,按颜色 dwColor 居中显示名字 name
void DrawTextInRegionCentral(char *name, PRegion ptRegion, unsigned int dwColor) { int n = strlen(name); int iFontSize = ptRegion->iWidth / n / 2; FontBitMap tFontBitMap; int iOriginX, iOriginY; int i = 0; int error; if (iFontSize > ptRegion->iHeigh) //计算字体大小 iFontSize = ptRegion->iHeigh; /* 当前文字的基点 */ iOriginX = (ptRegion->iWidth - n * iFontSize)/2 + ptRegion->iLeftUpX; iOriginY = (ptRegion->iHeigh - iFontSize)/2 + iFontSize + ptRegion->iLeftUpY; SetFontSize(iFontSize); // 设置字体大小 while (name[i]) //字符编码 { /* get bitmap */ tFontBitMap.iCurOriginX = iOriginX; tFontBitMap.iCurOriginY = iOriginY; error = GetFontBitMap(name[i], &tFontBitMap); //根据字符编码获取位图,保存在 参数 tFontBitMap中 if (error) { printf("SelectAndInitFont err\n"); return; } /* draw on buffer */ DrawFontBitMap(&tFontBitMap, dwColor); // 绘制位图 iOriginX = tFontBitMap.iNextOriginX; //获取下一文字的基点坐标 iOriginY = tFontBitMap.iNextOriginY; i++; } }
- 将绘制到的按钮,刷新到 buffer 上。
返回 LCD 的 framebuffer , 以后上层 APP 可以直接操作LCD, 可以不用 FbFlushRegion
也可以 malloc 返回一块无关的buffer, 要使用 FbFlushRegion。
这里 为了 以后代码的可移植性,加上了刷新界面,但 并未做出任何举动。
五、默认按下按键事件函数
按下按键事件函数 也需要 上面 绘制函数 里的 : 绘制底色, 居中写文字 ,刷新界面。这里就不再 重述了。
static int DefaultOnPressed(struct Button *ptButton, PDispBuff ptDispBuff, PInputEvent ptInputEvent) { unsigned int dwColor = BUTTON_DEFAULT_COLOR; //按钮初始颜色 红色,0xff0000 ptButton->status = !ptButton->status; // 按钮状态,未按下时 为 0 if (ptButton->status) dwColor = BUTTON_PRESSED_COLOR; //按下的颜色为 绿色, 0x00ff00 /* 绘制底色 */ DrawRegion(&ptButton->tRegion, dwColor); /* 居中写文字 */ DrawTextInRegionCentral(ptButton->name, &ptButton->tRegion, BUTTON_TEXT_COLOR); /* 刷新界面 到 lcd/web */ FlushDisplayRegion(&ptButton->tRegion, ptDispBuff); return 0; }
六、测试程序
- 显示界面初始化
- 字体初始化
- ui 界面
/* 设置按钮的显示位置,宽高 */ tRegion.iLeftUpX = 200; tRegion.iLeftUpY = 200; tRegion.iWidth = 300; tRegion.iHeigh = 100; /* 显示 "test", 调用默认绘制,按下函数 */ InitButton(&tButton, "test", &tRegion, NULL, NULL); tButton.OnDraw(&tButton, ptBuffer); //绘制按键 while (1) { tButton.OnPressed(&tButton, ptBuffer, NULL); //按下按键, sleep(2); // 休眠 2 秒 }
实验效果
实验结果就是每隔 2 秒,自动 变换按键的颜色。(我按钮后面的图是 自己的QT界面,忽略即可)
总结
UI 系统界面的逻辑性 不难,就是一些函数的编写有些困难。
这些封装好的代码,可以用于以后代码的一直调用。