yongshi123

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

     本文假设您已经了解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/*COMMAND ID*/, 
     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控件实现的伪控件。

posted on 2009-07-21 00:25  yongshi123  阅读(1037)  评论(0编辑  收藏  举报