本篇演示实现仿QQ界面的异型菜单与滚动条自绘。
先讲解一下如何实现QQ的圆角菜单,这个要用到HOOK了,因为菜单是一种特殊的窗口,无法用FindWindow或通过HMENU来获取到窗口句柄,也就无法子类化。必须下钩子,这里下的是WH_CALLWNDPROC的钩子:
C/C++ code
BOOL QQMenu::InstallHook()
{
if(m_hMenuHook == NULL)
m_hMenuHook = SetWindowsHookEx(WH_CALLWNDPROC,MenuHook,GetInstance(),GetCurrentThreadId());
return (BOOL)m_hMenuHook;
}
LRESULT CALLBACK QQMenu::MenuHook(int code,WPARAM wParam,LPARAM lParam)
{
PCWPSTRUCT lpCwp = (PCWPSTRUCT)lParam;
if(code == HC_ACTION && lpCwp->message == WM_CREATE)
{
//检测是否是菜单
TCHAR szClassName[16];
int nCount = GetClassName(lpCwp->hwnd,szClassName,12);
if(nCount == 6 && lstrcmpi(szClassName,_T("#32768")) == 0)
{
QQMenu* pMenu = (QQMenu*)GetWindowPtr(lpCwp->hwnd);
if(pMenu == NULL)
{
//未子类化
pMenu = window->NewRingObj(pMenu,TRUE);
pMenu->Attach(lpCwp->hwnd);
}
}
}
return CallNextHookEx(m_hMenuHook,code,wParam,lParam);
}
查MSDN知道菜单的窗口类名是“#32768”,找到菜单窗口就好办了,子类化后就可以做手脚了,做的手脚不多,只需要实现圆角矩形外形就够了,菜单的自绘早已实现,是交由主窗口处理的,就不必费手脚了。
首先是WM_NCCALCSIZE消息,需要加大其非客户区,为画圆角腾出空间来,但是注意腾出了空间画圆角,客户区的面积就缩小了,有可能会使菜单项显示不全,因此需要在WM_WINDOWPOSCHANGING消息里把吃掉的客户区空间再吐出来,就是加大菜单窗口的尺寸,这样才不影响菜单项的显示。怎么实现及绘制圆角矩形窗口这个系列文章的第一篇就讲过了,这里就不多费口舌了。
回答一下有位朋友在我博客前面文章的留言,为什么子类化菜单后截不到WM_MOUSEMOVE消息,试了一下的确没有,其实菜单的WM_MOUSEMOVE消息被替换掉了,变成了MN_MOUSEMOVE消息,值为0x01EE,定义看这个链接:http://topic.csdn.net/t/20050713/18/4142641.html
接下来讲解怎么实现QQ左下角按钮上弹出来的异型菜单,一般做法是自己实现一个异型窗口然后加载菜单,不过既然是HOOK了菜单,还是直接在菜单上实现比较好。首先一个问题是怎么区分这个主菜单和其他圆角菜单,才好实现不同的处理。方法是主菜单创建的时候调用SetMenuInfo为其设置一个值,以后可以通过GetMenuInfo来检测这个值以鉴别是主菜单:
C/C++ code
//设置数据标识QQ主菜单
MENUINFO mf;
mf.cbSize = sizeof(MENUINFO);
mf.fMask = MIM_MENUDATA;
mf.dwMenuData = ID_QQMENU;
m_rmQQ->SetMenuInfo(&mf);
鉴别出主菜单后就可以实现不同的外形处理了,这个菜单的外形可以有两种方案,一个是带上QQ圆形按钮的外形,弹出时盖住QQ按钮。不过考虑到QQ图案不能调色,绘制时要多道手脚专门画那个企鹅,还是省点事采用挖掉那个圆形的外形,如下图(左边的图像):
由于菜单项是可增减的,因此这个外形必须动态生成,几何学得好的话可以用线条绘制出来,我是属于几何学得不好的,早忘光了:P,因此采用圆角矩形和图片合成的方式来构造这个外形。看上图中的右图,兰色边框勾勒出了圆角矩形,可以用CreateRoundRectRgn生成,红色虚线勾出了需要在这个圆角矩形区域挖掉和添加的部分,黄色是这两个HRGN相交叠的部分,这块区域是固定尺寸不会变动的,因此可以用图片实现,根据这个图来生成HRGN。这张图片如下:
形状有点怪异,黑色部分就是需要生成的HRGN形状,生成HRGN后可以调用CombineRgn来与圆角矩形的HRGN合并,参数采用RGN_XOR:
C/C++ code
CombineRgn(m_hrgn,m_hrgn,hrgn,RGN_XOR);
m_hrgn是圆角矩形,hrgn是图中黑色部分形状的HRGN,采用RGN_XOR参数的效果是:合并两个形状,去掉重叠的部分,结果就是圆角矩形在左下角挖掉了图中半圆的形状,添加了下面的圆弧三角,恰好符合QQ主菜单的外形。
下面讲解一下如何根据图片来生成HRGN,原理是逐行扫描像素,发现是白色(透明色)不处理,发现是黑色(非透明色)就根据连续的像素数量调用CreateRectRgn来生成高度为1的HRGN,每一扫描行可能会有一些不连续的点或线,可以通过CombineRgn,采用RGN_OR参数把这些不连续的HRGN合并起来,然后把整个图象所有扫描行生成的这些HRGN再合并,最终就得到了图片中指定形状的HRGN,代码有点复杂,这里就不贴了,可以去看RingSDK图象库libsrc\ringdib\rdib.cpp里面的RingDIB::CreateRgn函数代码。
得到最终的HRGN后就可以通过SetWindowRgn实现异型菜单了,当然还要准备好窗口非客户区的贴图,左下角的贴图如下:
怎么贴图绘制就不说了,前面的文章都讲解过了。
接下来就是菜单的弹出问题,计算好位置弹出,貌似可以了,其实还是有工作要做,把窗口移到桌面上方,再点按钮弹出菜单,晕,菜单是往下弹出了,缺口跑到主界面下方去了,必须限制菜单的弹出方向,这时要用到TrackPopupMenuEx函数的最后一个参数LPTPMPARAMS了,这个参数一般调用时是指定为NULL,其实是可以用这个参数限制菜单的弹出方向的,这个参数说明如下:
C/C++ code
typedef struct tagTPMPARAMS {
UINT cbSize;
RECT rcExclude;
} TPMPARAMS, *LPTPMPARAMS;
cbSize :Size of structure, in bytes.
rcExclude :Rectangle to exclude when positioning the window, in screen coordinates
指定rcExclude的坐标,菜单弹出时就会避开这个区域,我们把主窗口下方的空间坐标赋给这个rcExclude,主菜单弹出时为了避免覆盖这个坐标,就只能往上弹出,这下终于达到我们期望的效果了。不过且慢,这个rcExclude只是个矩形,我们拦住了菜单不往下弹,却拦不住左右,把窗口移到屏幕最右边,再弹出菜单,继续晕,菜单往左弹出了!缺口跑到了左边,又露馅了。这下没有取巧的办法,只能实现菜单外形的左右翻转,同时因为左右翻转,还需要调整菜单的弹出位置以使缺口套准QQ圆形按钮。弹出菜单的代码如下:
C/C++ code
RINGCMD(WndQQButton,OnQQMenuPopup)
{
if(event == STN_CLICKED)
{
RECT rc,rcEx;
int xoff = 0;
GetWindowRect(m_hWnd,&rc);
rc.left += QQBTN_X;
rc.bottom -= (window->m_dibQQCorner.Height() + QQBTN_QQ_HEIGHT + 3);
//菜单如果靠屏幕右边弹出,需要横向翻转其不规则外形,则需要调整其弹出位置的X坐标
if(rc.left + QQMENU_WIDTH > WINVAR(SM_CXSCREEN))
xoff = QQMENU_OFFSET;
//限制菜单弹出位置,使其始终向上弹出。
rcEx.left = 0;
rcEx.right = rc.right;
rcEx.top = rc.bottom + 1;
rcEx.bottom = WINVAR(SM_CYSCREEN);
//先置标记通知刷新,绘制QQ按钮上的企鹅
m_bMenuPopup = TRUE;
InvalidateRect(m_hWnd,NULL,TRUE);
//弹出菜单,设定避开rcEx指定的坐标
window->m_rmQQ->PopupEx(m_hWnd,rc.left+xoff,rc.bottom,FALSE,&rcEx);
}
}
翻转菜单不规则外形,其实是先把左下角HRGN图象横向翻转,再生成HRGN。图象横向翻转其实就是把图象的每一行数据数组进行倒序,属于C语言的入门功课了,这里就不说了。
现在终于实现了QQ的异型主菜单,剩下最后好友列表区的滚动条了。
自绘滚动条的实现:
自绘滚动条其实很简单,看过这个系列前面的文章大家应该会了,子类化后贴图不是很难。但是QQ好友列表区的滚动条自绘却不是那么简单,因为好友列表是用ListBox控件实现,而ListBox的滚动条是内建的,是画出来的,根本不是窗口!没办法子类化,这下完蛋,只能子类化ListBox,自己实现绘制滚动条的代码。然而有个要命的地方,ListBox绘制滚动条既没提供接口,也没提供消息,光在WM_NCPAINT里面绘制没用,有很多消息都是直接进行了滚动条的绘制,这下子就需要围追堵截,把可能绘制滚动条的消息全部截过来自己处理。除了WM_NCPAINT,需要拦截的消息如下:
WM_STYLECHANGED
WM_LBUTTONDBLCLK
WM_NCLBUTTONDBLCLK
WM_TIMER
WM_LBUTTONDOWN
WM_NCLBUTTONDOWN
WM_LBUTTONUP
WM_MOUSEWHEEL
WM_SIZE
WM_VSCROLL
那个晕哪!有这工夫还不如自己写个窗口实现了。事实也确实是,研究这些消息的时间够自己写个窗口实现了。不过实现ListBox的滚动条自绘还是有意义的,一个个消息来吧。
首先当然要实现WM_NCPAINT消息,把滚动条画出来,这个不难。实现之后窗口被其他程序覆盖,再切换到前台,滚动条象样子了,不过滚动条一动就露馅了。
然后是WM_VSCROLL,ListBox有个窗口类型是LBS_DISABLENOSCROLL,说明没有滚动条,窗口还是能滚动的,因此在这个消息里面,先把窗口的WS_VSCROLL类型去掉,然后调用默认的窗口过程,再把WS_VSCROLL类型设置回来。现在效果是按翻页滚动不会露馅,发送WM_VSCROLL消息设定滚动条位置不会露馅。
C/C++ code
case WM_VSCROLL:
{
ReplaceStyle(WS_VSCROLL,0);
LRESULT res = RingListBox::RingdowProc(m_hWnd,param);
ReplaceStyle(0,WS_VSCROLL);
return res;
}
WM_SIZE消息做同样处理,这下调整窗口大小也不会露馅了。
WM_STYLECHANGED,WM_LBUTTONDBLCLK,WM_NCLBUTTONDBLCLK三个消息拦截掉,直接return 0;这下在窗口上双击不会露馅了。
WM_LBUTTONDOWN,因为设定了ListBox的LBS_NOINTEGRALHEIGHT类型,因此ListBox的最下面一个可视的选项有可能是显示不完全的,如果选择了这个选项,ListBox会滚动窗口让这个选项完全显示,这时会重绘滚动条,因此需要在这个消息里检测如果选中的是没有显示完整的选项,就发送wParam为SB_LINEDOWN的WM_VSCROLL消息让其显示完全,然后选中该选项,return 0;其余情况交给默认的窗口过程处理。
WM_MOUSEWHEEL:计算好要滚动的位置,然后连着发送下面两个消息就OK了。
C/C++ code
SendMessage(m_hWnd,WM_VSCROLL,MAKELONG(SB_THUMBPOSITION,nPos),0);
SendMessage(m_hWnd,WM_VSCROLL,SB_ENDSCROLL,0);
接下来就来到最困难的部分,点击滚动条上的上下箭头和拖动滑块。这个需要先处理WM_NCLBUTTONDOWN和WM_LBUTTONUP消息,WM_NCLBUTTONDOWN消息里面先检测鼠标是否点在了滚动条上,是就要SetCapture捕获鼠标,如果是按在上下箭头或不在滑块上,就设定一下定时器,在WM_TIMER消息里发送wParam为SB_LINEUP,SB_LINEDOWN,SB_PAGEUP,SB_PAGEDOWN的WM_VSCROLL消息,可以连续滚动。WM_LBUTTONUP消息里ReleaseCapture,清除定时器,再发送wParam为SB_ENDSCROLL的WM_VSCROLL消息终止滚动。
最难的就是拖动滑块的处理了,必须自己绘制滑块位置,定时器检测并绘制滑块位置,当检测到滑块移动的距离足够需要滚动窗口的时候,就需要计算出滚动条的滑块位置并发送wParam为SB_THUMBPOSITION的WM_VSCROLL消息,ListBox的滚动条滚动范围(GetScrollRange返回的值)是选项总数-1,绘制时是按像素,因此计算时难免会有误差,会造成滑块拖动时的偶尔抖动,不过总算是实现了ListBox内建的滚动条自绘,演示程序就先这样了,要想达到商用级别尚需进行代码优化和调整。
现在看看程序的截图:
终于完成了,长吁一口气,在此谢谢大家一直以来的支持和鼓励。
演示程序下载地址:http://download.csdn.net/source/2125267
说明:在编写这个演示程序的时候发现和修正了RingSDK的几个BUG,因此编译这个演示程序需要更新到最新版本的RingSDK,不更新的话主菜单和部分调色功能可能会不正常,不过不影响效果演示。这个演示程序的源代码也已经提交到SVN,更新RingSDK的时候会自动下载下来,可以不用去下演示程序。
分享到:
相关推荐
在本教程中,我们将深入探讨如何“循序渐进实现仿QQ界面(四)”。这个教程是基于VC6,一个经典的Microsoft Visual C++ 6.0集成开发环境,虽然现在可能较旧,但它仍然是学习Windows应用程序开发的良好平台。在本阶段...
编译有问题的,请到以下地址去看: ... 循序渐进实现仿QQ界面(六):异型菜单与内建滚动条自绘的配套源码,文章请见: http://blog.csdn.net/ringphone/archive/2010/03/13/5377522.aspx
编译有问题的,请到以下地址去看: ... 循序渐进实现仿QQ界面(五):半透明窗体与不透明控件的配套源码,文章请见: http://blog.csdn.net/ringphone/archive/2010/02/11/5306231.aspx
在Android应用开发中,"fragment+viewpager 仿qq界面"是一个常见的需求,它涉及到对Android UI组件的深入理解和巧妙组合。在这个项目中,我们主要会利用`Fragment`和`ViewPager`来创建一个类似QQ应用的多页面切换...
【标题】:“完整代码,是类似QQ滚动菜单的表单界面” 这个标题表明我们讨论的是一个Visual FoxPro(VFP)编程项目,它涉及到创建一个模仿QQ应用的滚动菜单表单界面。QQ作为一款流行的即时通讯软件,其用户界面设计...
缺头文件的,请到下面文章的地址去看。 循序渐进实现仿QQ界面(二):贴图按钮的三态模拟的配套源码,文章请见:http://blog.csdn.net/ringphone/archive/2010/01/10/5171490.aspx
本项目"QT实现高仿QQ QT实现QQ界面"旨在通过QT框架来复刻QQ的经典界面,从而展示如何利用QT库来创建类似QQ这样的即时通讯软件的用户界面。下面将详细介绍这个项目可能涉及的关键知识点: 1. **QT Widgets模块**:QT...
在IT行业中,用户界面的设计和用户体验是至关重要的,滚动条作为网页或软件界面的一个基本元素,它的设计和功能实现往往直接影响到用户的交互感受。QQ软件作为一款广泛应用的即时通讯工具,其滚动条的设计自然需要...
资源名称:循序渐进学java视频教程之山寨版QQ资源目录:【】abbr_2b0a956c6deaffd161055bcc69352490【】abbr_69fe1d0216f529000a178ba7a943dbd8【】abbr_83be30c3d0d895e0b4887fe4e9c049f2【】abbr_89247c1a9c971d54...
实现一个简易仿qq登录界面,要求实现: 1) 登录界面有帐号、密码文本和编辑框,登录和退出按钮。在程序中维护一个帐号密码的数组,用以判断正确登录与否。如果登录成功,则进入qq主界面,否则清空帐号和密码编辑框,...
【标题】"仿QQ登录界面"是一个项目,旨在利用MFC(Microsoft Foundation Classes)库来创建一个类似于腾讯QQ的登录窗口。MFC是微软为Windows应用程序开发提供的一组C++类库,它使得开发者能够方便地构建用户界面,...
7. **网络通信**:为了让仿QQ界面具有实际功能,还需要实现客户端与服务器之间的通信。这涉及到网络编程,可以使用C#的Socket类进行TCP/IP通信,或者使用WebSocket等高级API来处理实时数据交换。 8. **多线程**:...
自绘MFC各种基本控件,包括png,jpeg库,文本框、编辑框、滚动条、滑块、按钮、进度条、菜单、富文本框、单选框、列表框、对话框等常用控件 . 网上搜集的vc++对话框自绘例子集合,包含qq界面,云界面,天气预报界面...
在本文中,我们将深入探讨如何使用C++编程语言来实现一个仿照QQ2008版界面的应用程序。QQ作为一款流行的即时通讯软件,其用户界面设计独特且易用,吸引了大量的用户。为了重现这样的用户体验,我们需要理解界面设计...
韩顺平.循序渐进学.java.从入门到精通-之一PPT.pp
循序渐进实现仿QQ界面.rar 音乐播放器 .rar 用C写的播放器,超强.rar 优美菜单.rar 注册表监控程序.rar 自绘button.zip 显示网络流量.rar 树控件拖动.zip 完全自绘软件更换皮肤例子.rar 网络电视软件.zip
首先,"winform高仿qq登陆界面"这个标题暗示了我们需要使用Microsoft的Windows Forms(WinForms)技术来构建一个与腾讯QQ登录界面类似的用户界面。WinForms是.NET Framework的一部分,用于开发桌面应用程序,提供了...
有以下功能; 1.实现了对话框伸缩 2.实现QQ号码的添加 3.具有简单的与ACCESS数据库连接验证的功能 4.增加一点输入的音效效果 附加说明: ...未实现功能: 圆角按钮:不知是位图还是什么其它的!...仿QQ登录界面
这是一项针对软件界面设计与实现的挑战,旨在模仿QQ的经典外观和交互体验,使得用户在使用这款应用时能够感受到与QQ相似的界面风格。 【描述】中的"精仿QQ的界面,C++无错版,开发要求vc6+win"进一步指出了项目的...