zl程序教程

您现在的位置是:首页 >  Java

当前栏目

简单三步实现QQ窗体靠边隐藏及GG 4.2 最新源码

2023-03-09 22:19:37 时间

QQ有个靠边隐藏的功能,使用起来很方便:在屏幕上拖动QQ的主窗体,当窗体的上边沿与屏幕的上边沿对齐时,主窗体就会duang~~地隐藏起来,当将鼠标移到屏幕上边沿的对应区域时,主窗体又会duang~~显示出来。

三步 短时间实现QQ窗体靠边隐藏

我在GG的***版4.2中也增加了靠边隐藏的功能,支持靠左边沿隐藏、靠上边沿隐藏、靠右边沿隐藏三种模式,并且,将靠边隐藏实现为了一个可复用的组件AutoDocker。

那么,靠边隐藏功能到底是怎么实现的了?(最初实现的过程中,遇到了很多问题,花了不少时间,现在直接把成果共享出来)

一.靠边隐藏的原理

靠边隐藏的本质实际上并不是将窗体的Visiable设为false,而是将整个窗体的位置挪到屏幕区域之外。比如,靠右边沿隐藏,实际的效果图如下所示:

方案说明如下:

(1)当拖动窗体在屏幕上移动时,检测窗体的位置,是否抵达了屏幕的边界,如果达到了边界,则准备靠边隐藏。

(2)当达到了隐藏条件,并且鼠标的光标已经离开了主窗体,则实现隐藏。

(3)窗体隐藏后,当鼠标的光标移动到窗体与屏幕相交的边界位置时,则正常显示窗体;之后:

a. 当鼠标再度离开窗体区域,则又隐藏窗体。

b.如果鼠标拖动窗体改变了其位置,使其不再满足隐藏的条件,则之后一直正常显示窗体。

#p#

二.具体实现过程

1.基本元素定义

首先,我们需要定义靠边隐藏的类型:靠左、靠上、靠右。使用DockHideType枚举表示:

  1. /// <summary> 
  2.  /// 靠边隐藏的类型。 
  3.  /// </summary> 
  4.  public enum DockHideType 
  5.  { 
  6.      /// <summary> 
  7.      /// 不隐藏 
  8.      /// </summary> 
  9.      None = 0
  10.      /// <summary> 
  11.      /// 靠上边沿隐藏 
  12.      /// </summary> 
  13.      Top, 
  14.      /// <summary> 
  15.      /// 靠左边沿隐藏 
  16.      /// </summary> 
  17.      Left, 
  18.      /// <summary> 
  19.      /// 靠右边沿隐藏 
  20.      /// </summary> 
  21.      Right 
  22.  } 

 其次,根据上面的原理描述,我们知道窗体有三种状态:正常显示、准备隐藏、已经隐藏。这三种状态使用FormDockHideStatus枚举表示:

  1. /// <summary> 
  2.     /// 窗体的显示或隐藏状态 
  3.     /// </summary> 
  4.     public enum FormDockHideStatus 
  5.     { 
  6.         /// <summary> 
  7.         /// 已隐藏 
  8.         /// </summary> 
  9.         Hide = 0
  10.  
  11.         /// <summary> 
  12.         /// 准备隐藏 
  13.         /// </summary> 
  14.         ReadyToHide, 
  15.          
  16.         /// <summary> 
  17.         /// 正常显示 
  18.         /// </summary> 
  19.         ShowNormally 
  20.  
  21.     } 

 

2.判断是否达到隐藏条件

