天天看點

[轉]WIN MOBILE UI開發入門

标 題: 【原創】WIN MOBILE UI開發入門

作 者: 打小

時 間: 2009-06-06,12:17:14

鍊 接: http://bbs.pediy.com/showthread.php?t=90857

這是我昨天發在新人交流版塊的文章,還是放到這邊比較合适。嫌麻煩,沒有叫版主轉移,我自己拷貝過來咯。。。。

首先感謝 『嵌入式平台安全』版塊版主加百力的邀請(貌似算不上邀請,表達不好,Whatever),才有了這篇文章的誕生,

在下才識有限,接觸WIN MOBILE開發時間不久,倉促整理了一些UI部分入門級的東東(内容并不系統,全面),希望能夠對剛涉足MOBILE開發的朋友有幫助。         

注:由于自身局限,本文不涉及.net  

OK,廢話完畢,歡迎雞蛋和鮮花。

本文假設您已經了解SMART PHONE與POCKET PC的差別。沒有特别說明,均指在POCKET PC上。

現狀:IPHONE的風靡,引領了目前智能手機的系統及APP界面潮流。MS雖然釋出了WINDOWS MOBILE 6.5,但在将來不短的 一段時間内,承載MOBILE 6.2及以下系統的PPC仍将是主流。手持裝置的特殊性決定了其上APP界面表現的重要性,很多時候甚至項目70%的代碼 都與界面有關。

一。GDI繪圖基礎(必須掌握的基礎)

1.1  裝置環境DC

“裝置環境”(device context),經常簡稱寫為DC, 是Windows 用來管理通路顯示和列印裝置的工具。

1)  什麼是DC

一個DC是一個結構,它定義了一系列圖形對象的集合以及它們相關的屬性,以及影響輸出效果的一些圖形模式。

這些圖形對象包括一個畫線的筆,一個填充和painting的畫刷,一個用來向螢幕拷貝的位圖,一個定義了一系列顔色集合的調色闆,一個用來剪裁等操作的區域。

例如:使用TextOut,DC的屬性确定了文字的顔色、文字的背景色、顯示文字時字型等等。

如何了解DC:

DC用于繪圖輸出,輸出裝置包括螢幕,列印機等,在WM中一般總是螢幕輸出,總是與特定窗體相關。實際上是GDI内部儲存的資料結構, DC中的有些值定義了GDI繪圖函數工作的細節。

2)  擷取裝置DC

一個應用程式從不直接地通路(access)dc,常見的取得dc的方式:

HDC GetDC( HWND hWnd);

HDC GetWindowDC( HWND hWnd);

HDC GetDCEx( HWND hWnd, HRGN hrgnClip,DWORD flags);

Value   Description

DCX_WINDOW   傳回與視窗矩形而不是與客戶矩形相對應的裝置上下文環境

DCX_CACHE   從高速緩存而不是從OWNDC或CLASSDC視窗中傳回裝置上下文環境。覆寫CS_OWNDC和CS_CLASSDC。

DCX_PARENTCLIP(*)   使用父視窗的可見區域,父視窗的WS_CIPCHILDREN和CS_PARENTDC風格被忽略,并把裝置上下文環境的原點,設在由hWnd所辨別的視窗的左上角。

DCX_CLIPSIBLINGS   排除hWnd參數所辨別視窗上的所有子視窗的可見區域。

DCX_CLIPCHILDREN   排除hWnd參數所辨別視窗上的所有子視窗的可見區域。

DCX_NORESETATTRS(*)   當裝置上下文環境被釋放時,并不重置該裝置上下文環境的特性為預設特性。

DCX_EXCLUDERGN   從傳回裝置上下文環境的可見區域中排除由hrgnClip指定的剪切區域。

DCX_EXCLUDEUPDATE(*)   排除視窗更新區域.

DCX_INTERSECTRGN   The clipping region identified by hrgnClip is intersected with the visible region of the returned device context.

DCX_INTERSECTUPDATE   Returns a region that includes the window's update region

DCX_VALIDATE(*)   當與DCX_INTERSECTUPDATE一起指定時,緻使裝置上下文環境完全有效,該函數與 DCX_INTERSECTUPDATE和DCX_VALIDATE一起使用時與使用BeginPaint函數相同。.

1.2  有效區和無效區

Windows内部為每個視窗儲存一個「繪圖資訊結構」,這個結構包含了包圍無效區域的最小矩形的坐标以及其它資訊,這個矩形就叫做「無效矩形」,或者「無效區域」。當重繪視窗時(收到WM_PAINT消息),僅需重繪「無效區域」就可以 。

