循序漸進實現仿QQ界面(二):貼圖按鈕的三態模擬

開始之前先說一下RingSDK的編譯問題,這裏演示的程序需要用到最新版本的RingSDK,請務必用SVN到svn://svnhost.cn/RingSDK更新到最新版本,推薦用TortoiseSVN。


如果是VC2008,編譯應該沒有問題,只是警告多了一點。VC6編譯RingSDK之前需要安裝Platform SDK,並且選擇VC菜單Tools->Options...,在彈出的對話框中選擇Directories頁,Show directories for:下拉框裏選擇include files,然後在下面列表中確認Platform SDK的include目錄是排在第一位。同時把RingSDK的include目錄加入列表,如下圖:

 

 

然後Show directories for:下拉框裏選擇Library files,確認Platform SDK的lib目錄排在第一位,把RingSDK的lib目錄加入列表。如果你想調試程序時跟進RingSDK源代碼,應該把RingSDK下libsrc下的目錄加入到Source files列表裏面。

VC設置完畢,請先打開RingSDK/libsrc/freelib目錄,先編譯該目錄下3個第3方庫,然後打開ringsdk.dsw進行編譯。編譯通過,就可以編譯example裏面的演示程序了。

 

現在進入正題,講解如何實現貼圖按鈕的三態模擬。所謂三態,就是按鈕的正常狀態,鼠標移上去的高亮狀態以及按下狀態。實際應該還有一個Disable狀態,不過這個程序沒有不可用的按鈕,因此就不實現這個效果了。由於是貼圖,不是實際的按鈕,因此必須自己處理鼠標消息並進行三種狀態的繪製。首先當然要有一個鼠標位置檢測函數HitTest,檢測鼠標在哪個按鈕上面,由於程序的非客戶區僅僅是一個尺寸爲2的園角矩形框,標題欄是模擬在客戶區實現,因此這個HitTest函數是在WM_MOUSEMOVE消息裏調用判斷,無非是PtInRect進行各個按鈕座標的檢測,因此這個HitTest函數的實現這裏就不講解了,看代碼就明白了。如果鼠標在按鈕區域,該函數返回按鈕ID,在系統按鈕區域,返回HTMINBUTTON,HTMAXBUTTON,HTCLOSE,方便直接發送系統命令,不在任何按鈕,返回HTCAPTION,可以拖動窗口。

如果HitTest返回了按鈕ID,用戶沒按下鼠標,應該繪製按鈕的高亮狀態。這裏有個問題,繪製完了以後,用戶繼續移動鼠標,但是沒移出這個按鈕,WM_MOUSEMOVE消息又會檢測到需要繪製高亮狀態,這樣不斷繪製就會造成鼠標閃爍,因此需要定義一個m_nCurWhere的成員變量,記錄鼠標上一次的HitTest檢測值:

 

這樣就保證了各種狀態變化,只需要繪製一次。

 

實現按鈕按下狀態,需要在WM_LBUTTONDOWN消息裏處理,首先當然是進行HitTest檢測,這裏也要定義一個成員變量m_nCurSysCmd,標記當前是按下了哪個按鈕,然後繪製按鈕的按下狀態。由於用戶可能按下了按鈕,不鬆開鼠標進行移動,因此還需要定義一個成員變量m_bInCapture,在WM_LBUTTONDOWN消息裏置這個變量爲TRUE,SetCapture捕獲鼠標,在WM_LBUTTONUP消息裏ReleaseCapture釋放鼠標,置這個變量爲FALSE,這樣在WM_MOUSEMOVE裏調用的按鈕繪製函數就可以根據m_bInCapture判斷是該繪製按下狀態還是高亮狀態。WM_LBUTTONUP消息也可以根據這個標誌判斷是否該執行按鈕動作,否則在別的地方按下鼠標鍵,移到一個按鈕上鬆開鼠標,執行該按鈕功能有點不大對頭,應該判斷m_bInCapture爲TRUE且m_nCurSysCmd與HitTest檢測到的按鈕相等才執行該按鈕的功能。鼠標鍵按下時的移動,只會對m_nCurSysCmd標記的按鈕進行按下狀態和正常狀態的繪製,其餘按鈕一概不理。發現QQ2009在這方面沒有實現,按下按鈕後不鬆開鼠標鍵移出按鈕,按鈕狀態不會變化。

 

按此機制,WM_MOUSEMOVE裏的判斷應該修改一下了,增加對按鈕按下狀態的判斷:

 

至此,鼠標對按鈕的檢測功能已完成,剩下的就是按鈕的繪製工作了。按鈕的繪製工作分爲3塊:系統按鈕的繪製,用戶頭像旁邊一個可彈出菜單的按鈕和編輯個性簽名按鈕([我在線上]也是按鈕,不過這個與個性簽名按鈕實現是一樣的,這裏就省點事,不實現了),以及用戶頭像下方的工具欄上那一排按鈕。系統按鈕的繪製採用的是最常見的方法,這裏把系統按鈕資源圖象貼出來大家就知道了,根據需要繪製的狀態,只要把圖象上相應區域繪製到目標就可以了。如圖:

 

 

