QQ揭秘:如何实现托盘闪动消息提醒?【低调赠送:QQ高仿版GG 4.1 最新源码】
当QQ收到好友的消息时,托盘的图标会变成好友的头像,并闪动起来,点击托盘,就会弹出与好友的聊天框,随即,托盘恢复成QQ的图标,不再闪动。当然,如果还有其它的好友的消息没有提取,托盘的图标会变成另一个好友的图标,并继续闪动。那么,QQ的这一效果是如何实现的了?我在QQ高仿GG2014中实现了同样的效果,这里我就详细地介绍一下。另外,文末最后会奉上GG最新版本4.1的源码,这次甚至包含了JustLib项目的源码哦!
想要直接下载体验的朋友请点击:“下载中心”
一.TwinkleNotifyIcon的实现原理
这个会闪动的托盘图标,我将其定义为一个组件TwinkleNotifyIcon,我们先看TwinkleNotifyIcon的类图:
从TwinkleNotifyIcon类图,我们已经可以看出大致的实现方案:
(1)TwinkleNotifyIcon 内部使用了NotifyIcon,以显示在右下角的托盘。
(2)使用一个Timer定时器来控制托盘的闪动。
(3)使用一个队列friendQueue来存放待提取的好友消息。
(4)使用另一个队列groupQueue来存放待提取的群消息。
(5)当网络引擎接收到一个好友/群消息时,我们就调用PushFriendMessage/PushGroupMessage方法,将其压入friendQueue/groupQueue,并开始闪动图标。
(6)当托盘被点击时,就从Queue中提取最早的一个消息,并将其交给对应的聊天窗口去处理。
二.TwinkleNotifyIcon 实现要点
我们顺着以下的顺序来研究TwinkleNotifyIcon的实现代码,就很容易了:
(1)压入好友/群消息。
(2)点击托盘,提取消息。
(3)重新判断Queue中是否还有待提取的消息,以设置托盘的状态。
1. 压入消息
我们以PushFriendMessage方法为例,PushGroupMessage的道理是一样的。
public void PushFriendMessage(string userID, int informationType, byte[] info, object tag)
{
lock (this.locker)
{
try
{
this.twinkleNotifySupporter.PlayAudioAsyn(); //播放消息提示音
//首先查看是否已经存在对应的聊天窗口
IChatForm form = this.twinkleNotifySupporter.GetExistedChatForm(userID);
if (form != null)
{
form.HandleReceivedMessage(informationType, info, tag);
return;
} //接下来准备将消息压入queue
UnhandleFriendMessageBox cache = null;
lock (this.locker)
{
//先查看queue中目标好友对应的Cache是否存在
for (int i = ; i < this.friendQueue.Count; i++)
{
if (this.friendQueue[i].User == userID)
{
cache = this.friendQueue[i];
break;
}
} if (cache == null) //如果不存在,则为好友新建一个Cache
{
cache = new UnhandleFriendMessageBox(userID);
this.friendQueue.Add(cache);
//触发UnhandleMessageOccured事件
if (this.UnhandleMessageOccured != null)
{
this.UnhandleMessageOccured(UnhandleMessageType.Friend, userID);
}
} cache.MessageList.Add(new Parameter<int, byte[], object>(informationType, info, tag));
} string userName = this.twinkleNotifySupporter.GetFriendName(userID);
this.notifyIcon1.Text = string.Format("{0}({1}) {2}条消息", userName, userID, cache.MessageList.Count);
//获取好友的头像,将其作为托盘图标
this.twinkleIcon = this.twinkleNotifySupporter.GetHeadIcon(userID);
this.ControlTimer(true); //启动闪烁
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}
}
(1)在压入消息的时候,先要播放消息提示音,以通知使用者收到了新的消息。
(2)首先要判断消息的来源好友是否正在和自己聊天(已经打开了与对方的聊天窗口),如果是,则直接将消息交给聊天窗口去显示。否则,进入下一步。
(3)看当前的队列中是否已经存在了目标好友的Cache,因为可能已经有了待提取的来自该好友的消息了。如果已经存在这个Cache,则将消息直接放入Cache,否则,为之新建一个,再放入。
(4)之所以要触发UnhandleMessageOccured事件,是为了通知外面,有待提取的消息出现了。比如,MainForm就会预定这个消息,然后使得好友列表中对应的头像闪动。
void notifyIcon_UnhandleMessageOccured(UnhandleMessageType type, string friendOrGroupID)
{
if (type == UnhandleMessageType.Friend)
{
this.friendListBox1.SetTwinkleState(friendOrGroupID, true);
this.recentListBox1.SetTwinkleState(friendOrGroupID, false, true);
return;
} if (type == UnhandleMessageType.Group)
{
this.groupListBox.SetTwinkleState(friendOrGroupID, true);
this.recentListBox1.SetTwinkleState(friendOrGroupID, true, true);
return;
}
}
上面的UnhandleMessageOccured事件处理函数,不仅仅使得好友列表中对应的头像闪动,即使是最近联系人中,如果存在目标好友,也会使其头像闪动。
(5)将托盘图标设置为目标好友的头像,并将ToolTip设置为好友的名称及待提取的消息数量。
(6)使用定时器闪动图标。
2.点击托盘,提取消息
如果托盘正在闪动,表明有待提取的消息,此时点击托盘,将提取队列中最早压入的好友的消息。
void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
{
try
{
if (e.Button != MouseButtons.Left)
{
return;
} lock (this.locker)
{
if (this.friendQueue.Count > )
{
UnhandleFriendMessageBox cache = this.friendQueue[];
this.friendQueue.RemoveAt();
IChatForm form = this.twinkleNotifySupporter.GetChatForm(cache.User);
if (form != null) //如果为null,表示刚删除好友
{
form.HandleReceivedMessage(cache.MessageList);
} this.DetectUnhandleMessage(); if (this.UnhandleMessageGone != null)
{
this.UnhandleMessageGone(UnhandleMessageType.Friend, cache.User);
}
return;
} if (this.groupQueue.Count > )
{
UnhandleGroupMessageBox cache = this.groupQueue[];
this.groupQueue.RemoveAt();
IGroupChatForm form = this.twinkleNotifySupporter.GetGroupChatForm(cache.Group);
form.HandleReceivedMessage(cache.MessageList); this.DetectUnhandleMessage(); if (this.UnhandleMessageGone != null)
{
this.UnhandleMessageGone(UnhandleMessageType.Group, cache.Group);
}
return;
}
} if (this.MouseClick != null)
{
this.MouseClick(sender, e);
}
}
catch (Exception ee)
{
MessageBox.Show(ee.Message + " - " + ee.StackTrace);
}
}
(1)从上面代码执行的顺序来看,是优先提取好友消息,当所有的好友消息提取完后,才提取群消息。
(2)提取消息时,会调用twinkleNotifySupporter的GetChatForm方法来创建与目标好友的聊天窗口,并将提取的消息交给这个窗口去处理。
(3)当一个好友的消息被提取后,会触发UnhandleMessageGone事件,以通知外部消息已经被提取了。比如,MainForm就会预定这个消息,然后使得好友列表中对应的头像不再闪动。
void notifyIcon_UnhandleMessageGone(UnhandleMessageType type, string friendOrGroupID)
{
if (type == UnhandleMessageType.Friend)
{
this.friendListBox1.SetTwinkleState(friendOrGroupID, false);
this.recentListBox1.SetTwinkleState(friendOrGroupID, false, false);
return;
} if (type == UnhandleMessageType.Group)
{
this.groupListBox.SetTwinkleState(friendOrGroupID, false);
this.recentListBox1.SetTwinkleState(friendOrGroupID, true, false);
return;
}
}
(4)同时,会重新扫描队列中待提取消息的状况,重设托盘图标的状态,这就是DetectUnhandleMessage方法做的事情。
3.重新判断Queue中是否还有待提取的消息,以设置托盘的状态
每当有消息被提取后,我们都需要重新扫描Queue中是否还有其它的待提取消息,DetectUnhandleMessage方法会被经常调用。
private void DetectUnhandleMessage()
{
if (this.friendQueue.Count == && this.groupQueue.Count == )
{
this.ControlTimer(false);
}
else if (this.friendQueue.Count > )
{
UnhandleFriendMessageBox cache = this.friendQueue[];
string userName = this.twinkleNotifySupporter.GetFriendName(cache.User);
this.notifyIcon1.Text = string.Format("{0}({1}) {2}条消息", cache.User, userName, cache.MessageList.Count);
this.twinkleIcon = this.twinkleNotifySupporter.GetHeadIcon(cache.User);
}
else
{
UnhandleGroupMessageBox cache = this.groupQueue[];
string groupName = this.twinkleNotifySupporter.GetGroupName(cache.Group);
this.notifyIcon1.Text = string.Format("{0}({1}) {2}条消息", groupName, cache.Group, cache.MessageList.Count);
this.twinkleIcon = this.twinkleNotifySupporter.GroupIcon;
}
}
(1)如果好友消息的Queue和群消息的Queue当中都没有任何消息了,则不再闪动托盘图标。
(2)再依次扫描好友消息的Queue和群消息的Queue,如果发现还有待提取的消息,则设置托盘图标的图像,设置ToolTip,并开始闪动图标。
三.GG V4.1 源码
下载最新版本,请转到这里。
欢迎和我探讨关于GG的一切,我的QQ:2027224508,多多交流!
大家有什么问题和建议,可以留言,也可以发送email到我邮箱:ggim2013@163.com。
如果你觉得还不错,请粉我,顺便再顶一下啊,呵呵
QQ揭秘:如何实现托盘闪动消息提醒?【低调赠送:QQ高仿版GG 4.1 最新源码】的更多相关文章
- 即时通信系统中如何实现:聊天消息加密,让通信更安全? 【低调赠送:QQ高仿版GG 4.5 最新源码】
加密重要的通信消息,是一个常见的需求.在一些政府部门的即时通信软件中(如税务系统),对聊天消息进行加密是非常重要的一个功能,因为谈话中可能会涉及到机密的数据.我在最新的GG 4.5中,增加了对聊天消息 ...
- QQ揭秘:如何实现窗体靠边隐藏?【低调赠送:QQ高仿版GG 4.2 最新源码】
QQ有个靠边隐藏的功能,使用起来很方便:在屏幕上拖动QQ的主窗体,当窗体的上边沿与屏幕的上边沿对齐时,主窗体就会duang~~地隐藏起来,当将鼠标移到屏幕上边沿的对应区域时,主窗体又会duang~~显 ...
- 如何做到在虚拟数据库和真实数据库之间自由切换?【低调赠送:QQ高仿版GG 4.4 最新源码】
记得以前在公司上班时,有时候白天的活没干完,我就会把工作带回家晚上加班继续做.但是,我们开发用的数据库是部署在公司局网内部的一台服务器上的,在家里是肯定连不上这台机器的.在家里没有数据库,服务端就跑不 ...
- 如何实现:录制视频聊天的全过程? 【低调赠送:QQ高仿版GG 4.3 最新源码】
前段时间做个项目,客户需要将视频对话的整个过程录制下来,这样,以后就可以随时观看.想来录制整个视频聊天的过程这样的功能应该是个比较常见的需求,比如,基于网络语音视频的1:1的英语口语辅导,如果能将辅导 ...
- 即时通信系统中如何实现:全局系统通知,并与Web后台集成?【低调赠送:QQ高仿版GGTalk 5.1 最新源码】
像QQ这样的即时通信软件,时不时就会从桌面的右下角弹出一个小窗口,或是显示一个广告.或是一个新闻.或是一个公告等.在这里,我们将其统称为“全局系统通知”.很多使用GGTalk的朋友都建议我加上一个类似 ...
- 可在广域网部署运行的QQ高仿版 -- GG2014 完美版!新增支持:聊天记录、好友分组、托盘闪动消息提醒、登录状态、GIF动态表情
距上次GG V3.7版本(可在广域网部署运行的QQ高仿版 -- GG叽叽V3.7,优化视频聊天.控制更多相关细节)的发布,已经有50天了,这50天对于GG来说,是一个重大的飞跃.因为这段时间通过一些基 ...
- 可在广域网部署运行的QQ高仿版 -- GG叽叽(源码)
前段时间看到园子里有朋友开发了QQ高仿版的程序,我也非常有兴趣,以前一直有个做即时聊天程序的梦,趁这段时间工作不是很忙,就开始动手来做这个事情.根据我以往积累下来的项目经验,实现QQ的基本功能,问题应 ...
- 即时通信系统中实现全局系统通知,并与Web后台集成【附C#开源即时通讯系统(支持广域网)——QQ高仿版IM最新源码】
像QQ这样的即时通信软件,时不时就会从桌面的右下角弹出一个小窗口,或是显示一个广告.或是一个新闻.或是一个公告等.在这里,我们将其统称为“全局系统通知”.很多使用C#开源即时通讯系统——GGTalk的 ...
- 即时通信系统中实现聊天消息加密,让通信更安全【低调赠送:C#开源即时通讯系统(支持广域网)——GGTalk4.5 最新源码】
在即时通讯系统(IM)中,加密重要的通信消息,是一个常见的需求.尤其在一些政府部门的即时通信软件中(如税务系统),对即时聊天消息进行加密是非常重要的一个功能,因为谈话中可能会涉及到机密的数据.我在最新 ...
随机推荐
- amr转MP3
using System; using System.Threading; using System.IO; using System.Diagnostics; using System.Securi ...
- LINQ - 在Where條件式中使用in與not in
希望对大家在以后的项目中能用到,我也是在项目中碰到了这个问题: 算算時間,接觸LINQ也有一個月的時間了,可以算是落伍兼新生,不過最近在寫專案的時候,遇到了在LINQ的Where條件式中要如何使用in ...
- 开启梦幻般的webrtc之旅
废话不多说,直接上demo <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
- MySqlHelper
package utils; import java.io.IOException; import java.sql.CallableStatement; import java.sql.Connec ...
- loop 循环次数
在汇编中可以使用 loop 段地址:偏移地址 并配合 cx 达到循环执行的目的,但是在一些资料中看到说,cx 是循环的次数,我觉得这是不对的. 比如下面这段代码的作用是使得最终的 ax 中的值为 3 ...
- Qt 4.x调试器问题,缺失调试助手。
之前项目开发需要用到4.x环境,固定多少版本避免团队开发不协调,然后拿了同事的开发包安装[注:我本子上原来就有4.x版本跟5.x版本,只是对应的4.x跟需求的不一样] creator是2.4.1的,同 ...
- 【洛谷P3076】Taxi
这道题值得好好想一会 我们通过对一些小数据的手算,以及对于每段路程的拆分,可以发现: 1.每个st对应的ed这段路程无论如何都要算上 2.额外还要计算的一段路程,就是"切换"费用 ...
- mysql远程快速导出csv格式数据工具
如需转载,请经本人同意. 之前本人曾经写过一个使用 select ....into outfile原理导出数据的脚本,但该脚本值适用于本地快速导出,并不支持远程服务,故又编写了下面这个支持远程导出的脚 ...
- (转)详解Linux Top 命令
top 命令是最流行的性能监视工具之一,我们必需了解.它是一个优秀的交互式工具,用于监视性能.它提供系统整体性能,但报告进程信息才是 top 命令的长处.top 命令交互界面如下图所视:
- [机器学习] 在茫茫人海中发现相似的你:实现局部敏感哈希(LSH)并应用于文档检索
简介 局部敏感哈希(Locality Sensitive Hasing)是一种近邻搜索模型,由斯坦福大学的Mose Charikar提出.我们用一种随机投影(Random Projection)的方式 ...