STemWin 无疑非常好用,系统提供的各种机制让我们攻城尸 敲GUI代码如虎添翼,事半功倍
可是仍然会遇到一些问题与困惑。 比如想要的功能系统没有提供怎么办,想用的控件没有提供怎么办。
STemWin 可以自定义控件吗? 可以更改 原控件的行为吗? 回答是肯定改变行为通过回调函数很容易办到,
并且网上应该有大量教程代码,这里就不介绍了。
举个例子,假如开发一台检测 重量的东西,屏幕上根据重量显示类似强度条一样的东西,
并且 轻的时候强度条是绿色的重则是红色, 由轻到重 从绿色到红色渐变。 在使用 STemWin 想要实现这个功能,
应该没有任何控件可以实现 唯一的实现方式是使用 底层绘图函数。,但是这里有个问题,使用底层绘图函数
则表示你抛弃了 WM管理器的各种功能,分层,重叠,渲染,无效化,回调!等等,这会使你开发其他地方很郁闷。
笔者正是希望有这样一个控件而着手试着自定义一个控件。并将经验传授给大家抛砖引玉,以后大家都有自己的自定义控件
,更自由的使用STemWin。同时为了马克下代码 以免忘记
下面先传两张效果图
没有注释 大家看的顺序从
Create_demowidget 到 callback 就行了。
始终记得一点 WM管理器 通过 消息(callback) 来驱动 整个系统。
比如当你 的控件需要显示的时候 WM管理器会往你的 callback 发送一条 WM_PAINT 消息,此时你负责把控件应该有的样子显示出来就OK了
而这正好是我们想要。,其他消息则使用 WM_DefaultProc 去处理好了。
相信没有注释 你也能看懂的!
下面是代码 .h 和.c
- #include "WM.h"
- #include "DIALOG_Intern.h" /* Req. for Create indirect data structure */
- #include "WIDGET.h"
- #include
- #include "..GUIIncludeGRAPH_Private.h"
- #define DEMO_Widget_ID 0x34455854 /* DEMO_Widget */
- #define DEMO_Widget_INIT_ID(p) p->DebugId = DEMO_Widget_ID
- typedef struct {
- WIDGET Widget;//第一项必须窗口 控件WM管理器使用
- GUI_COLOR aBkColor[2];//根据需要添加 本控件 的两项颜色值 分别为上图的 背景黑色 和白色
- GUI_COLOR RECT_Color;//上图外框的蓝色
- WM_HMEM hpText;//未用到
- WM_HMEM ahDrawObj[1];//未用到
- int size_base;//中间最短一条的长度
- GRAPH_PROPS Props;//盗用其他控件的 颜色, 绘外框的时候使用
- uint16_t Sta_MAX;//控件的最大条数
- uint16_t State;//当前显示的 信息
- #if GUI_DEBUG_LEVEL >= GUI_DEBUG_LEVEL_CHECK_ALL
- int DebugId;
- #endif
- } DEMO_Widget_Obj;
- GUI_HMEM Create_demowidget(int x0, int y0, int xsize, int ysize, WM_HWIN hParent, int WinFlags, int ExFlags, int Id);
- void Set_demowidget_Sta(GUI_HMEM hObj,int state);
- void Set_demowidget_Sta_MAX(GUI_HMEM hObj,int MAX);
- void Set_demowidget_Base(GUI_HMEM hObj,int base);
复制代码
下面是 .c
- #include "demowidget.h"
- //这个函数负责显示 控件的外观 主要是调用一下 GUI 2D 绘图函数或者 有必要的话显示文本
- void _Paint(DEMO_Widget_Obj *pObj,GUI_HMEM hObj,GUI_RECT * pRectInvalid)
- {
- GUI_RECT RectClient;
- int i,size_x;
- GUI_RECT rClient, rInside,Rect={0,0,0,0},Rect1={0,0,0,0},Rect2={0,0,0,0};
- GUI_GetClientRect(&RectClient);
- rClient = RectClient;
- Rect=rClient;
- GUI_Clear();
- GUI_SetBkColor(pObj->Props.aColor[1]);
- GUI_SetColor(pObj->Props.aColor[2]);
- GUI_DrawRectEx(&Rect);
- GUI__ReduceRect(&Rect, &Rect, 1);
- GUI_DrawRectEx(&Rect);
- Rect=rClient;
- GUI__ReduceRect(&rClient, &rClient, 3);
- LCD_SetBkColor (pObj->aBkColor[0]);
- LCD_SetColor (pObj->RECT_Color);
- size_x=(rClient.x1-pObj->size_base+1)/2/pObj->Sta_MAX;
- Rect.x0=rClient.x0+pObj->Sta_MAX*size_x;
- Rect.x1=rClient.x1-pObj->Sta_MAX*size_x;
- Rect.y1=(rClient.y1/2)+rClient.y1/(pObj->Sta_MAX*2+1)/2-1;
- Rect.y0=(rClient.y1/2)-rClient.y1/(pObj->Sta_MAX*2+1)/2;
- GUI__ReduceRect(&Rect1,&Rect,0);
- GUI__ReduceRect(&Rect2,&Rect,0);
- GUI_DrawRectEx(&Rect);
- for(i=0;iSta_MAX;i++)
- {
- Rect1.x0+=-size_x;
- Rect1.x1+=+size_x;
- Rect1.y0+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect1.y1+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.x0+=-size_x;
- Rect2.x1+=+size_x;
- Rect2.y0+=+rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.y1+=+rClient.y1/(pObj->Sta_MAX)/2;
- GUI_DrawRectEx(&Rect1);
- GUI_DrawRectEx(&Rect2);
- }
- LCD_SetColor(GUI_GREEN);
- LCD_SetBkColor (pObj->aBkColor[0]);
- GUI__ReduceRect(&Rect1,&Rect,1);
- GUI__ReduceRect(&Rect2,&Rect,1);
- GUI_FillRectEx(&Rect1);
- for(i=0;iState;i++)
- {
- LCD_SetColor((255-((255/pObj->Sta_MAX)*i)<<8)|((255/pObj->Sta_MAX*i)));
- Rect1.x0+=-size_x;
- Rect1.x1+=+size_x;
- Rect1.y0+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect1.y1+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.x0+=-size_x;
- Rect2.x1+=+size_x;
- Rect2.y0+=+rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.y1+=+rClient.y1/(pObj->Sta_MAX)/2;
- GUI_FillRectEx(&Rect1);
- GUI_FillRectEx(&Rect2);
- }
- LCD_SetColor(pObj->aBkColor[1]);
- for(;iSta_MAX;i++)
- {
- Rect1.x0+=-size_x;
- Rect1.x1+=+size_x;
- Rect1.y0+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect1.y1+=-rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.x0+=-size_x;
- Rect2.x1+=+size_x;
- Rect2.y0+=+rClient.y1/(pObj->Sta_MAX)/2;
- Rect2.y1+=+rClient.y1/(pObj->Sta_MAX)/2;
- GUI_FillRectEx(&Rect1);
- GUI_FillRectEx(&Rect2);
- }
- }
- static void _Delete(DEMO_Widget_Obj* pObj) {
- GUI_ALLOC_FreePtrArray(pObj->ahDrawObj, GUI_COUNTOF(pObj->ahDrawObj));
- }
- void DEMO_Widget_callback(WM_MESSAGE*pMsg)
- {
- GUI_HMEM hObj=pMsg->hWin;
- DEMO_Widget_Obj * pObj;
- if (WIDGET_HandleActive(hObj, pMsg) == 0) {
- return;
- }
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj);
- switch(pMsg->MsgId)
- {
- case WM_PAINT://响应重绘命令,显示自己 WM管理器在需要显示的时候自动发送。
- _Paint(pObj, hObj,(GUI_RECT*)pMsg->Data.p);
- return;
- case WM_DELETE:
- _Delete(pObj);
- break; /* No return here ... WM_DefaultProc needs to be called */
- //删除控件 这里不能return 因为需要WM_DefaultProc 迭代删除子控件
- }
- WM_DefaultProc(pMsg);
- }
- GUI_HMEM Create_demowidget(int x0, int y0, int xsize, int ysize, WM_HWIN hParent, int WinFlags, int ExFlags, int Id)
- {
- GUI_HMEM hObj;
- GUI_USE_PARA(ExFlags);
- /* Create the window */
- WM_LOCK();
- hObj = WM_CreateWindowAsChild(x0, y0, xsize, ysize, hParent, WinFlags, DEMO_Widget_callback,
- sizeof(DEMO_Widget_Obj) - sizeof(WM_Obj));
- //创建一个Window 小工具的灵魂, 靠它小工具才受WM管理器管理 各种WM管理器的功能才得以使用
- //这里需要注意 设置了N个字节的 userdata ,userdata 和创建的Window 实际使用的内存是连续的
- //
- if(hObj)
- {
- DEMO_Widget_Obj * pObj = (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj);
- //将Window 句柄转换为DEMO_Widget_Obj 类型的指针 这里的转换关系 需要 仔细思考!
- //上面提到userdata 和创建的Window 实际使用的内存是连续的 实际上 Window 实际使用的内存就是 WIDGET
- //这也是为什么一开始 说 DEMO_Widget_Obj 的第一项必须是 WIDGET
- WIDGET__Init(&pObj->Widget, Id, WIDGET_STATE_FOCUSSABLE);
- DEMO_Widget_INIT_ID(pObj);//下面是设置一些默认属性
- pObj->aBkColor[0]=GUI_BLACK;
- pObj->aBkColor[1]=GUI_WHITE;
- pObj->RECT_Color=GUI_BLUE;
- pObj->Sta_MAX=40;
- pObj->State=0;
- pObj->size_base=5;
- pObj->State=0;
- pObj->Props = GRAPH__DefaultProps;
- }
- else
- {
- GUI_DEBUG_ERROROUT_IF(hObj==0, "DEMO_Widget_Create failed")
- }
- WM_UNLOCK();
- return hObj;
- }
- //下面的函数都由应用程序 运行过程中调用,
- //按照格式改属性然后 使用WM_InvalidateWindow 告诉 WM管理器 控件需要重绘就OK了
- void Set_demowidget_Sta(GUI_HMEM hObj,int state)
- {
- DEMO_Widget_Obj * pObj;
- if(hObj)
- {
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj); /* Don't use use WIDGET_H2P because WIDGET_INIT_ID() has not be called at this point */
- }
- else
- {
- return;
- }
- WM_LOCK();
- if(state<=pObj->Sta_MAX)
- pObj->State=state;
- WM_InvalidateWindow(hObj);
- WM_UNLOCK();
- }
- void Set_demowidget_Sta_MAX(GUI_HMEM hObj,int MAX)
- {
- DEMO_Widget_Obj * pObj;
- if(hObj)
- {
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj); /* Don't use use WIDGET_H2P because WIDGET_INIT_ID() has not be called at this point */
- }
- else
- {
- return;
- }
- WM_LOCK();
- pObj->Sta_MAX=MAX;
- WM_InvalidateWindow(hObj);
- WM_UNLOCK();
- }
- void Set_demowidget_Base(GUI_HMEM hObj,int base)
- {
- DEMO_Widget_Obj * pObj;
- if(hObj)
- {
- pObj= (DEMO_Widget_Obj *)GUI_ALLOC_h2p(hObj); /* Don't use use WIDGET_H2P because WIDGET_INIT_ID() has not be called at this point */
- }
- else
- {
- return;
- }
- WM_LOCK();
- pObj->size_base=base;
- WM_InvalidateWindow(hObj);
- WM_UNLOCK();
- }
复制代码
STemWin 树形文件 目录 执行 初始化速度 和 内存优化
不知道大家有没有 这样的应用,需要将磁盘上面的文件以列表方式显示出来。,
TREEVIEW 无疑是最佳选择了,可是 假如存在这样一种情况,你的设备经常需要保存文件, 比如每个小时保存一个 文件 每天或者每个月一个文件夹
也许没这么频繁,但是无论如何 设备使用到一定时间 磁盘里面的文件数量将不可估量。
那么问题来了
第一、 树形目录 一个节点消耗100字节 一个树叶 消耗 50字节 当你的文件成千上万的时候 你的内存消耗得起么?
第二、众所周知磁盘操作速度很慢的,就算你获取文件名 同样需要操作磁盘 当你的文件成千上万的时候 你初始化这个 树形结构 可能都需要10多秒甚至更多。
别认为没那么慢,笔者曾经在 NAND FLASH上使用JFFS2文件系统 从文件一个有500个子文件的文件夹中 遍历检索第 100-103 4个文件花的时间长达2s
当然这个 JFFS2这个文件系统有关系, 后来使用FATFS文件系统 就快了起来 但是第一点依然是很严重的问题
为解决这个问题 笔者提出一个方案。
初始化 TREE的时候只将第一层 节点添加进去,对于文件夹, 在节点内添加一个占位 叶节点,这样看起来这个节点前面就有一个 +号可以被打开。
当打开这个节点的时候查看子节点是否为预添加的占位符, 如果是 再去遍历 对应的文件夹,同时删除占位符,添加节点。
如此 遍历的时间由单个文件夹的文件数量决定,不会因为总数量 庞大而慢。
内存则由 打开过的文件决定, 甚至 当系统内存不足的是否可以 删除被关闭的 节点内容 再次添加占位符。释放内存。
那么现在方案有了 具体怎么实现呢?
这里笔者提供一个已经实现的 回调函数 callback
- TREEVIEW_ITEM_Handle Item_get_next(TREEVIEW_ITEM_Handle hItem)//用到辅助函数
- {
- TREEVIEW_ITEM_OBJ *itemobj;
- if(hItem)
- {
- itemobj=(TREEVIEW_ITEM_OBJ*)GUI_ALLOC_h2p(hItem);
- return itemobj->hNext;
- }
- return 0;
- }
- void _cbtree(WM_MESSAGE * pMsg)
- {
- int Id, NCode;
- TREEVIEW_ITEM_Handle hItem,fristitem;
- char buf[20];
- int num[2]={1,2000};
- TREEVIEW_ITEM_INFO info;
- switch (pMsg->MsgId)
- {
- case WM_KEY://拦截 按键消息
- {
- int pressed;
- pressed=((WM_KEY_INFO*)(pMsg->Data.p))->PressedCnt;
- NCode = ((WM_KEY_INFO*)(pMsg->Data.p))->Key;
- switch(NCode)
- {
- default:
- if(pressed)
- {
- if (NCode==GUI_KEY_RIGHT)// 树默认right 键打开节点,再打开前 添加节点
- {
- hItem=TREEVIEW_GetSel(pMsg->hWin);
- TREEVIEW_ITEM_GetInfo(hItem,&info);
- if(info.IsNode)
- {
- fristitem=TREEVIEW_GetItem(pMsg->hWin,hItem,TREEVIEW_GET_FIRST_CHILD);
- TREEVIEW_ITEM_GetText(fristitem,(uint8_t*)buf,20);
- if(strcmp("space",buf)==0)//比较是否为占位符
- {
- TREEVIEW_ITEM_Delete(fristitem);
- tree_add_filelist(num,0,pMsg->hWin,hItem);//这个函数将 遍历磁盘文件添加到树 指定节点
- }
- }
- }
- }
- else
- {
- ((WM_KEY_INFO*)(pMsg->Data.p))->PressedCnt=1;
- TREEVIEW_Callback(pMsg);
- }
- break;
- }
- }
- break;
- case WM_TOUCH:// 这个是处理触摸事件 比较麻烦一点 主要是获得触摸的地方, 遍历TREE 取得被点击的节点 判断是否为 节点 然后的操作就和 按键一样了
- {
- TREEVIEW_ITEM_Handle hNode;
- GUI_PID_STATE* pState;
- TREEVIEW_OBJ *treeobj;
- pState = (GUI_PID_STATE*)pMsg->Data.p;
- treeobj = (TREEVIEW_OBJ*)GUI_ALLOC_h2p(pMsg->hWin);
- if(pState->y/treeobj->Props.MinItemHeight[tr]NumVisItems+1)
- {
- hNode=TREEVIEW_GetItem(pMsg->hWin,0,TREEVIEW_GET_FIRST);
- if(hNode)
- {
- int num=pState->y/treeobj->Props.MinItemHeight +((pState->y%treeobj->Props.MinItemHeight)>0 ?1:0)-1;//计算被点击的是第几个节点
- if(treeobj->ScrollStateV.v>0)
- num+=treeobj->ScrollStateV.v;
- while(num--)//这个while 遍历得到被点击的 节点handle
- {
- TREEVIEW_ITEM_GetInfo(hNode,&info);
- if(info.IsNode)
- {
- if(info.IsExpanded)
- {
- hNode=TREEVIEW_GetItem(pMsg->hWin,hNode,TREEVIEW_GET_FIRST_CHILD);
- }
- else
- {
- hNode=TREEVIEW_GetItem(pMsg->hWin,hNode,TREEVIEW_GET_NEXT_SIBLING);
- }
- }
- else
- {
- hNode=Item_get_next(hNode);
- }
- }
- }
- TREEVIEW_ITEM_GetInfo(hNode,&info);
- if(info.IsExpanded==0)
- {
- if(info.IsNode&&(pState->x<((info.Level+1)*treeobj->Props.Indent-treeobj->ScrollStateH.v))&&
- pState->x>((info.Level)*treeobj->Props.Indent-treeobj->ScrollStateH.v)
- )
- {
- fristitem=TREEVIEW_GetItem(pMsg->hWin,hNode,TREEVIEW_GET_FIRST_CHILD);
- TREEVIEW_ITEM_GetText(fristitem,(uint8_t*)buf,20);
- if(strcmp("space",buf)==0)
- {
- TREEVIEW_ITEM_Delete(fristitem);
- tree_add_filelist(num,0,pMsg->hWin,hNode);
- }
- }
- }
- else
- {
- }
- TREEVIEW_Callback(pMsg);
- }
- }
- break;
- default:
- TREEVIEW_Callback(pMsg);
- break;
- }
- }
复制代码
效果图片
|