【SkinUI实例】仿QQ界面设计第三十二课

文章转自:http://www.gymsaga.com/project/607.html

还是先看效果吧

face界面

功能:字体格式的设置

创建全局的CHARFORMAT2对象,初始化它

ZeroMemory(&m_cfRich,sizeof m_cfRich);
m_cfRich.cbSize = sizeof CHARFORMAT2;
m_cfRich.yHeight = 18*12;
m_cfRich.dwMask = CFM_COLOR | CFM_SIZE | CFM_FACE | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
m_cfRich.dwEffects = CFE_ALLCAPS;
m_cfRich.crTextColor = RGB(0,0,0);
StrCpy(m_cfRich.szFaceName,TEXT("宋体"));
调用一下SetRichFormat

void CChatDlg::SetRichFormat()
{
    m_SendEdit.SetSel(0, -1);
    m_SendEdit.SetWordCharFormat(m_cfRich);
    m_SendEdit.SetSel(-1, -1);
    
    m_SendEdit.SetFocus();
}

接下来就是m_cfRich的设置,先看一下系统字体是怎么获取的

BOOL CALLBACK CChatDlg::EnumFamScreenCallBackEx(ENUMLOGFONTEX* pelf, NEWTEXTMETRICEX* lpntm, int FontType, LPVOID pThis)
{
    if (FontType&RASTER_FONTTYPE)
    {
        return TRUE;
    }
    
    CString str;
    
    if (FontType & TRUETYPE_FONTTYPE)
    {
        str=((pelf)->elfLogFont.lfFaceName);
    }
    
    int i=((CChatDlg *)pThis)->m_comFont.AddString(str);
    
    return TRUE;
}
    
