WINCE应用的UI实现方案
一、MFC的硬傷
在接手現(xiàn)在這個(gè)項(xiàng)目之前,我對WINDOWS平臺上的UI開發(fā)還是個(gè)白癡,除了MFC,就只知道GDI了。而且居然大言不慚地說用MFC只能畫畫灰色的對話框和按鈕。但不論如何,在嵌入式這種對成本極度敏感的項(xiàng)目上,我是不會拍板用MFC的。假設(shè)極端情況,定制后的系統(tǒng)是31.8M,我放一個(gè)ARMV4I上的MFC DLL進(jìn)去,大概500多K,那么只有兩種選擇,要么把32M的FLASH換成64M的——我的上司會把我給砍了,要么把應(yīng)用層的UI代碼全部重寫——我的下屬會把我給剁了。另一方面,WINCE上的應(yīng)用軟件我看過不少開源代碼,也接觸了一些外包的軟件,還真沒見過誰用MFC的。網(wǎng)上公論用MFC后會導(dǎo)致程序在不同平臺上移植性降低,因?yàn)槟悴荒苤竿麆e人的平臺給你準(zhǔn)備好奢侈的MFC。另一方面,多數(shù)高手都不屑用。我不是高手,但可以學(xué)人家擺譜,于是“不會用”就變成了“不屑用” ^_^
二、GDI的痛苦
把整套UI從CreateWindow開始寫起,的確很累人。我寫了500多行才勉強(qiáng)實(shí)現(xiàn)BUTTON類,另一個(gè)同事也用了500行左右才實(shí)現(xiàn)了TRACK BAR類,而且還未經(jīng)測試,也沒有很正式的CODE REVIEW。如果工業(yè)設(shè)計(jì)中心多增加幾種圖樣,那么我們就得多些幾個(gè)基類,然后再賠進(jìn)去CODE REVIEW的時(shí)間、測試時(shí)間、BUG FIX的時(shí)間。不痛苦,那是不可能滴~。
三、GWES的探路,我不是先鋒
群眾的智慧是無窮的。當(dāng)我這組同事的思維都受制于我的GDI方案時(shí),從通信部過來協(xié)助完成項(xiàng)目的軟件工程師從WINCE500的一個(gè)應(yīng)用SAMPLE CODE里把DialogBox函數(shù)給抓出來了。我認(rèn)為自己在定UI實(shí)現(xiàn)方案上很失敗的一點(diǎn)就是習(xí)慣性思維地從eVC里建立DIALOG RESOURCE后,立刻就要去點(diǎn)Class Wizard, 然后就是關(guān)聯(lián)MFC類。而他卻畫出來的DIALOG和BUTTON后,拿著RESOUCE ID從DialogBox函數(shù)建立起UI。并且我又習(xí)慣性思維地認(rèn)為DialogBox并不在STANDARD SDK 500里面,但他確實(shí)從STANDARDSDK_500里不引用其它LIB和DLL就把DialogBox和BUTTON用起來了,然后過來找我談?wù)撊绾伟褕D片疊加在DIALOG和BUTTON上。淚奔一百里~ 我應(yīng)該去找塊豆腐撞死~
四、最后的攻關(guān),GWES API能否成為我們需要的堅(jiān)實(shí)地基
GWES系列API能否實(shí)現(xiàn)我們所需的所有UI功能呢?沒有人知道,需要評估。剛才起草稿時(shí),我把這些都寫在同一篇文章里了?,F(xiàn)在覺得還是分篇好些,畢竟主題不同。請繼續(xù)看中篇:GWES方案上幾技術(shù)難點(diǎn)的解決
這里談?wù)摰乃^技術(shù)難點(diǎn),其實(shí)根本不值一提。只不過微軟定了一套游戲規(guī)則,我們目前不清楚這套游戲規(guī)則,花時(shí)間去摸索而已。
1、BUTTON的動畫效果
我們用了GWES里提供的BUTTON類,在WINCE PRODUCT DOCUMENT里的位置是
ms-help://MS.WindowsCE.500/wceshellui5/html/wce50grfButtonReference.htm
里面并沒有給出太多的說明,在Button Messages里提到有WM_CTLCOLORBTN消息, 但簡單試用后發(fā)現(xiàn)和預(yù)期效果不符。我亂翻亂點(diǎn)時(shí)注意到了eVC在畫圖時(shí),對BUTTON點(diǎn)右鍵出的菜單里,打開Properties,里面的Styles頁有個(gè)復(fù)選框"Owner draw", 我就抓住這根稻草,GOOGLE一把,方法就出來了。
當(dāng)Owner draw屬性被勾選時(shí),輪到該BUTTON繪圖時(shí),程序就不會跑DefDlgProc去畫個(gè)灰色突出的效果并把按鈕名字寫上去,而是給BUTTON的父窗口,也就是DIALOG的PROC發(fā)個(gè)消息WM_DRAWITEM,并且所帶的lParam中有我們需要的所有東西。來個(gè)強(qiáng)制轉(zhuǎn)換
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
然后根據(jù) lpDIS->itemAction 和 lpDIS->itemState 判斷BUTTON當(dāng)前狀態(tài),以決定加載哪幅圖片
DRAWITEMSTRUCT的詳細(xì)說明請參考 ms-help://MS.WindowsCE.500/wceshellui5/html/wce50lrfdrawitemstruct.htm
我簡單試了了一下,
(1) 初始化時(shí)
itemAction == 1;??// ODA_DRAWENTIRE
itemState == 16;??// ODS_FOCUS
(2) BUTTON被按下時(shí)
itemAction == 2;??// ODA_Select
itemState == 17;??// ODS_FOCUS | ODS_SelectED
(3) BUTTON被點(diǎn)擊后松開時(shí)
itemAction == 2;??// ODA_Select
itemState == 16;??// ODS_FOCUS
ODA和ODS宏定義數(shù)值
/*** from Winuser.h ***/
#define ODT_MENU????????1
#define ODT_LISTBOX???? 2
#define ODT_COMBOBOX????3
#define ODT_BUTTON??????4
//action
#define ODA_DRAWENTIRE??0x0001
#define ODA_Select??????0x0002
#define ODA_FOCUS?????? 0x0004
//state
#define ODS_SelectED????0x0001
#define ODS_GRAYED??????0x0002
#define ODS_DISABLED????0x0004
#define ODS_CHECKED???? 0x0008
#define ODS_FOCUS?????? 0x0010
在WM_DRAWITEM中有兩點(diǎn)要特別注意
(1) 不能在里面用InvalidateRect(lpDIS->hwndItem, lpDIS->rcItem, NULL),這會立即再發(fā)一個(gè)WM_DRAWITEM消息過來,接著再調(diào)InvalidateRect, 進(jìn)入死循環(huán)直至把設(shè)備上的內(nèi)存耗光,導(dǎo)致死機(jī)
(2) 對itemAction和itemState作判斷時(shí),必須把兩者同時(shí)都判斷了才能確定CLICK狀態(tài),單獨(dú)判斷action或單獨(dú)判斷state是不夠的,會導(dǎo)致重繪作用在不希望發(fā)生的情況下。并且不能簡單地作itemAction & ODA_Select這樣的位與判斷,還必須有排他性,我干脆就用==號了。
參考代碼如下
1 BOOL CALLBACK DialogProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
2 {
3???? switch(uMsg)
4???? {
5???????? case WM_DRAWITEM:
6???????? {
7???????????? LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
8
9???????????? if(lpDIS->CtlID == IDC_BTN_1)
10???????????? {
11
12???????????????? if( (lpDIS->itemAction & ODA_DRAWENTIRE)?? //這是初始化時(shí)的情況
13???????????????????? ||????( (lpDIS->itemAction == ODA_Select) && (lpDIS->itemState == ODS_FOCUS) ) ) //這是按下后放開時(shí)的情況
14???????????????? {
15???????????????????? //畫上未被按下時(shí)的圖片
16???????????????? }
17???????????????? else if( (lpDIS->itemAction & ODA_Select) && (lpDIS->itemState == (ODS_FOCUS | ODS_SelectED) ) ) //被按下時(shí)的情況
18???????????????? {
19???????????????????? //畫上被按下時(shí)的圖片
20???????????????????? MessageBeep(MB_OK); //順便響一聲,比較有手感
21???????????????? }
22???????????? }
23???????????? return TRUE;
24???????? }
25???????? default:
26???????????? //STUB
27???????????? break;
28???? }
29????
30???? return FALSE;
31 }
2、DLU和PIXEL的單位轉(zhuǎn)換
本來以為做完BUTTON效果后就OVER了,結(jié)果今天傍晚時(shí)候遇到一個(gè)很惱火的問題。在VC / eVC / VS中畫的對話框、按鈕等控件時(shí),在IDE右下角顯示的 XX * XX單位是DLU (Dialog Unit), 這是根據(jù)你設(shè)置的對話框字體大小而改變的。這種做法無可厚非。如果把字體改大了,那么DIALOG和BUTTON自然也被“撐”大了,比較靈活。但是我往上疊加的圖片是按像素(PIXEL)來算的。最后實(shí)現(xiàn)出來,有兩個(gè)方法。按照國際慣例,當(dāng)然是先講笨的方法,MSDN上的作風(fēng)也是如此。
(1)方法一:我查了下DLU和PIXEL之間的換算關(guān)系,有個(gè)講得比較全的網(wǎng)頁是http://support.microsoft.com/default.aspx?scid=kb;en-us;145994 (How to caculate dialog box units based on the current font in Visual C++) 按照文中的Method Two簡單測試了一下
1 BOOL CALLBACK DialogProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
2 {
3???? switch(uMsg)
4???? {
5???????? case WM_INITDIALOG:
6???????? {
7???????????? RECT rc={0,0,4,8};
8
9???????????? MapDialogRect(hDlg, &rc );
10???????????? printf("baseUnitX = %ld\n", rc.right);
11???????????? printf("baseUnitY = %ld\n", rc.bottom);
12
13???????????? //DLU_Weight = pixel_weight * 4 / baseunitX;
14???????????? //DLU_Height = pixel_height * 8 / baseunitY;
15
16???????????? return TRUE;
17???????? }
18???? }
19
20???? return FALSE;
21 }
對于我DIALOG中設(shè)置的Courier New 10pt字體,
第一步:從代碼中得到baseUnitX = 7, baseUnitY = 16,
第二步:套用公式
weight_DLU = weight_pixel * 4 / baseUnitX
height_DLU = height_pixel * 8 / baseUnitY
比如我想建立一個(gè)320*240 PIXEL的對話框,那么
weight_DLU = 320 * 4 / 7 = 182.86, 取整為183
height_DLU = 240 * 8 / 16 = 120
第三步:到VC里去拖動對話框的邊界,畫出183*120的對話框,那么代碼運(yùn)行起來后,通過GetClientRect查一下,的確是得到320*240 pixel的對話框了。
但是這種方法有一個(gè)致命的缺點(diǎn)。比如我要畫一個(gè)60*70的按鈕,按照上面的baseUnitX和baseUnitY折算后應(yīng)該為34.28 * 35,但是我畫34*35 DLU時(shí),運(yùn)行得到59*70 PIXEL窗口;畫35*35 DLU時(shí),運(yùn)行得到61*70 PIXEL窗口,無法恰到好處,這導(dǎo)致了往上疊加60*70 pixel的圖片時(shí),按鈕邊緣會出現(xiàn)不連續(xù)的黑點(diǎn)。所以這段文字只能當(dāng)作DLU和PIXEL的換算關(guān)系來玩了,沒有任何實(shí)際應(yīng)用價(jià)值。
(2) 方法二:正確的方法
其實(shí)也很簡單。晚飯后突然想到的,用SetWindowPos強(qiáng)制設(shè)置window的長寬和左上角坐標(biāo)。我在WM_DRAWITEM消息的處理中簡單試了一下:
SetWindowPos(lpDIS->hwndItem, NULL, 10, 10, 60, 70, SWP_NOZORDER);
設(shè)置前是59*70的按鈕,設(shè)置后就是60*70了,并且疊加圖片沒有任何問題。OK,整個(gè)基于GWES的UI方案至此成型了。后面貌似沒有什么大的技術(shù)障礙了。
至此,我沒有發(fā)現(xiàn)GWES方案上還有什么路障了,可以拍板使用這套方案了,和只用GDI寫UI相比,軟件研發(fā)的工作量大概降低了30%左右。當(dāng)然事情還沒有就這樣結(jié)束,這套方案對我這項(xiàng)目組的意義是很深遠(yuǎn)的。請看下篇:代碼中的一小步,項(xiàng)目進(jìn)度管理上的一大步
當(dāng)我試驗(yàn)SetWindowPos成功時(shí),我感覺到對我這個(gè)應(yīng)用開發(fā)組來說,這是一次革命了。項(xiàng)目進(jìn)度上的革命。
按照目前的進(jìn)度安排方式,事業(yè)部發(fā)布設(shè)計(jì)需求后各部門的工作狀態(tài)時(shí)這樣的:
(1) 軟件研發(fā),首先去確定底層接口,比如要調(diào)用BSP的哪些DeviceIoControl,要用哪些協(xié)議棧,要約定哪些注冊表鍵值,約定各應(yīng)用的進(jìn)程間通信。
(2) 工業(yè)設(shè)計(jì)中心, 同步開始設(shè)計(jì)UI圖片。
(3) 測試組,同步開始編寫測試?yán)?br />
而三者之中,工業(yè)設(shè)計(jì)中心是最慢的,界面風(fēng)格需要多次評審和修改,而且主觀因素很強(qiáng),領(lǐng)導(dǎo)說不好看,就得繼續(xù)改,隨便調(diào)整一下就是一兩天。以我做幾個(gè)項(xiàng)目的經(jīng)歷來看,往往是軟件研發(fā)人員和測試都完成第一步了,工業(yè)設(shè)計(jì)中心還沒發(fā)出切割圖,然后大家就傻在那里等資源。等工業(yè)設(shè)計(jì)中心正式發(fā)布切割圖后,軟件研發(fā)才開始埋頭苦干,這時(shí)候測試組又繼續(xù)閑著,等到出了ALPHA版才開始測試工作。
以我當(dāng)前這個(gè)項(xiàng)目為例,工業(yè)設(shè)計(jì)用兩個(gè)人花了足足一個(gè)月的時(shí)間才完成一級界面和二級界面,所以應(yīng)用組的人也不緊不慢地花了一個(gè)月的時(shí)間來作底層接口的研究和確定,慢慢地看文檔。實(shí)際上如果都是該領(lǐng)域的熟手,并且效率夠高的話,這些事情最多兩周就能做完了。
而如果用了GWES的API,加上SetWindowPos的做法之后,項(xiàng)目進(jìn)度上的優(yōu)勢是非常明顯的:
(1) 軟件研發(fā):確定底層接口后,立刻建立起DIALOG和BUTTON,EDITOR等控件,根本不用關(guān)心UI最后設(shè)計(jì)成什么樣。重點(diǎn)是上層的數(shù)據(jù)結(jié)構(gòu)和邏輯,和編寫代碼對底層接口進(jìn)行調(diào)用測試。UI并不再會成為瓶頸,只要隨便拖幾個(gè)控件出來就行了,坐標(biāo)和長寬也是隨意的,只要把功能做對了。
(2) 工業(yè)設(shè)計(jì)中心:可以慢慢地做圖片,一輪一輪地慢慢評審。由于疊加圖片的方式已經(jīng)很明確,并且程序員寫繪圖代碼時(shí)可以同時(shí)指定坐標(biāo)和長寬,直接修正原型開發(fā)時(shí)亂拼湊的界面,所以切割圖在軟件BETA RELEASE前兩三天發(fā)布就來得及了。
(3) 測試組:由于軟件研發(fā)可以很快地把界面丑陋、但功能實(shí)現(xiàn)好的ALPHA版程序發(fā)布,所以測試組可以大大提前手工測試的開始時(shí)間點(diǎn)。并且盡早開始BUG反饋。甚至于在UI圖片出來之前,就可以改幾輪BUG了。在UI圖片出來之前完成ALPHA版,并且改過幾輪BUG,這種情況在以前是從來不能想象的,應(yīng)用工程師肯定會說:圖片都還沒有,怎么寫代碼?寫了也白寫,反正還要改。
(4) 圖片發(fā)布后,每個(gè)應(yīng)用程序最多花兩天工夫作圖片疊加,而且原先寫的代碼在圖片疊加的工作中完全不用改動。
(2) 由于功能實(shí)現(xiàn)的代碼段沒有因?yàn)閳D片疊加而改動,所以之前測試的BUG仍然全部有效,并且因?yàn)閳D片疊加而產(chǎn)生的高級別BUG可能性很小。
OHYE,事情想象得真美妙。
難道是我年少無知,其實(shí)其他公司早就是這么開發(fā)應(yīng)用軟件的?我今天造的新詞很適合形容現(xiàn)在的心情:淚奔一百里。GO ON~嗯~嗯~一百里啊一百里~
轉(zhuǎn)載于:https://www.cnblogs.com/Jade2009/archive/2009/02/16/1391640.html
總結(jié)
以上是生活随笔為你收集整理的WINCE应用的UI实现方案的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 海康SDK/Ehome协议/RTSP协议
- 下一篇: VS code 快捷键常用