标 題: 【原創】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