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

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

这节说一下QQ的聊天面板

由于这两天事情比较多,聊天面板也只是搭建完了界面,明天把内容充实一下,代码也将于明天公布,敬请期待!

先说一下修改的内容:

1:目前发现,当拖动窗口时,程序会出现卡顿的情况,同时cpu会显著提升,经测试,此问题的根源在于GDI+的DrawImage,此函数造成整个效率的下降,为什么会出现这种问题,当Bitmap被创建之后,Bitmap的格式可能DrawImage时不一样,当不一样的时候,他就会根据其类型进行转码,期间需要消耗一部分的时间,我们知道GDI的BltBit函数效果是很高的,虽然说鱼和熊掌不可兼得,但是想同时兼顾GDI的高效和GDI+的易用,其实是有办法的。

1:创建Bitmap的缓存,由Bitmap Clone出一个新的Bitmap,这个时候就可以指定图像的格式,当绘制DrawImage的时候,会节约一部分时间。不过效率并不会提高多少

2:将Bitmap对象通过CreateDIBSection转换成BITMAPINFO对象,这样就可以通过BltBit绘制了,效率也会提高不少,不过对于图片资源比较多的,最好使用多线程,且将CreateDIBSection放在初始化中


不过,为了节约时间,本次我通过CImage类完成,效率比之前的GDI+提高了不少,不过这里需要注意一下,采用CImage绘制时,如果对图像进行缩放,尤其是缩小处理的时候,会出现像素丢失失真的情况,此时通过pDC->SetStretchBltMode(HALFTONE);即可解决问题


CImage对于Png这种带有Alpha通道的图像,需要进行Alpha预乘,代码如下

bool CImageEx::SetAlphaBit()
{
    ASSERT(IsNull() == false);
    if(IsNull())return FALSE;
   
    if ( GetBPP() == 32 )//png图像
    {
        LPVOID pBitsSrc = NULL;
        BYTE * psrc = NULL;
        BITMAP stBmpInfo;
   
        HBITMAP hBmp = (HBITMAP)*this;
   
        ::GetObject(hBmp, sizeof(BITMAP), &stBmpInfo);
   
        if (32 != stBmpInfo.bmBitsPixel || NULL == stBmpInfo.bmBits)
            return FALSE;
   
        psrc = (BYTE *) stBmpInfo.bmBits;
   
        for (int nPosY = 0; nPosY < abs(stBmpInfo.bmHeight); nPosY++)
        {
            for (int nPosX = stBmpInfo.bmWidth; nPosX > 0; nPosX--)
            {
                BYTE alpha  = psrc[3];
                psrc[0] = (BYTE)((psrc[0] * alpha) / 255);
                psrc[1] = (BYTE)((psrc[1] * alpha) / 255);
                psrc[2] = (BYTE)((psrc[2] * alpha) / 255);
                psrc += 4;
            }
        }
    }
   
    return TRUE;
}

在说一下窗口的三个系统按钮,最大化,最小化,关闭,我们发现QQ,不同的窗口,系统按钮的多少是不确定的,有的只有关闭,有的没有最大化,有的一个按钮也没有(比如QQ更新的时候)如果我们每个窗口都进行这些按钮的加载处理又太过于繁琐,所以,我们在基类中完成这项工作,我们定义一个枚举,定义刚才的这些情况

enum AFX_WND_STYLE
{
    en_Wnd_Normal=0,            //关闭,最大化,最小化同时存在
    en_Wnd_MinimizeBox,         //无最大化按钮
    en_Wnd_CloseBox,            //只有关闭按钮
    en_Wnd_None                 //无按钮
};

我们将构造函数添加一个可选参数,默认,三种按钮同时存在

CSkinManager(UINT nIDTemplate,CWnd* pParent = NULL,AFX_WND_STYLE Style = en_Wnd_Normal);   // 标准构造函数

此时,我们在初始化的时候进行判断处理

CRect rcClient;
GetClientRect(&rcClient);
   