BOOL CChatDlg::EnumerateFonts()
{
    HDC hDC;
    hDC=::GetWindowDC(NULL);
    LOGFONT lf;
    memset(&lf,0,sizeof(LOGFONT));
    lf.lfCharSet=GB2312_CHARSET;
    
    if (!::EnumFontFamiliesEx(hDC,&lf,(FONTENUMPROC)EnumFamScreenCallBackEx,(LPARAM)this,(DWORD) 0))
    {
        return FALSE;
    }
    
    ::ReleaseDC(NULL,hDC);
    
    CString strCurrentFont;
    CString strPreFont=TEXT("");
    
    int nCount=m_comFont.GetCount();
    
    // 删除重复项
    for (int i=nCount;i>0;i--)
    {
        m_comFont.GetLBText((i-1),strCurrentFont);
    
        if (strCurrentFont==strPreFont)
        {
            m_comFont.DeleteString(i-1);
        }
    
        strPreFont=strCurrentFont;
    }
    
    // 删除@字体  循环次序 为 从高到低   因为删除某个元素后, 各个位置可能改变
    nCount=m_comFont.GetCount();
    for (int i=nCount-1;i>=0;i--)
    {
        m_comFont.GetLBText(i,strCurrentFont);
        strCurrentFont=strCurrentFont.Left(1);
    
        if (strCurrentFont==TEXT("@"))
        {
            m_comFont.DeleteString(i);
        }
    }
    
    //设置当前选中项
    CString temp;
    for (int i=0;i

使用EnumFontFamiliesEx,关于此函数定义请参阅MSDN

关于字体加粗等操作,此处已加粗为例,其他雷同

static bool bBold = true;
    
if ( bBold )
    m_cfRich.dwEffects |= CFE_BOLD;
else
    m_cfRich.dwEffects &= ~CFE_BOLD;
    
bBold = !bBold;
        
SetRichFormat();

只需要为dwEffects 修改一下属性即可

再说一下这QQ的Face面板,看一下头文件代码

#ifndef EXPRESSION_HEAD_FILE
#define EXPRESSION_HEAD_FILE
    
#pragma once
    
//////////////////////////////////////////////////////////////////////////////////
//类型定义
    
//类说明
class CExpression;
class CExpressionManager;
class CExpressionControl;
    
//表情信息
struct tagExpressionInfo
{
    INT                             nLength[3];                         //描述长度
    TCHAR                           szExpression[3][8];                 //表情描述
};
    
//解释结果
struct tagTranslateResult
{
    //数据变量
    INT                             nStartPos;                          //开始位置
    INT                             nDescribeLen;                       //描述长度
    
    //对象变量
    CExpression *                   pExpressionItem;                    //表情信息
};
    
//数组定义
typedef CArray       CExpressionArray;                   //表情数组
    
//////////////////////////////////////////////////////////////////////////////////
    
//表情接口
interface IExpressionSink
{
    //选择表情
    virtual VOID OnExpressionSelect(CExpression * pExpression, tagExpressionInfo * pExpressionInfo)=NULL;
};
    
//////////////////////////////////////////////////////////////////////////////////
    
//聊天表情
class CExpression
{
    //友元定义
    friend class CExpressionControl;
    friend class CExpressionManager;
    
    //变量定义
protected:
    WORD                            m_wIndex;                           //索引标志
    CGIFImage                       m_ExpressionItem;                   //表情资源
    tagExpressionInfo               m_ExpressionInfo;                   //表情信息
    
    //函数定义
protected:
    //构造函数
    CExpression();
    //析构函数
    virtual ~CExpression();
    
    //功能函数
public:
    //获取索引
    WORD GetIndex() { return m_wIndex; }
    //获取信息
    tagExpressionInfo *GetExpressionInfo(){ return &m_ExpressionInfo; }
    //获取路径
    bool GetExpressionPath(LPTSTR pszImagePath, WORD wMaxCount);
    //设置表情
    bool SetExpressionInfo(WORD wIndex, tagExpressionInfo&ExpressionInfo);
};
    
//////////////////////////////////////////////////////////////////////////////////
    
//表情管理
class CExpressionManager
{
    //组件变量
protected:
    CExpressionArray                m_ExpressionArray;                  //表情数组
        
    //静态变量
protected:
    static CExpressionManager *     m_pExpressionManager;               //对象指针
    
    //函数定义
public:
    //构造函数
    CExpressionManager();
    //析构函数
    virtual ~CExpressionManager();
    
    //功能函数
public:
    //加载表情
    bool LoadExpression();
    //解释表情
    bool TranslateString(LPCTSTR pszString, tagTranslateResult&TranslateResult);
    
    //表情信息
public:
    //表情数目
    WORD GetExpressionCount();
    //枚举表情
    CExpression * GetExpressionItem(WORD wIndex);
    
    //静态函数
public:
    //获取对象
    static CExpressionManager * GetInstance() { return m_pExpressionManager; }
};
    
//////////////////////////////////////////////////////////////////////////////////
    
//表情窗口
class CExpressionControl : public CDialog
{
    //变量定义
protected:
    WORD                            m_wItemCount;                           //表情数目
    WORD                            m_wSelectIndex;                         //选择索引
    
    //组件变量
protected:
    IExpressionSink *               m_pIExpressionSink;                     //表情接口
    
    //函数定义
public:
    //构造函数
    CExpressionControl();
    //析构函数
    virtual ~CExpressionControl();
    
    //功能函数
public:
    //显示控件
    VOID ShowExpression(CWnd * pParentWnd, INT nXPos, INT nYPos, IExpressionSink * pIExpressionSink);
    
    //辅助函数
protected:
    //绘画头像
    VOID DrawExpression(CDC * pDC, WORD wItemIndex, bool bSelected);
    
    //消息函数
public:
    //重画消息
    VOID OnPaint();
    //绘画背景
    BOOL OnEraseBkgnd(CDC * pDC);
    //焦点消息
    VOID OnKillFocus(CWnd * pNewWnd);
    //鼠标消息
    VOID OnLButtonDown(UINT nFlags, CPoint Point);
    //光标消息
    BOOL OnSetCursor(CWnd * pWnd, UINT nHitTest, UINT uMessage);
    
    DECLARE_MESSAGE_MAP()
};
    
//////////////////////////////////////////////////////////////////////////////////
    
#endif
我们定义了三个类,CExpression为单个Face的属性,CExpressionManager负责图像的加载使用,注意,此处使用了单例模式,所以在程序初始化的时候,在InitInstance中加载一遍资源即可,不需要每次打开聊天面板都加载资源,同时该类负责解析文本内图像标签,比如在QQ聊天面板中,我们输入/qq,此时出现的是一个亲亲的Gif,为了实现这个效果,我们在创建了配置文件,用来存储这些Face属性,文件格式如下
[Face]
    
PY000=wx
CN000=微笑
EN000=:)

这里我们定义了三种解析形式,当资源初始化加载的时候,我们也将这些信息存储到容器,这样当用户输出/wx或者/微笑或者/:)时,我们就可以通过遍历容器找到Face的ID,从而显示出Face了,当然此Face的界面仍然不是控件重绘出来的,绘制过程请看源码