窗體收到WM_PAINT消息,

BeginPaint擷取無效區域的dc,計算無效區域,

重繪無效區域,

EndPaint函數,将無效區域标記為有效區域。

Windows不會将多個WM_PAINT消息都放在消息隊列中。WM_PAINT隻會更新無效區域内資訊。在處理WM_PAINT消息後,整個程式界面都變為有效區域,如果不對程式進行任何操作,它是不會産生無效區域的。引起WM_PAINT消息的事件:

使用者移動視窗時,先前被覆寫的視窗顯示時;

使用者調整視窗大小

使用ScrollDC等函數滾動視窗時

使用者子產品發送WM_PAINT消息(一般在InvaladataRECT 之後發送消息)

WM_PAINT

case WM_PAINT:

{

HDC hdc;

PAINTSTRUCT ps;

RECT rect;

hdc = BeginPaint(hwnd, &ps);

GetClientRect(hwnd, &rect);

DrawText(hdc, g_szMessage, -1,   &rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER);

EndPaint (hwnd, &ps);

}

break;

将有效區域标記為無效區域

BOOL InvalidateRect( HWND   hWnd,const RECT *lpRect, BOOL  bErase);

InvalidateRect隻是增加重繪區域,在下次WM_PAINT的時候才生效 。使無效區域立刻重繪:

InvalidateRect(…);

hWnd窗體發出WM_PAINT的消息,

lpRect:是指定要重新整理的區域,此區域外的區域 不被重繪,這樣防止一個局部的改動,而導緻整個客戶區域重繪而導緻閃爍。     

BOOL  bErase的參數TRUE表示在無效區域重繪之前之前還向窗體發送WM_ERASEBKGND消息,這樣将導緻,用背景色将所選區域覆寫一次後再重繪,(背景色可通過設定BRUSH來改變)。

将無效區域标記為有效區域

可以通過呼叫ValidateRect函數使顯示區域内的任意矩形區域變為有效。如果這呼叫具有令整個無效區域變為有效的效果,則目前隊列中的任何 WM_PAINT消息都将被删除。

windows不會将多個WM_PAINT消息都放在消息隊列中。

1.3  位圖

BMP(Bitmap-File)圖形檔案是Windows采用的 圖形檔案格式,在Windows環境下運作的所有圖象處理軟體都支援BMP圖象檔案格 式。Windows系統内部各圖像繪制操作都是以BMP為基礎的。 Windows 3.0以前的BMP圖檔案格式與顯示裝置有關,是以把這種BMP圖象 檔案格式稱為裝置相關位圖DDB(device-dependent bitmap)檔案格式。Windows 3.0以後的BMP圖象檔案與顯示裝置無 關,是以把這種BMP圖象檔案格式稱為裝置無關位圖DIB(device-independent bitmap)格式(注:Windows 3.0以 後,在系統中仍然存在DDB位圖,象BitBlt()這種函數就是基于DDB位圖的,隻不過如果你想将圖像以BMP格式儲存到磁盤檔案中時,微軟 極力推 薦你以DIB格式儲存),目的是為了讓Windows能夠在任何類型的顯示裝置上顯示所存儲的圖象。BMP位圖檔案預設的檔案擴充名是BMP或 者 bmp(有時它也會以.DIB或.RLE作擴充名)。

檔案結構:

位圖檔案可看成由4個部分組成:位圖檔案頭(bitmap-file header)、位圖資訊頭(bitmap- information header)、彩色表(color table)和定義位圖的位元組陣列,它具有如下所示的形式。

位圖檔案的組成                     結構名稱               符号

位圖檔案頭(bitmap-file header)   BITMAPFILEHEADER       bmfh

位圖資訊頭(information header)   BITMAPINFOHEADER       bmih

彩色表(color table)              RGBQUAD                aColors[]

圖象資料陣列位元組                 BYTE                   aBitmapBits[]

二:常用繪圖技巧

2.1 雙緩沖繪制技術:

限于手機的硬體性能,開發者經常需要解決螢幕閃爍的問題,一般都可以通過雙緩沖繪制來解決。

所謂的雙緩沖就是把所有内容先繪制在一個記憶體DC上; 之後一次性拷到螢幕DC,作為最終顯示。 

記憶體DC,是一個虛拟的記憶體裝置上下文,對它進行繪圖等操作,不會顯示在螢幕上,在記憶體DC繪制完成之後,再拷貝到螢幕上,這樣可以避免因為操作而給螢幕帶來的閃爍。

步驟:

HDC memdc = CreateCompatibleDC(hdc);//建立和目的DC一緻的内DC

