在之前的某个项目中用到了emwin图形库,有些想要的效果官方的控件无法实现,所以就研究了一下,自己做了几个可以嵌入到emwin官方库的控件.
本文实现的check button和这个有些像(原谅我随便在网上截了个图,硬件暂时不在,等硬件到了会补)没有选中是是一个空心框,选中后是一个实心框,功能和emwin中的checkbox相同,可以认为是checkbox的重写版本,相对官方控件这个更大比较适合触屏的界面,风格也简洁些,更没有旁边一坨丑了吧唧的文字。接口命名和官方保持一致,支持xx_CreateEx和资源列表等所有控件创建方式,还有一些常用参数设置函数。
Check button继承自父控件windows,通过覆盖windows的callback函数实现自定义效果,除父控件windows自身的参数外, check button新增加的相关数据通过userdata的方式存储,另外还需实现check button自身xx_GetUserData和xx_SetUserData函数,用于check button的子类派生。
Checkbutton自身参数结构体如下,主要是控件尺寸和颜色,及开关状态信息。
控件直接创建函数如下,主要是通过传来的参数创建了一个window控件,控件回调使用checkbutton默认回调函数。
注意127行,这个是check button的默认参数,在未设置check button显示参数时会使用这组值。
下面是以资源列表方式创建check button控件的入口函数,相对复杂一点,因为emwin会传入一个资源列表的结构体,需要从结构体中提取出需要的参数。
我们再来看看核心的回调函数是如何实现的
/*********************************************************************
*
* CheckButton工具默认回调函数
*/
void CHECKBUTTON_Callback(WM_MESSAGE *pMsg)
{
// WM_HWIN hItem;
// int NCode;
// int Id;
static unsigned char tsign=0;
GUI_RECT area;
GUI_PID_STATE *pPID;
CHECK_BUTTON_ITEM RBItem;
WINDOW_GetUserData(pMsg->hWin,&RBItem,sizeof(CHECK_BUTTON_ITEM));//获取自定义数据
switch (pMsg->MsgId) {
case WM_TOUCH:
pPID=(GUI_PID_STATE *)pMsg->Data.p;
//点击事件
if(pPID->Pressed==1)
{
//检查是否首次点击
if(tsign==0)
{
WM_NotifyParent(pMsg->hWin,WM_NOTIFICATION_CLICKED); //发送点击消息
tsign=1;
//切换状态
if(RBItem.SWITCH_STATUS==CHECKBUTTON_ON)
RBItem.SWITCH_STATUS=CHECKBUTTON_OFF;
else if(RBItem.SWITCH_STATUS==CHECKBUTTON_OFF)
RBItem.SWITCH_STATUS=CHECKBUTTON_ON;
//写入数据
WINDOW_SetUserData(pMsg->hWin,&RBItem,sizeof(CHECK_BUTTON_ITEM)); //写入具体数据
WM_InvalidateWindow(pMsg->hWin); //无效化窗口
WM_NotifyParent(pMsg->hWin,WM_NOTIFICATION_VALUE_CHANGED); //发送选择更改消息
}
}
//释放事件
if(pPID->Pressed==0)
{
WM_NotifyParent(pMsg->hWin,WM_NOTIFICATION_RELEASED); //发送释放消息
tsign=0;//归零点击标志
}
break;
case WM_PAINT:
//关闭状态时
if(RBItem.SWITCH_STATUS==0)
{
//绘制空白区域
GUI_SetColor(RBItem.BACK_COLOR);
area.x0=1;
area.x1=RBItem.WIN_XSIZE-2;
area.y0=1;
area.y1=RBItem.WIN_YSIZE-2;
GUI_FillRectEx(&area);
//绘制边框
GUI_SetColor(RBItem.BORDER_COLOR);
area.x0=0;
area.x1=RBItem.WIN_XSIZE-1;
area.y0=0;
area.y1=RBItem.WIN_YSIZE-1;
GUI_DrawRectEx(&area);
}
//开启状态时
else if(RBItem.SWITCH_STATUS==1)
{
//绘制空白区域
GUI_SetColor(RBItem.BACK_COLOR);
area.x0=1;
area.x1=RBItem.WIN_XSIZE-2;
area.y0=1;
area.y1=RBItem.WIN_YSIZE-2;
GUI_FillRectEx(&area);
//绘制中心矩形
GUI_SetColor(RBItem.CENTER_COLOR);
area.x0=2;
area.x1=RBItem.WIN_XSIZE-3;
area.y0=2;
area.y1=RBItem.WIN_YSIZE-3;
GUI_FillRectEx(&area);
//绘制边框
GUI_SetColor(RBItem.BORDER_COLOR);
area.x0=0;
area.x1=RBItem.WIN_XSIZE-1;
area.y0=0;
area.y1=RBItem.WIN_YSIZE-1;
GUI_DrawRectEx(&area);
}
break;
case WM_DELETE:
break;
default:
BUTTON_Callback(pMsg);
break;
}
}
回调函数入口处会首先提取自己的私有数据,然后主要需要处理两个消息事件,分别是WM_TOUCH和WM_PAINT,这里采用事件处理和显示刷新分离的方式,当有鼠标或者触屏点击控件时,emwin会向callback发送WM_TOUCH消息,callback通过读取私有数据中控件事件状态和WM_TOUCH消息中具体内容完成事件处理,并把处理结果写回到私有数据中,随后通过WM_InvalidateWindow向调度器发送失效消息无效化自己的显示,并通过WM_NotifyParent向父窗口发送WM_NOTIFICATION_VALUE_CHANGED消息告知父窗口自己的状态已经发送了改变。到这里就完成了事件处理的部分,随后开始显示刷新,emwin在接收到无效化消息并处理完其他的事件后,会再次向窗口发送WM_PAINT消息,然后又是一轮相似的操作,读私有数据,根据数据刷新显示,到这里屏幕上控件显示就已经变化了,一次输入事件处理完成。
除WM_TOUCH和WM_PAINT外,callback还需处理WM_DELETE消息,主要是避免窗口的缺省函数乱删东西,缺省函数使用的BUTTON_Callback,因为我测试发现使用WINDOWS_Callback有时会出现问题,所以使用了一个现成控件的。
核心的部分讲完了,其他的就是一些补充的函数。
Check button用于派生子类的两个函数,子控件可以通过使用xx_GetUserData和xx_SetUserData继承check button全部特征并实现自身特征。
两个和参数读写有关的函数,CHECKBUTTON_GetState和CHECKBUTTON_SetState,其他的参数读写函数,比如说设置边框颜色,设置中心颜色,间距,宽度等等也都是大同小异。
控件使用的方式和emwin官方的控件完全一样,可以直接在资源列表中插入一个check button控件,然后正常使用即可。
到这里制作emwin控件就算基本讲完了,check button控件是一种相对来说比较简单的控件,所有我以它为例讲解了一下控件实现的大体结构,以后有时间可能会讲解一下其他比较复杂的控件。
源码链接:https://download.csdn.net/download/CSDN1344789841/15611114
文章链接:https://blog.csdn.net/CSDN1344789841/article/details/114435225
转载需标明出处