`
snzipeng
  • 浏览: 21988 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

[转]循序渐进实现仿QQ界面(四):圆形按钮与工具栏自绘

阅读更多

这一篇本来应该演示如何实现仿QQ界面的中间客户区与底部工具栏,不过在实现底部工具栏的时候发现圆形按钮与工具栏自绘有不少取巧的方法,因此加插这么一篇,讲解一下如何实现圆形按钮和工具栏自绘。

前面几篇都是在讲解如何实现QQ顶部的标题栏,是用窗口贴图实现,也讲到底部区域会用不同的方法实现,因此这里底部的QQ按钮和工具栏不是在主窗口上画图了,而是用控件实现。并且这里讲解的方法不局限于使用RingSDK界面库及实现这个仿QQ界面程序,类似的效果用MFC或API都可以轻易实现。

讲到圆形按钮,大家一定会想到要实现按钮的自绘,然而有取巧的方法,就是用静态文本控件进行模拟,完全不需要自绘。先截个QQ的图作为资源,如下图:



这张图包含了两个按钮,一个收起/展开侧边栏的按钮和弹出主菜单的QQ按钮,两个都是圆形按钮。我们先建一个跟这张图一样大小的无边框的子窗口,用这张图作为窗口背景,想来这个大家都会,用RingSDK界面库只要调用一下SetBkgBitmap就一切搞定。然后建两个静态文本控件,一个13*13大小,一个36*36大小,位置正好覆盖那两个按钮。静态文本控件本来就是透明的,只要不设置文字,两个控件就跟不存在一样,不影响背景,但是占据了位置却正好可以模拟出按钮的动作。当然,这两个控件还要先CreateEllipticRgn,再SetWindowRgn一下,使其成为圆形,这样鼠标就必须进入到这个圆形按钮区域才会有响应动作,这下不用费劲在主窗口判断鼠标位置了:)

首先是模拟鼠标移上去的高亮状态,鼠标移到按钮上,父窗口是没有WM_MOUSEMOVE消息的,因此这里必须要由控件处理这个消息,需要子类化这两个静态文本控件,MFC有个第3方的静态文本控件,类名不记得了,是实现超文本链接的,如果有这个类的话,可以不用自己进行子类化,用这个类就可以了,RingSDK界面库则已经封装了这个功能,只要调用RingStatic::SetHyperlink就可以实现。实现这个超文本链接功能的原理是调用TrackMouseEvent,然后处理WM_MOUSEHOVER和WM_MOUSELEAVE消息,这两个消息会在鼠标移入控件和离开控件各发送一次,因此不需要象前面的贴图按钮一样用个标志记录是否绘制过按钮的各种状态,只需要在WM_MOUSEHOVER消息里绘制按钮的高亮状态,在WM_MOUSELEAVE消息里恢复按钮的原始状态就行了。界面库的RingStatic类因为在这两个消息里有自己的处理,没有把这两个消息转发给父窗口,而是发送了WM_LINKHOVER和WM_LINKLEAVE两个自定义消息,因此演示程序的绘制是在这两个消息里处理,实际是跟WM_MOUSEHOVER和WM_MOUSELEAVE消息一样的。绘制代码很简单,把两张用到的资源图片贴出来,大家一看就知道了,就是把图片的不同区域绘制到窗口。第2张图片采用连续绘制实现了动画显示,因为需要调色的关系,这张图片是实时生成的。

C/C++ code
RINGMSG(WndQQButton,WM_LINKHOVER) { if((HWND)param.wParam == m_btn->Handle()) { int sx = BTN_PICWIDTH; if(m_bIsSideToolHide) sx += m_dibArrBtn.Width()/2; m_dibArrBtn.Draw((BTN_WIDTH - BTN_PICWIDTH)/2,0,sx,0,BTN_PICWIDTH,BTN_PICHEIGHT,BTN_PICWIDTH,BTN_PICHEIGHT); } else if((HWND)param.wParam == m_btnQQ->Handle()) { m_dibQQBtn.Draw(0,0,0,0,QQBTN_WIDTH,QQBTN_HEIGHT,QQBTN_WIDTH,QQBTN_HEIGHT); Sleep(80); m_dibQQBtn.Draw(0,0,QQBTN_WIDTH,0,QQBTN_WIDTH,QQBTN_HEIGHT,QQBTN_WIDTH,QQBTN_HEIGHT); Sleep(80); m_dibQQBtn.Draw(0,0,QQBTN_WIDTH*2,0,QQBTN_WIDTH,QQBTN_HEIGHT,QQBTN_WIDTH,QQBTN_HEIGHT); } return TRUE; } RINGMSG(WndQQButton,WM_LINKLEAVE) { if((HWND)param.wParam == m_btn->Handle()) { int sx = 0; if(m_bIsSideToolHide) sx = m_dibArrBtn.Width()/2; m_dibArrBtn.Draw((BTN_WIDTH - BTN_PICWIDTH)/2,0,sx,0,BTN_PICWIDTH,BTN_PICHEIGHT,BTN_PICWIDTH,BTN_PICHEIGHT); } else if((HWND)param.wParam == m_btnQQ->Handle()) { m_dibQQBtn.Draw(0,0,QQBTN_WIDTH,0,QQBTN_WIDTH,QQBTN_HEIGHT,QQBTN_WIDTH,QQBTN_HEIGHT); Sleep(80); m_dibQQBtn.Draw(0,0,0,0,QQBTN_WIDTH,QQBTN_HEIGHT,QQBTN_WIDTH,QQBTN_HEIGHT); Sleep(80); m_dibBkg.Draw(0,0,QQBTN_X,QQBTN_Y,QQBTN_WIDTH,QQBTN_HEIGHT,QQBTN_WIDTH,QQBTN_HEIGHT); } return TRUE; }



代码里面的m_dibXXX几个对象在初始化的时候设定了绘制目标窗口是两个静态文本控件,因此这里看不到GetDC之类的代码,封装起来了,相应的因为目标DC是两个静态文本控件,坐标也比较好计算。这些代码改成GDI操作也比较容易。静态文本控件已经设置成圆形,DC以外的图象会绘制到父窗口,这样正好满足我们的要求,因为那个小按钮鼠标移上去会有一圈光晕(不是很明显),而这个光晕是在按钮范围以外。

除去初始化创建静态文本控件,图片调色和子类化静态文本控件代码以外,只要响应上面两个消息就实现了QQ2009左下角两个按钮的模拟,是不是很简单?子类化静态文本控件的代码可以去看RingSDK界面库的ringstatic.cpp或者是扩展的MFC超文本链接控件类的代码。QQ2009的这两个按钮没有按下的状态,但是很容易添加实现,演示程序里的相关代码也很容易就可以改成MFC或API的,可以实现自己的比较酷的按钮效果。顺便说一下,创建的静态文本控件必须带上SS_NOTIFY窗口类型,否则点击后父窗口是收不到消息的。父窗口的窗口类型不需要带上WS_CLIPCHILDREN和WS_CLIPSIBLINGS类型,否则不会绘制静态文本控件占据的区域背景。

接下来说一下工具栏的自绘。

实现自绘工具栏一种方法是子类化,自己处理其WM_PAINT消息。但是工具栏按钮的状态实在太多,按钮形态,扁平形态,高亮,按下,不可用等等状态,是否显示文字,是否显示下拉箭头等等,要做出一个通用的自绘工具栏实在是一件很麻烦的事。这里教你一个取巧的办法,以QQ2009的工具栏为例,子类化工具栏是逃不掉的,但是只要处理WM_ERASEBKGND消息,刷上你要的背景就可以了,不需要处理WM_PAINT消息。先创建工具栏,带上TBSTYLE_FLAT类型,扁平按钮,然后准备好如下两张图片:



接下来,下面所列出的代码是调用的API:

C/C++ code
HBITMAP = LoadBitmap(...); HIMAGELIST himg = ImageList_Create(20,20,ILC_COLOR32|ILC_MASK,9,4); //这里的参数请根据图片自行调整 ImageList_AddMasked(himg,hbm,0x00FF00FF); //指定紫红为透明色 SendMessage(hWndToolbar,TB_SETIMAGELIST,0,(LPARAM)himg);



另一张图片如法炮制,SendMessage(m_hWndToolbar,TB_SETHOTIMAGELIST,0,(LPARAM)himg);设置为高亮图案。然后发送TB_INSERTBUTTON消息给工具栏添加按钮,TBBUTTON结构的iBitmap设置为图片中相应按钮图案的序号(从0开始),这样生成的工具栏按钮图案是不局限于256色的。鼠标移到按钮上,就显示为第2张图相应的高亮图案,但是按钮周围还是有一个边框,这是系统帮你画的,高亮图案上已经画了个漂亮的边框,我们不需要这个边框,怎么去掉呢,父窗口响应NM_CUSTOMDRAW的WM_NOTIFY消息:

C/C++ code
case WM_NOTIFY: { LPNMHDR lpnm = (LPNMHDR)lParam; if(lpnm->code == NM_CUSTOMDRAW) { LPNMCUSTOMDRAW lpnmdraw = (LPNMCUSTOMDRAW)lParam; if(lpnmdraw->dwDrawStage == CDDS_PREPAINT) return CDRF_NOTIFYITEMDRAW; //指定通知按钮自绘 else if(lpnmdraw->dwDrawStage == CDDS_ITEMPREPAINT) return TBCDRF_NOEDGES; //其余交给系统绘制,指定不需要绘制按钮外边框 } break; }


要求自绘按钮时返回TBCDRF_NOEDGES,系统就不绘制按钮边框了,这样这个工具栏乍看就跟QQ2009的工具栏一样了。但是按钮按下去的状态就露馅了,因为高亮图案上画的边框也跟着图标一起按下去了,这个边框毕竟不是真的边框,只是高亮图案罢了。当然如果这个效果你能接受,那也可以了,至此打住,可以省不少事。

现在要使按钮按下去仅按下图标,边框位置不动。可是工具栏只能设置两个IMAGELIST,没有按下状态的IMAGELIST可以设置,因此只能自绘,就需要判断lpnmdraw->uItemState标志,如果是CDIS_SELECTED,就自绘按钮的按下状态,可是要自绘按钮,又很麻烦了,画边框,画图标,画文字...,嘿嘿,这里又有取巧的方法,我们只要画个按下状态的边框就可以了,还是返回TBCDRF_NOEDGES,其他的系统会帮我们画的。不过系统帮我们画上去的是高亮图案,又有一个按下去的边框,所以我们要取消高亮图案,创建工具栏的时候只需要发送TB_SETIMAGELIST消息设置按钮图标就可以了,高亮和按下的边框我们自己画,准备好下面一张图:



左边是高亮边框,右边是按下的边框。自绘时根据lpnmdraw->uItemState标志把这张图的相应边框画上去:
C/C++ code
case WM_NOTIFY: { LPNMHDR lpnm = (LPNMHDR)lParam; if(lpnm->code == NM_CUSTOMDRAW) { LPNMCUSTOMDRAW lpnmdraw = (LPNMCUSTOMDRAW)lParam; if(lpnmdraw->dwDrawStage == CDDS_PREPAINT) return CDRF_NOTIFYITEMDRAW; //指定通知按钮自绘 else if(lpnmdraw->dwDrawStage == CDDS_ITEMPREPAINT) { int sx; int off = (lpnmdraw->rc.bottom - lpnmdraw->rc.top - 20)/2; if((lpnmdraw->uItemState & CDIS_SELECTED)) sx = 20; else if((lpnmdraw->uItemState,CDIS_HOT)) sx = 0; else return TBCDRF_NOEDGES; HDC hMemDC = CreateCompatibleDC(lpnmdraw->hdc); SelectObject(hMemDC,hbm); //hbm为事先加载的边框图案的HBITMAP BitBlt(lpnmdraw->hdc,lpnmdraw->rc.left + off,lpnmdraw->rc.top + off,20,20,hMemDC,sx,0,SRCCOPY); DeleteDC(hMemDC); return TBCDRF_NOEDGES; //其余交给系统绘制,指定不需要绘制按钮外边框 } } break; }


OK,就这么简单,现在这个工具栏就跟QQ2009的工具栏的效果一样了。

现在说说演示程序里的工具栏实现,代码差不多,但是实际复杂得多,因为界面库对此进行了封装。界面库实现了任意子窗口和控件都可以停靠,因此QQ按钮窗口和工具栏是作为停靠窗口停靠在主界面的下方,只是设置了不能拖动。因为是停靠窗口,所以你在主窗口的WM_SIZE消息里看不到移动QQ按钮窗口和工具栏的代码,封装起来了。这个不是本篇重点,有兴趣的可以去看界面库里ringdocksite.cpp的代码。

现在看看程序的截图:



变化不是很大,本篇旨在教大家一个自绘按钮和自绘工具栏的比较简单的方法,不知道说清楚没有,有问题或是不同意见欢迎留言讨论。下一篇会实现QQ2009的客户区域和左下角按钮弹出的异型菜单。

演示程序下载地址:http://d.download.csdn.net/down/2025754/ringphone

分享到:
评论

相关推荐

    循序渐进实现仿QQ界面(四)

    在本教程中,我们将深入探讨如何“循序渐进实现仿QQ界面(四)”。这个教程是基于VC6,一个经典的Microsoft Visual C++ 6.0集成开发环境,虽然现在可能较旧,但它仍然是学习Windows应用程序开发的良好平台。在本阶段...

    仿QQ界面程序(2)

    缺头文件的,请到下面文章的地址去看。 循序渐进实现仿QQ界面(二):贴图按钮的三态模拟的配套源码,文章请见:http://blog.csdn.net/ringphone/archive/2010/01/10/5171490.aspx

    仿QQ界面程序(5)

    编译有问题的,请到以下地址去看: ... 循序渐进实现仿QQ界面(五):半透明窗体与不透明控件的配套源码,文章请见: http://blog.csdn.net/ringphone/archive/2010/02/11/5306231.aspx

    QT实现高仿QQ QT实现QQ界面

    本项目"QT实现高仿QQ QT实现QQ界面"旨在通过QT框架来复刻QQ的经典界面,从而展示如何利用QT库来创建类似QQ这样的即时通讯软件的用户界面。下面将详细介绍这个项目可能涉及的关键知识点: 1. **QT Widgets模块**:QT...

    一个简易仿qq登录界面

    实现一个简易仿qq登录界面,要求实现: 1) 登录界面有帐号、密码文本和编辑框,登录和退出按钮。在程序中维护一个帐号密码的数组,用以判断正确登录与否。如果登录成功,则进入qq主界面,否则清空帐号和密码编辑框,...

    C# 仿qq界面

    7. **网络通信**:为了让仿QQ界面具有实际功能,还需要实现客户端与服务器之间的通信。这涉及到网络编程,可以使用C#的Socket类进行TCP/IP通信,或者使用WebSocket等高级API来处理实时数据交换。 8. **多线程**:...

    仿QQ登录界面 实现QQ的登陆界面

    有以下功能; 1.实现了对话框伸缩 2.实现QQ号码的添加 3.具有简单的与ACCESS数据库连接验证的功能 4.增加一点输入的音效效果 附加说明: ...未实现功能: 圆角按钮:不知是位图还是什么其它的!...仿QQ登录界面

    仿qq界面实现(仿照qq2008版界面实现)

    1. **窗口布局**:QQ2008的界面布局清晰,通常包括菜单栏、工具栏、聊天窗口、联系人列表等部分。我们可以使用Qt中的QLayout类来管理这些控件的位置和大小。例如,我们可以使用QHBoxLayout和QVBoxLayout来实现水平和...

    仿QQ,outlook工具栏控件

    "仿QQ, Outlook工具栏控件"是一个专为Visual Basic(VB)开发者设计的组件,旨在模仿QQ和Outlook电子邮件客户端的工具栏设计,使得在VB应用中可以创建类似的专业级界面。 工具栏控件在应用程序中扮演着导航和快捷...

    循序渐进学java视频教程之山寨版QQ

    资源名称:循序渐进学java视频教程之山寨版QQ资源目录:【】abbr_2b0a956c6deaffd161055bcc69352490【】abbr_69fe1d0216f529000a178ba7a943dbd8【】abbr_83be30c3d0d895e0b4887fe4e9c049f2【】abbr_89247c1a9c971d54...

    仿QQ登录界面

    【标题】"仿QQ登录界面"是一个项目,旨在利用MFC(Microsoft Foundation Classes)库来创建一个类似于腾讯QQ的登录窗口。MFC是微软为Windows应用程序开发提供的一组C++类库,它使得开发者能够方便地构建用户界面,...

    winform高仿qq登陆界面

    首先,"winform高仿qq登陆界面"这个标题暗示了我们需要使用Microsoft的Windows Forms(WinForms)技术来构建一个与腾讯QQ登录界面类似的用户界面。WinForms是.NET Framework的一部分,用于开发桌面应用程序,提供了...

    仿QQ界面程序(6)

    编译有问题的,请到以下地址去看: ... 循序渐进实现仿QQ界面(六):异型菜单与内建滚动条自绘的配套源码,文章请见: http://blog.csdn.net/ringphone/archive/2010/03/13/5377522.aspx

    韩顺平.循序渐进学.java.从入门到精通-之一PPT.pp

    韩顺平.循序渐进学.java.从入门到精通-之一PPT.pp

    纯Windows API实现仿QQ界面

    1. **窗口样式与控件**:QQ界面包含多个窗口和控件,如标题栏、菜单栏、工具栏、聊天窗口等。我们可以使用`CreateWindow`函数为这些元素分别创建窗口,并设置相应的窗口样式。例如,`WS_OVERLAPPEDWINDOW`创建一个...

    QT实现仿QQ界面源码

    在"QT实现仿QQ界面源码"这个项目中,我们可以学习到如何使用QT来设计和实现类似QQ的用户界面。QQ作为一款流行的即时通讯软件,其用户界面设计独特且用户体验良好,因此,模仿QQ界面可以作为一个很好的学习实践案例,...

    仿QQ聊天界面

    1. **界面布局**:仿QQ聊天界面首先要考虑的是布局设计,通常包括顶部的标题栏、左侧的好友列表、中间的聊天窗口以及底部的输入框。标题栏通常包含应用的logo、昵称、设置等选项,而聊天窗口则分为发送和接收的消息...

    仿QQ界面UI

    【标题】"仿QQ界面UI" 是一个针对移动应用用户界面设计的话题,它涉及到的是如何模仿QQ这款流行的即时通讯应用的界面风格和交互设计。在Android开发中,创建这样一个界面通常意味着开发者会关注布局设计、图标设计、...

    Android仿QQ界面

    在Android平台上,模仿QQ界面是一项常见的练习,旨在帮助开发者熟悉Android UI设计以及如何实现与QQ类似的交互效果。这个项目中的代码简洁易懂,是初学者深入理解Android开发的好材料。以下将详细介绍在这个过程中...

Global site tag (gtag.js) - Google Analytics