• [转]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/*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控件实现的伪控件。

  • 相关阅读:
    DinnerNow中的Work Flow应用(上) 订单流程
    在Silverlight 2 (Beta2) 中使用webclient上传图片
    初尝dinnernow
    在silverlight 2 (beta1) 中使用BackgroundWorker
    徒劳的尝试 动态修改LINQ TO SQL的表名称
    你是否积极主动(Proactivity)?
    动态加载Silverlight应用的dll文件(可实现插件功能)
    silverlight与javascript交互操作
    DinnerNow中的Work Flow应用(下) 订单流程
    C#中winform窗体常用设置
  • 原文地址:https://www.cnblogs.com/fromchaos/p/1677028.html
Copyright © 2020-2023  润新知