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