HBITMAP  hbmp;

hbmp= CreateCompatibleBitmap(hdc,rect.Width(),rect.Height());

SelectObject(memdc,bmp);    //建立一張螢幕DC的位圖并選入記憶體DC

Drawmemdc (memdc) ;       //在記憶體DC繪圖

BitBlt(hdc,0,0,rect.right,rect.bottom,memdc,0,0,SRCCOPY);//繪制到螢幕DC

DeleteObject(memdc);       //銷毀資源,釋放記憶體DC

2。2圖像半透明混合:

原理:操作像素點陣,假設一幅圖象是A,另一幅透明的圖象是B,那麼透過B去看A,看上去的圖象C就是B和A的混合圖象,設B圖象的透明度為 alpha(取值為0-1,1為完全透明,0為完全不透明),Alpha混合公式如下:

R(C)=(1-alpha)*R(B)+alpha*R(A)

G(C)=(1-alpha)*G(B)+alpha*G(A)

B(C)=(1-alpha)*B(B)+alpha*B(A)

步驟:1:擷取原圖大小  2:擷取原始像素點陣  3:對各像素RGB分量進行混合  

for(int i=0;i<length;i++)

{

for(int t=0; t<width; t++ )

{

pix_array[i*width+t] = Getpixel(hdc,i,t);

}

}

for(int i=0; i<length*width; i++)

{

newpix_array[i].byRed  = pix_array[i].byRed* alpha + 255*(1- alpha);

newpix_array[i].byGreen  = pix_array[i].byGreen* alpha + 255*(1- alpha);

newpix_array[i].byBlue = pix_array[i].byBlue* alpha + 255*(1- alpha);

}

或者直接調用API:AlphaBlend

2.3背景色透明

調用API:TransparentBlt

BOOL TransparentBlt(

HDC hdcDest,        //目的DC句柄

int nXOriginDest,   //目的DC左上X軸坐标

int nYOriginDest,   //目的DC左上Y軸坐标

int nWidthDest,     //繪制時的矩形寬度

int hHeightDest,    //繪制時的矩形高度

HDC hdcSrc,         //源DC句柄

int nXOriginSrc,    //源DC左上X軸坐标

int nYOriginSrc,    //源DC左上Y軸坐标

int nWidthSrc,      //源矩形寬度

int nHeightSrc,     //源矩形高度

UINT crTransparent  // 需要透明的顔色RGB值

);

三:PPC UI常見問題

3.1 手勢識别

原理:捕獲使用者觸筆點選事件,收集觸筆運動軌迹,觸筆離開後綜合收集到的軌迹點,進行分析判斷。

基本實作:捕獲系統響應消息,進行處理

單擊事件:WM_LBUTTONDOWN

移動事件:WM_MOUSEMOVE

彈起事件:WM_LBUTTONUP

常用技巧:

控件聚焦 : 由于手持裝置螢幕有限,控件分布相對密集,對使用者操作的精準性有一定要求,當使用者進行滾動條的下拉或者其他移動控件的動作時,很有可能在移動過程中觸筆或手指會離開控件範圍,進而中斷移動操作,而這并非使用者所預期。

是以,有必要對控件進行聚焦,即便出現上述狀況,控件仍能收到WM_MOUSEMOVE事件。

解決該問題可調用API : SetFocus( HWND hWnd);

3.2界面自适應

由于POCKET PC支援橫豎屏兩種模式,在豎屏,橫屏間切換時,如果不對程式的控件坐标進行調整,會造成界面混亂的後果。

基本實作:捕獲系統的橫豎屏切換消息,判斷将切換到何種模式,在OnSize()中進行相應處理。

常用方法:

另外由于PPC螢幕尺寸的繁雜,對不同螢幕尺寸的自适應也需要注意。此處不再贅述。

1):調整控件坐标

捕獲WM_SIZE消息,在響應函數中擷取目前螢幕模式,根據螢幕大小對控件坐标進行調整,使整個界面自适應。

2):準備兩套不同UI資源

針對橫豎屏各準備一套UI資源,在WM_SIZE消息的處理函數中擷取目前螢幕模式,根據不同模式調用相應UI資源,達到界面自适應的目的。

3.3 螢幕輸入面闆(sip)

在POCKET PC上進行應用開發時,并非所有界面都需要SIP輸入面闆,是以經常需要對SIP輸入面闆的顯示和隐藏處理。

下面以直覺的圖給出兩者的對比:

在WINDOWS MOBILE中隐藏SIP的方法很多,以下簡要介紹其中幾種方法:

1)SHSipPreference(m_hWnd,SIP_Down);