很明显,我们应当在每次窗体的位置发生变化时,做出这样的判断,所以,这个判断应该在Form的LocationChanged事件中调用。

 

  1. private void dockedForm_LocationChanged(object sender, EventArgs e) 
  2.         { 
  3.             this.ComputeDockHideType(); 
  4.             if (!this.IsOrg) 
  5.             { 
  6.                 this.lastBoard = this.dockedForm.Bounds; 
  7.                 this.IsOrg = true
  8.             } 
  9.         } 
  10.  
  11.         /// <summary> 
  12.         /// 判断是否达到了隐藏的条件?以及是哪种类型的隐藏。 
  13.         /// </summary> 
  14.         private void ComputeDockHideType() 
  15.         { 
  16.             if (this.dockedForm.Top <= 0
  17.             { 
  18.                 this.dockHideType = DockHideType.Top; 
  19.                 if (this.dockedForm.Bounds.Contains(Cursor.Position)) 
  20.                 { 
  21.                     this.formDockHideStatus = FormDockHideStatus.ReadyToHide; 
  22.                     return
  23.                 } 
  24.                 this.formDockHideStatus = FormDockHideStatus.Hide; 
  25.                 return
  26.             } 
  27.             else 
  28.             { 
  29.                 if (this.dockedForm.Left <= 0
  30.                 { 
  31.                     this.dockHideType = DockHideType.Left; 
  32.                     if (this.dockedForm.Bounds.Contains(Cursor.Position)) 
  33.                     { 
  34.                         this.formDockHideStatus = FormDockHideStatus.ReadyToHide; 
  35.                         return
  36.                     } 
  37.                     this.formDockHideStatus = FormDockHideStaus.Hide; 
  38.                     return
  39.                 } 
  40.                 else 
  41.                 { 
  42.                     if (this.dockedForm.Left < Screen.PrimaryScreen.Bounds.Width - this.dockedForm.Width) 
  43.                     { 
  44.                         this.dockHideType = DockHideType.None; 
  45.                         this.formDockHideStatus = FormDockHideStatus.ShowNormally; 
  46.                         return
  47.                     } 
  48.                     this.dockHideType = DockHideType.Right; 
  49.                     if (this.dockedForm.Bounds.Contains(Cursor.Position)) 
  50.                     { 
  51.                         this.formDockHideStatus = FormDockHideStatus.ReadyToHide; 
  52.                         return
  53.                     } 
  54.                     this.formDockHideStatus = FormDockHideStatus.Hide; 
  55.                     return
  56.                 } 
  57.             } 
  58.         } 

 

上面的代码主要体现了以下几个要点:

(1)靠边的优先级判断:***判断靠上边沿隐藏、其次判断靠左边沿隐藏、***判断靠右边沿隐藏。

(2)只要窗体的某一边超出的屏幕的边界(比边沿对齐更加容易控制),则视为达到隐藏条件。

(3)如果达到了隐藏条件,仍然要判断光标的位置(Cursor.Position)是否在窗体内,如果在其内,则为准备隐藏状态。

详细分析一下上面的过程,就会发现,当处于准备隐藏状态时,如果将鼠标移出到窗体外(这次移动并没有拖动窗体改变其位置),那么,窗体会一直处于“准备隐藏”的状态。所以,此时,必须要有一个机制来触发它,真正进行隐藏动作。我是用一个定时器来循环判断的。

3.定时检测满足/退出隐藏条件

我使用一个定时器,每隔300ms检测一次,用于判断从正常显示到隐藏、以及从隐藏到正常显示的转变。

 

  1. /// <summary> 
  2.         /// 定时器循环判断。         
  3.         /// </summary>        
  4.         private void CheckPosTimer_Tick(object sender, EventArgs e) 
  5.         {//当鼠标移动到窗体的范围内(此时,窗体的位置位于屏幕之外) 
  6.             if (this.dockedForm.Bounds.Contains(Cursor.Position)) 
  7.             {                             if (this.dockHideType!= DockHideType.Top) 
  8.                 { 
  9.                     if (this.dockHideType!= DockHideType.Left) 
  10.                     { 
  11.                         if (this.dockHideType!= DockHideType.Right) 
  12.                         { 
  13.                             return
  14.                         } 
  15.                         if (this.formDockHideStatus == FormDockHideStatus.Hide) 
  16.                         { 
  17.                             this.dockedForm.Location = new Point(Screen.PrimaryScreen.Bounds.Width - this.dockedForm.Width, this.dockedForm.Location.Y); 
  18.                             return
  19.                         } 
  20.                     } 
  21.                     else 
  22.                     { 
  23.                         if (this.formDockHideStatus == FormDockHideStatus.Hide) 
  24.                         { 
  25.                             this.dockedForm.Location = new Point(0this.dockedForm.Location.Y); 
  26.                             return
  27.                         } 
  28.                     } 
  29.                 } 
  30.                 else 
  31.                 { 
  32.                     if (this.formDockHideStatus == FormDockHideStatus.Hide) 
  33.                     { 
  34.                         this.dockedForm.Location = new Point(this.dockedForm.Location.X, 0); 
  35.                         return
  36.                     } 
  37.                 } 
  38.             } 
  39.             else //当鼠标位于窗体范围之外,则根据DockHideType的值,决定窗体的位置。 
  40.             {                switch (this.dockHideType) 
  41.                 { 
  42.                     case DockHideType.None: 
  43.                         { 
  44.                             if (this.IsOrg && this.formDockHideStatus == FormDockHideStatus.ShowNormally &&  
  45.                                (this.dockedForm.Bounds.Width != this.lastBoard.Width || this.dockedForm.Bounds.Height != this.lastBoard.Height)) 
  46.                             { 
  47.                                 this.dockedForm.Size = new Size(this.lastBoard.Width, this.lastBoard.Height); 
  48.                             } 
  49.                             break
  50.                         } 
  51.                     case DockHideType.Top: 
  52.                         { 
  53.                             this.dockedForm.Location = new Point(this.dockedForm.Location.X, (this.dockedForm.Height - 4) * -1); 
  54.                             return
  55.                         }       
  56.                     case DockHideType.Left: 
  57.                         {                            
  58.                             this.dockedForm.Location = new Point(-1 * (this.dockedForm.Width - 4), this.dockedForm.Location.Y); 
  59.                             return
  60.                         } 
  61.                     default
  62.                         { 
  63.                             if (anchorStyles2 != DockHideType.Right) 
  64.                             { 
  65.                                 return
  66.                             }                             
  67.                             this.dockedForm.Location = new Point(Screen.PrimaryScreen.Bounds.Width - 4this.dockedForm.Location.Y); 
  68.                             return
  69.                         } 
  70.                 } 
  71.             } 
  72.         } 

 

(1)在窗体隐藏的情况下,准确地说,是窗体在屏幕区域之外时,将鼠标光标移动到窗体上(实际上,是窗体的边界),则修改窗体的Location,让其正常显示。

(2)从(1)描述的窗体隐藏切换到正常显示时,代码对窗体的位置进行了控制,使其的边界恰好与屏幕的边界对齐,这样做的目的是,当鼠标再离开窗体范围时,窗体又可以duang隐藏起来。

(3)定时器的循环检测配合鼠标拖动窗体的事件处理,就完全实现了类似QQ的靠边隐藏的效果,而且,我们比QQ更强,QQ只实现了靠上隐藏。

#p#

三.如何使用AutoDocker组件

AutoDocker是以组件(Component)的形式实现的,编译后,会在工具箱中出现一个AutoDocker组件。其使用非常简单:

从工具箱中将AutoDocker拖放到主窗体MainForm上,然后在主窗体的构造函数中添加一行代码:

this.autoDocker1.Initialize(this);

这样,主窗体运行起来后,就拥有了自动靠边隐藏的功能了,是不是很duang~~~

在GG 4.2的源码中,找到客户端项目(GG2014)下的AutoDocker.cs文件,即可详细研究靠边隐藏的实现细节。

四.GG V4.2 源码(点击下载

GG是可在广域网部署运行的QQ高仿版,2013.8.7发布V1.0版本,至今***是4.2版本,关于GG更详细的介绍,可以查看 可在广域网部署运行的QQ高仿版 -- GG2013概要。

GG V4.2 源码(点击下载)

如果觉得我的工作对你有帮助,请顶我&粉我啊,duang~~~