1. 程式人生 > >【SkinUI例項】仿QQ介面設計第三一課

【SkinUI例項】仿QQ介面設計第三一課

這節說一下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模擬出一個工具欄,這樣我們可以解決原有工具欄影象支援單一的問題,明天我們再講一下工具欄的繪製和使用