关于头像的点击,这里我们定义了接口IExpressionSink,我们只需要重载将聊天面板继承该接口,重载该函数就可以获取该接口的内容,比如:

VOID CChatDlg::OnExpressionSelect( CExpression * pExpression, tagExpressionInfo * pExpressionInfo )
{
    //获取路径
    TCHAR szImagePath[MAX_PATH]=TEXT("");
    pExpression->GetExpressionPath(szImagePath,CountArray(szImagePath));
    
    CString strTime;
    
    CTime tm =CTime::GetCurrentTime();
    strTime=tm.Format(TEXT("%Y/%m/%d %X\n"));
    strTime.Insert(0,TEXT("丄偙寵ル"));
    
    CHARFORMAT2 cf2;
    ZeroMemory(&cf2, sizeof(CHARFORMAT2));
    cf2.cbSize = sizeof(CHARFORMAT2);
    cf2.dwMask = CFM_COLOR | CFM_SIZE | CFM_FACE | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
    cf2.dwEffects = CFE_ALLCAPS |CFE_BOLD;
    cf2.yHeight = 18*10;
    cf2.crTextColor = RGB(0,128,64);
    StrCpy(cf2.szFaceName,TEXT("微软雅黑"));
    
    m_RecEdit.InsertString(strTime,cf2);
    m_RecEdit.InsertImage(szImagePath);
    m_RecEdit.ReplaceSel(TEXT("\n"));
}

这里,当用户选择了头像文件后,将头像插入到了返回消息的Rich中了,至于QQ插入到发送框中的效果,大家有兴趣的话可以继续做一下

今天有朋友问我,之前好友列表的滚动条是怎么绘制上去,那么细,其实,很简单,之前接触过DirectUI设计的很轻松就能知道工作原理,这里我们的好友列表用过CWnd派生,既然是通过窗口派生的,那么我们就能很轻松的在CWnd的一边绘制出这么一个滚动条,当然空有滚动条是不可以的,因为他控制不了界面的移动,所以,我们必须要监听到该CWnd的Windows消息,所有事情都全部到位了,那么我们就可以在这些消息事件中去实现我们想要的效果,可能到这里你还是不理解,那么举一个例子你可能会豁然开朗,还记得我们之前窗口重绘中,关于标题栏上面的按钮是怎么重绘的么,正是无非就是判断的鼠标消息,在WM_NCPAINT中绘制出了按钮,那么滚动条也是一样,只是滚动条包含按钮和滑块控件,使的大家感觉很难而已,当然为了代码的整洁,我们将滚动条单独的抽取出来,就形成了CScrollBarEx,到这里你应该明白了把,CScrollBarEx不是窗口,只是图片胡出来的


到这里我们的QQ重绘课程就结束了,关于QQ界面,如果有什么不懂的地方欢迎留言。

代码下载地址:http://pan.baidu.com/s/1mq19a

CSDN下载地址:http://download.csdn.net/detail/gym1039/6514673





你可能感兴趣的:(im即时通讯,UI界面)