不過這裏還是要有個技巧,最下面一排是正常狀態按鈕,爲將來界面調色的需要,處理成了透明的,僅邊框和線條是不透明的,這樣繪製正常狀態按鈕的時候就需要恢復背景色,然後透明繪製正常狀態的按鈕,有點麻煩,因此程序初始化的時候就初始化了系統按鈕區域大小的一個內存圖片,在WM_PAINT消息裏面貼圖的時候,順便就把該正常系統按鈕區域的圖象繪製到了這個內存圖象,這樣恢復系統按鈕狀態只要繪製一遍這個內存圖象就可以了。

 

接下來是繪製工具欄那一排按鈕,來看看所需要的資源圖片,一共是兩幅:


實現的按鈕三態效果,第二個是高亮狀態,第三個是按下狀態:

 

恩?這個是怎麼實現的?關鍵在第2張資源圖片,分爲左右兩部分,左邊是高亮狀態,右邊是按下狀態,每一部分圖象是5個像素寬,左右各2像素分別是按鈕左右邊框,中間1像素是按鈕的中間圖案,繪製時需要橫向拉伸至按鈕除去左右邊框的寬度。先恢復背景圖案,然後繪製高亮狀態按鈕,最後把整個工具欄圖象透明繪製上去就可以了,不會影響已經繪製好的背景。按下狀態的繪製稍稍有點不一樣,因爲需要把按下的按鈕圖象向右下方偏移1個像素,顯得按鈕是按下去了,這樣就不能一下把整個工具欄圖象畫上去,需要繪製按下的按鈕圖象,然後把這個按鈕左右邊的按鈕繪製上去。

 

用戶頭像旁邊的按鈕與工具欄的繪製類似,就不講解了,看源代碼就知道了,代碼裏面圖象的源區域座標,繪製目的座標有點繞,需要頭腦清楚纔不會搞糊塗。需要搞清楚圖象庫的DrawTo,StretchTo幾個函數的參數定義,具體看RingSDK的幫助文件吧。

 

順便說一下,圖象庫的雙緩衝繪製操作是直接操作的圖象數據,說白了只是一些COLORREF數組數據的搬移,除了繪製文字,根本不需要HDC的參與,因此速度和效率是很高的。

 

至此貼圖按鈕的三態模擬完成,還需要實現其他一些功能,工具提示,把工具欄按鈕的工具提示加了上去,不過最右邊兩個“打開消息盒子”和“更改外觀”的按鈕的工具提示需要額外處理,因爲窗口可以調整大小,一改變尺寸原來的座標就不對了,因此需要動態改變座標,在哪裏改呢,WM_SIZE消息?用戶改變窗口尺寸的時候其實是沒辦法把鼠標移到這兩個按鈕上的,而且WM_SIZE消息太頻繁了,我們只需要在尺寸調整完畢的時候更新一下座標就行了,這個消息就是WM_EXITSIZEMOVE:

 

實際程序裏的代碼要複雜一些。系統按鈕的工具提示沒加,有興趣的可以自己完成,需要跟這兩個工具欄按鈕一樣的處理,更新座標。

 

還有是按下用戶頭像旁按鈕彈出菜單,QQ2009這個菜單的最後兩項與系統欄圖標上彈出的菜單是不一樣的,這裏就不麻煩了,直接彈出了系統欄圖標上彈出的菜單。

 

個性簽名按鈕,按下後會顯示一個編輯框允許編輯簽名,這個不難,麻煩的是這個編輯框需要自動隱藏,處理其EN_KILLFOCUS消息並不能完全實現效果,因爲界面上沒有別的控件可以搶它的焦點,只有程序失去焦點纔會有這個消息,因此需要在WM_LBUTTONDOWN裏面再加個判斷處理,既然收到WM_LBUTTONDOWN消息,說明是在編輯框外按了鼠標,可以隱藏了。可惜這樣做還不夠,調整窗口尺寸的時候是點擊了非客戶區,沒有WM_LBUTTONDOWN消息,因此需要在WM_ENTERSIZEMOVE消息裏再加個判斷處理,這樣編輯個性簽名的功能纔算完善。

 

至此,這個程序已經實現了所有按鈕的三態模擬,系統按鈕的功能響應,工具欄按鈕的工具提示,可編輯個性簽名,可彈出菜單。加了WS_EX_TOOLWINDOW的擴展類型,程序不會出現在任務欄上,沒加限制只執行一次程序的功能。有個小小BUG,鼠標移到系統按鈕區域,系統按鈕顯示高亮狀態,這時把鼠標快速向上移出窗口,按鈕狀態不會恢復,因爲沒有了WM_MOUSEMOVE消息,解決這個問題需要TrackMouseEvent,然後在WM_MOUSELEAVE消息裏恢復按鈕狀態,有興趣的可以自己解決。

 

最後看看程序截圖:

 

 

界面下方的工具欄以後會用不同的方法實現,下一篇,將會講解如何實現一個激動人心的功能:界面調色。大家打開程序資源,可以看到裏面已經有更改外觀的設置對話框了,以後會講解如何自繪控件,實現QQ的這個更改外觀對話框。

 

演示程序下載地址:http://download.csdn.net/source/1982937

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章