QQ表情选择面板的VC实现

  ==========================================================================

  补充:

  (1)为了在预览图中显示动态的 GIF 图片,我舍弃了 CImage, 而引用了 CxImage 类库的代码。这样可以看到动态的 GIF 动画。

  (2)改进了绘制方法,改为使用内存位图绘制,并把 WM_ERASEBKGND 合并到 WM_PAINT 中,以降低闪烁。

  (3)修改了鼠标位于表情网格(Cells)的最右边缘时,错误的认为选中表情是下一行的第一个 Cell 的BUG。该 BUG 会导致鼠标在

    最右侧移入移出时在右上角有强烈闪烁感。

                        --hoodlum1980   2010年1月29日

  ==========================================================================

  最近有好多篇帖子是关于QQ的选择表情的那个窗口实现的。最初以为这是一个比较简单的功能,不过做起来还是发现做了整个一晚上才做的差不多做好。做完我想为什么有很多人愿意尝试它呢?可能是基于它的那个“预览图”对鼠标的“躲藏”功能是它的一个亮点,所以可能是因为这个原因引起很多人的兴趣来实现它。前人已经采用了很多种方式实现,例如 C# 的 winform,等等。在这里是使用 VC 实现的。在这个范例中也展示了如何定制一个特殊外观的窗口,以及自定义绘制(OWNER DRAW)按钮(1/2,上一页,下一页按钮),如何遍历一个文件夹下的所有文件等基本方法。

  不过这个例子仅仅是模仿QQ的UI,实际上这也是这个例子的唯一意义。由于VC里面对于存在多帧的 gif 格式并没有特别好的支持,包括 CImage 尽管支持大多数图像格式,但是却仅能加载 gif 的一帧,而要展示动画形式的gif,则通常要求助于第三方代码,例如 CxImage 等。因此这里我就仅仅模拟这个界面,实际上在预览框里面我仅仅绘制一下而已。而并没有去真正的做 gif 动画的预览。

  当鼠标在窗口上移动时,需要绘制一个蓝色小边框反应当前选中的是哪一个cell,而且预览图会尽可能惰性的“远离”鼠标。所谓惰性,是指仅在它发觉鼠标有向它逼近时,它的位置才会突变。例如当预览框位于左上角(跨度占据0,1,2列),而鼠标从右侧首次进入第4列时, 它是不动的,但鼠标在第4列继续移动时,它才会突然移动到右侧。因此为了检测鼠标的这种行为,实际上在范例中我记录了鼠标最近两次的停留位置,即 lastSelection,currentSelection(二者的逻辑关系实际上是一个仅包含两个元素的队列)。

   效果如下图所示:

  

  这个范例在UI方面基本是采用对QQ2009的表情选择窗口的截图来制作的, 使用的表情文件夹是来自于qq2008的 “Face2” 。但该范例还是和真正的QQ有一些出入的地方,例如:

  (1)QQ的表情选择窗口弹出来时,qq表情按钮是呈现按下状态的。范例为了简单期间,只是弹出模态对话框,所以这里不如QQ的 UI 自定义的那么彻底。

  (2)QQ的表情选择窗口和其父窗口是非模态性的关系,而范例是模态关系,所以在没有加载任何图片的情况下,为了使窗口能够关闭,我做了一点特殊处理,也就是在范例中可以通过点击“空白处”的表情退出,而在QQ中点击空白位置是没有回应的。

  (3)QQ的表情选择窗口有Tooltip,不过我不觉得这个功能很必要,估计很少会有人去关注这个功能,而且它还挡住了窗口的一部分。实现起来是很简单的,所以范例中没有tooltip了。

  (4)QQ的表情选择窗口能自动调节自己的出现位置,尽可能靠近触发按钮,并且保证自身的可见性(完全位于屏幕范围以内)。操作中很多弹出式的窗口具有这种功能,例如 ToolTip , ComboBox, DateTimePicker 类控件的下拉窗口等。这个功能看起来简单,但是实现起来还是需要一定技巧和复杂度。范例中仅仅让窗口的位置固定出现在按钮上方。

  (5)QQ的表情选择窗口对用户自己添加的表情在绘制时,使用了拉伸绘制。在范例中为了简单,没有考虑表情图片的大小。

  本范例使用的代码都属于比较传统的技术,所以并没有什么特别需要单独列出和讲解的地方。由于最近该类实现的文章较多,所以将本文发于首页候选区。

  在这里仅提供本范例的源代码下载连接:(可执行程序位于Debug目录中)

  https://files.cnblogs.com/hoodlum1980/QQFaceVC.rar 

  --hoodlum1980

  参考资料:

  【1】 本文代码中,关于 GIF 图片的解码和展示引用了 CxImage 中的代码。关于 CxImage 的介绍地址:

  http://www.codeproject.com/KB/graphics/cximage.aspx


  【补充:】2013年12月 ,帮网友写的一个 DEMO (含源代码)。(采用 VC6.0 和 MFC 为基础开发)。

  https://files.cnblogs.com/hoodlum1980/EmotionSelDemo.zip

  说明:

  (1)选择的表情的 FullPath 前面加了 “file:///" 。可以直接用在 html 代码里了。
 
  (2)增加了 ToolTip 提示但是现在只是展示了文件名和帧数量。如果要添加其他提示信息,则需要增加其他数据文件。(例如,使用一个 ini 文件提供每个文件的中文提示文本)。
 
  (3)用法更简单了,只需要添加 CEmotionWnd.h/cpp; 添加 CxImage 文件夹。(项目属性中设置为 不使用 precompiled header)。
   
  细节:
   
  (a)添加 #include "EmotionWnd.h"
  (b)添加成员变量:CEmotionWnd *m_pEmotionWnd;
  (c)初始化:
 
    this->m_pEmotionWnd = new CEmotionWnd();
 
    //第一个参数是接收 WM_NOTIFY 通知消息的窗口( CWnd* );
    //第二个参数是为表情选择窗口设置一个唯一的ID (int),能和其他控件的 ID 区分开即可。
    this->m_pEmotionWnd->CreateWnd(this, IDC_EMOTIONWND);
 
    //从某个文件夹下面添加所有 GIF 格式的文件;
    this->m_pEmotionWnd->LoadEmotions(_T("E:\\Face"));
 
   (d)点击表情按钮时,弹出表情选择窗口(如果在对话框位于屏幕最右侧,可自动移回屏幕范围内):
 
    //获得“表情”按钮的屏幕坐标(IDC_SEL_EMOTION 为表情按钮的 ID
    CWnd *pBtn = this->GetDlgItem(IDC_SEL_EMOTION);
    RECT rc;
    pBtn->GetWindowRect(&rc);
    this->m_pEmotionWnd->ShowOrHide(rc.left, rc.top);
 
  (e)处理 WM_NOTIFY 得到用户选择的表情的完整路径(前面已经添加 "file:///");
  (f)在不再需要时,销毁表情窗口,(例如主窗口的 OnDestroy):
    this->m_pEmotionWnd->DestroyWindow();
    delete this->m_pEmotionWnd;
    this->m_pEmotionWnd = NULL;

  

    -- hoodlum1980, ON 2013年12月12日。

原文地址:https://www.cnblogs.com/hoodlum1980/p/1642732.html