if ( m_enWndStyle != en_Wnd_None )
{
    m_btClose.Create(NULL,WS_VISIBLE|WS_CHILD,CRect(rcClient.Width()-41,0,0,0),this,IDCANCEL);
    m_btClose.SetBackImage(TEXT("\\QQ\\Button\\btn_close_normal.png"),TEXT("\\QQ\\Button\\btn_close_highlight.png"),TEXT("\\QQ\\Button\\btn_close_down.png"),TEXT("\\QQ\\Button\\btn_close_normal.png"));
    m_btClose.SetButtonType(en_PushButton);
    m_btClose.SetParentBack(hParentDC);
    m_btClose.SetSize(39,20);
   
    if ( m_enWndStyle != en_Wnd_CloseBox )
    {
        if( m_enWndStyle != en_Wnd_MinimizeBox )
        {
            m_btMax.Create(NULL,WS_VISIBLE|WS_CHILD,CRect(rcClient.Width()-69,0,0,0),this,IDC_WNDMAX);
            m_btMax.SetBackImage(TEXT("\\QQ\\Button\\btn_max_normal.png"),TEXT("\\QQ\\Button\\btn_max_highlight.png"),TEXT("\\QQ\\Button\\btn_max_down.png"),TEXT("\\QQ\\Button\\btn_max_normal.png"));
            m_btMax.SetButtonType(en_PushButton);
            m_btMax.SetParentBack(hParentDC);
            m_btMax.SetSize(28,20);
        }
   
        m_btMin.Create(NULL,WS_VISIBLE|WS_CHILD,CRect(rcClient.Width()-69-(m_enWndStyle==en_Wnd_Normal?28:0),0,0,0),this,IDC_WNDMIN);
        m_btMin.SetBackImage(TEXT("\\QQ\\Button\\btn_mini_normal.png"),TEXT("\\QQ\\Button\\btn_mini_highlight.png"),TEXT("\\QQ\\Button\\btn_mini_down.png"),TEXT("\\QQ\\Button\\btn_mini_normal.png"));
        m_btMin.SetButtonType(en_PushButton);
        m_btMin.SetParentBack(hParentDC);
        m_btMin.SetSize(28,20);
    }
}

在WM_SIZE消息内,在分别处理一下按钮的位置,这样,我们就不用在每个子类里分别创建这些按钮了。

关于最大化,可能说到最大化,你第一反应是ShowWindow(SW_MAXIMIZE),对,这个的确可以最大化,但是你发现没有,他貌似实现的是全屏,我们并不希望把任务栏给盖住,那么我们就需要获取窗口能在桌面显示的大小

void CSkinManager::OnBnClickWindowMax()
{
    static CRect rcClient(0,0,0,0);
   
    if ( m_bIsZoomed )
    {
        m_btMax.SetBackImage(TEXT("\\QQ\\Button\\btn_max_normal.png"),TEXT("\\QQ\\Button\\btn_max_highlight.png"),TEXT("\\QQ\\Button\\btn_max_down.png"),TEXT("\\QQ\\Button\\btn_max_normal.png"));
   
        MoveWindow(&rcClient);
   
        m_bIsZoomed = false;
    }
    else
    {
        GetWindowRect(&rcClient);
        m_btMax.SetBackImage(TEXT("\\QQ\\Button\\btn_restore_normal.png"),TEXT("\\QQ\\Button\\btn_restore_highlight.png"),TEXT("\\QQ\\Button\\btn_restore_down.png"),TEXT("\\QQ\\Button\\btn_restore_normal.png"));
   
        CRect rc;
        SystemParametersInfo(SPI_GETWORKAREA,0,&rc,0);
        MoveWindow(&rc);
   
        m_bIsZoomed = true;
    }
}

我们通过SystemParametersInfo函数就可以轻松实现这个功能,这样我们就不需要获取任务栏的位置,任务栏的高度或者宽度,通过这些属性去计算了

QQ聊天面板,关于工具栏,我们通过CWnd模拟出一个工具栏,这样我们可以解决原有工具栏图像支持单一的问题,明天我们再讲一下工具栏的绘制和使用






你可能感兴趣的:(【SkinUI实例】仿QQ界面设计第三一课)