2)SIPINFO si;

memset(&si,sizeof(si));

SHSipInfo(SPI_GETSIPINFO,0,&si,0);

si.fdwFlags&=~SIPF_ON;

SHSipInfo(SPI_SETSIPINFO,0,&si,0);

3) SHFullScreen(hDlg,SHFS_SHOWTASKBAR,SHFS_HIDESIPBUTTON);

4)SipShowIM(SIPF_DOWN);

5) 獲得視窗名為menu_worker的SIP視窗句柄,進行隐藏或顯示,實作:

CWnd* pWndSIP = FindWindow( _T("menu_worker"), 0 );

if ( pWndSIP )

{

pWndSIP->ShowWindow(SW_HIDE);// SW_SHOW

}

6)另外通過IMM也可以對SIP進行控制。

3.4  MenuBar定制

MOBILE底部的MenuBar在應用程式中扮演着和使用者互動的關鍵作用,根據不同的需求,經常需要定制MenuBar,本節将介紹如何對 MenuBar資源進行更改。

基本操作:

1. SHMENUBARINFO結構體

typedef struct tagSHMENUBARINFO {

DWORD         cbSize;      // SHMENUBARINFO結構體大小

HWND          hwndParent;  //CommondBar父視窗句柄

DWORD         dwFlags;    //MenuBar類型辨別

UINT            nToolBarId;  //工具欄辨別

HINSTANCE      hInstRes;    //控制資源的執行個體句柄

int               nBmpId;     // 按鈕背景bmp圖檔資源ID

int               cBmpImages; //bmp圖檔資源數量

HWND           hwndMB;    //【輸出參數】控制MenuBar的視窗句柄

COLORREF  clrBk;            //MenuBar背景顔色參數,包括SIP()

} SHMENUBARINFO,*PSHMENUBARINFO;

2.MenuBar類型

可以通過對dwFlags的設定,建立不同類型的MenuBar

SHCMBF_COLORBK          設定menu bar背景顔色

SHCMBF_EMPTYBAR      建立一個空的menu bar

SHCMBF_HIDDEN          建立一個隐藏的menu bar

SHCMBF_HIDESIPBUTTON   建立一個沒有sip的menu bar

SHCMBF_HMENU              通過資源定制menu bar而不通過工具欄

3.MenuBar的建立

SHMENUBARINFO mbi;

memset(&mbi, 0, sizeof(SHMENUBARINFO));

mbi.cbSize     = sizeof(SHMENUBARINFO);

mbi.hwndParent = hWnd;//視窗句柄

mbi.nToolBarId = IDR_MENU;//菜單資源号

mbi.hInstRes   = g_hInst;//執行個體句柄

SHCreateMenuBar(&mbi)

g_hWndMenuBar = mbi.hwndMB;

建立完MenuBar執行個體之後,再對資源檔案進行修改,指定

IDR_MENU SHMENUBAR DISCARDABLE

BEGIN

IDR_MENU, //ID

2,//個數

I_IMAGENONE, IDM_OK,

TBSTATE_ENABLED,

TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE,

IDS_OK,

0, NOMENU,

I_IMAGENONE,IDM_HELP,TBSTATE_ENABLED, TBSTYLE_DROPDOWN|TBSTYLE_AUTOSIZE,

IDS_HELP, 0, 0,

END

IDR_MENU SHMENUBAR DISCARDABLE

BEGIN

IDR_MENU,

1,

I_IMAGENONE, IDM_OK, TBSTATE_ENABLED,

TBSTYLE_BUTTON |TBSTYLE_AUTOSIZE,

IDS_OK, 0, NOMENU,

END

通過以上操作,開發者可以給自己的程式定制個性化的MenuBar。

四:定制控件

這是目前MOBILE開發很重要,也很讓開發者頭疼的一個問題,有很多可以說但一下子說完貌似又不現實。大體來說,主要分為控件自繪,以及“僞控件”。

所幸,大部分控件和桌面系統一緻,碰到有關控件的自繪和僞控件的實作問題,相關資料網路上都能找到不少,故在此不再展開。(combobox和桌面系統不同,MOBILE上不支援自繪)

如果覺得描述的不夠具體可以看看下面的連結,是一些筆者曾制作的UI:http://hi.baidu.com/%C0%B6%C9%AB%D3%...5a044df04.html

其中 “按鈕GO”就是BUTTON控件自繪

另外的COMBOBOX及顯示資料的表格控件,都是用STATIC控件實作的僞控件。

轉載于:https://www.cnblogs.com/fromchaos/archive/2010/03/03/1677028.html