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