九宫格菜单制作

来源:互联网 发布:网络推广职位 编辑:程序博客网 时间:2024/04/30 13:52

1 引言

随着3G时代的来临,基于智能手机的无线应用大量涌现。同时windows mobile逐渐壮大,已经成为智能手机市场占据第二位置的操作系统,基于windows mobile的应用更是蓬勃发展。而在手机应用的UI设计中,九宫格菜单是一种常见的形式,它操作简单方便,界面简约美观,深得用户的喜爱。本文将用两种不同的方案探讨九宫格菜单的在windows mobile上的设计实现,并比较两种方案的利弊。

2 利用控件制作九宫格菜单

       windows mobile提供了列表视图控件(List Control) 。列表视图控件可以在窗体中显示和管理列表项,能够以图标的形式显示数据,就像Windows的桌面一样可以摆放若干个图标,点击相应的图标就可启动相应的程序。这恰恰符合了设计要求,下面看其具体的实现。

2.1 创建相关对象

       首先建立一个单文档的应用程序,并在View类的头文件中添加代码:

CListCtrl m_listCtrl;

CImageList m_imageList;//用于保存图像列表

int m_nSelected;//用于标识哪一个项目被选中,并在构造函数中初始化为4

View类的OnCreate成员函数中添加代码:

m_imageList.Create(64,64,ILC_MASK|ILC_COLOR,1,1);//64*64为图片大小

m_listCtrl.Create(LVS_SINGLESEL|LVS_NOSCROLL|LVS_ICON|WS_CHILD|WS_VISIBLE|LVS_SINGLESEL,CRect(rect),this,IDC_LISTMENU);

2.2生成九宫格菜单界面

       下面关心怎样将图片装载进程序中。首先要解决的第一个问题是使用.bmp还是.jpg格式的图片源文件。.jpg格式的图片比bmp格式的图片占存储空间小很多,但缺点是在装载时也要转换为.bmp格式。采用.jpg格式的源文件,一方面当程序运行时,装载图片后占用的内存空间并不小,另一方面,格式转换要占用系统资源,延长程序的启动时间。考虑到嵌入式设备较小的存储空间,并且程序往往都是在需要时才加以运行,因此采用.jpg格式的源文件是合理的选择。

       那么怎样将.jpg格式的源文件图片导入程序中并保存在m_imageList成员变量中呢?可以有两种方法:

(1)              使用函数SHLoadImageResource()。这个函数可以将资源文件中的图片文件(可以是.jpg格式也可以是.gif格式等)装载到内存中,将其转换为位图格式,并将位图的句柄返回。然后就可以调用CImageList::Add()成员函数将图片保存到m_imageList中,因为有九个菜单项,即九个图标,因此要将九张图片依次导入。这里需要注意一点:如果图片源文件的名字为abc.jpg,在将图片导入资源文件时,应将资源类型设问GIF,并将该图片资源的ID号改成IDR_ABC_JPG。注意这之间的对应关系,因为只有这样图片才能正确的被加载进来。

(2)              使用函数SHLoadImageFile()。该函数可以直接装载位于手机文件当中的图片,而不必把它先导入到资源文件当中。例如:

::SHLoadImageFile(L"//Program Files//JiuGongGeMenu//Image//abc.jpg");

显然第二种方法更加灵活一些,如果用户想更换自己喜欢的一组图标,只要将图片的源文件替换掉即可。这里简单的说明一下换肤技术的实现:只需要设置一个标识flag,当flag=1时装载一组图片,当flag=0时装载另一组图片。

至此已经将9张图片到存到m_imageList当中了,下面要做的是将它与m_listCtrl关联起来,并将图片显示到窗口中。添加如下代码:

    m_listCtrl.SetImageList(&m_imageList,LVSIL_NORMAL);

    m_listCtrl.SetBkColor(RGB(255,255,255));//设置背景颜色

    m_listCtrl.SetTextColor(RGB(80,80,80));设置文本颜色

m_listCtrl.InsertItem(0,LABC,0);//将第m_imageList中的0号图片插入到//m_listCtrlO号项目当中,并且指定项目名为ABC//照此方法依次将九张图片加入到m_listCtrl中。

然后调用CListCtrl::SetItemPosition()将项目以九宫格的形式显示在窗口的客户区。

到现在为止,程序应经拥有了一个九宫格菜单的界面,但还不能响应触笔的点击。

 

2.3 响应触笔点击

当用触笔单击窗口中的某个项目时,View类就会收到NM_CLICK通知消息,只要对该消息进行消息响应即可。

后台程序传进来一个NMHDR *型的指针pNMHDR,将其重新解释成LPNMHEADER型的指针,既:LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);   

phdr是一个指向NMHEADER结构体的指针。这个结构体的成员iItem就标志了当前哪一个菜单项目受到了点击。然后调用ResponseCmd(phdr->iItem)即可。这里ResponseCmd()是处理命令的函数,具体实现时可以用一个switch条件语句。

2.4按键的处理

       当按“上下左右”键时,发现程序并不能像九宫格菜单一样,选中项随着按键循环移动。为解决这个问题需要定义一个成员变量int m_nSelected。用这个成员变量标识当前那个项目被选中。因为窗口中的项目是按次序排列好的,如图1所示。因此按“上”键,只需要令m_nSelected-3,当m_nSelected<0时只要对其加9,就可以上下实现循环;当按“右”键时,只需要令m_nSelected+1,m_nSelected>8时,只要令m_nSelected-9既可以实现选中第8项后再“右”键可以选中第0项。“下”键和“左”键同理。当按下“确定”键时,只需要调用ResponseCmdm_nSelected)即可。

1  九宫格菜单界面

      

当有按键按下时,View类会受到LVN_KEYDOWN消息,添加消息响应函数。同样,后台程序传进来一个NMHDR *型的指针pNMHDR,将其重新解释成LPNMLVKEYDOWN型的指针,既:LPNMLVKEYDOWN pLVKeyDow = reinterpret_cast<LPNMLVKEYDOWN>(pNMHDR)

pLVKeyDow是一个指向NMLVKEYDOWN结构体的指针。这个结构体的wVKey成员指明了按键的ANSCLL码。

代码如下:

         switch(pLVKeyDow->wVKey)

        {

        case 38:       

            m_ nSelected = m_ nSelected -3;

            if(m_ nSelected <0) m_ nSelected +=9;

            break;

        case 39:

            m_ nSelected = m_ nSelected +1;

            if(m_ nSelected >=9) m_ nSelected =0;

            break;

        case 40:

            m_ nSelected = m_ nSelected +3;

            if(m_ nSelected >=9) m_ nSelected -=9;

            break;

        case 37:

            m_ nSelected = m_ nSelected -1;

            if(m_ nSelected <0) m_ nSelected =8;

            break;

        case 13:

            ResponseCmd(m_ nSelected);

            break;

            }

然后调用CListView::SetItemPosition()把第m_ nSelected置于选中状态就可以了。

至此,一个简单的九宫格菜单就做好了,当用触笔单击其中某项时能够进行命令响应;当按上下左右键时,选中项随之移动;按确定键可以使选中项进行命令响应。

但是这个九宫格菜单似乎还不够漂亮。注意到当某项被选中时只是加上了蓝色的掩码,而很多手机应用程序选中的项目通常是一会儿变大,一会儿变小,交替进行。一方面十分美观,另一方面可以引起用户的注意,方便其使用。这在实现时往往是用两幅大小不同的位图交替显示来实现的,而在m_imageList.Create(64,64,ILC_MASK|ILC_COLOR,1,1)m_imageList中的图片大小必须一样大。同时,在同一时刻一个CListView对象只能和一个CImageList对象关联,这样就不可能实现上述功能。因为该功能要求同一时刻能显示大小不一的9张图(处于选中状态的为大图,其他为小图),也就是要在CImageList的对象中保存大小不同的图片,这显然是做不到的。

下面用另一种类似于绘图的方法制作九宫格菜单,这种方法灵活方便,并可可以实现第一种方案难以实现的效果。

3 用绘图制作九宫格菜单

       首先说一下这种方法的设计思想:先将窗口的客户区均分为9个格子,如图2所示,当然画出来只是为了直观一点儿,实际上格子只是概念上的。然后将9张图片分别绘制与各个格子内。当某一项处于选中状态时,就用另一张稍大的图片替换掉格子中原来的图片,设置一个定时器,交替显示两幅图片就可以实现一会儿变大一会儿变小的效果。另外,当发生触笔点击事件时,根据发生点击的位置就可以判断是哪个格子内发生了点击,从而确定应有哪个菜单项目进行响应。

2 将窗口客户区分为9个格子

3.1 绘制九宫格菜单界面

       首先建立一个单文档的应用程序。为了实现当项目被选中时,其对应的图标会变大的功能,应该将18张图片导入程序当中。也就是说每一个格子都应该有俩张图片与之对应,一张大的,一张小的。定义HBITMAP 型的数组m_hBitmap[18],使用SHLoadImageFile()依次导入这18张图片,并将图片的句柄保存到m_hBitmap数组中。为了以后使用方便,约定数组中第0号图片与第0+9号图片是要在第0个格子内显示的图片,其中0号图片为小图片,0+9号图片为大图片。数组中第1号与第1+9号图片是要在第1个格子内显示的图片。其它依次类推。

    下面就要将这些位图绘制到窗口上。有两种方法,一种是将位图一张一张的贴到窗口的客户区。另外一种是先创建一个兼容的内存位图,将位图先一张一张的贴到这个兼容的内存位图上,再将该兼容位图一次性绘制到视类窗口上。两种方法相比较,第一种占内存资源少,执行速度快,但有可能造成屏幕的闪烁(视设备的运行速度而定);第二种方法消耗内存资源多,执行速度相对慢一些,但可以较好的避免屏幕闪烁。

    那么为什么执行慢的反而更不容易出现屏幕闪烁呢?人眼的分辨能力是有限的,如果绘制过程中人眼不能察觉,用户就会不会感到屏幕闪烁。第二中方法虽然在准备兼容位图时消耗了一些时间,但在将兼容位图绘制到视类窗口的过程是很快的;相反第一种方法要一张一张的把位图绘制到窗口中,导致绘制过程较长,因而更容易出现闪烁。总之,闪烁与否仅仅取决于绘制时间,与绘图的准备时间无关。采用第二种方法的代码如下: 

  CDC* pDC=GetDC();

  CDC dcMemory;

  dcMemory.CreateCompatibleDC(pDC);//创建兼容的DC, dcMemory

  CBitmap bmpBnkgnd;

  bmpBnkgnd.CreateCompatibleBitmap(pDC,240,266);//创建兼容的内存位图

  dcMemory.SelectObject(&bmpBnkgnd);//将兼容位图选进兼容的DC

  CDC dcMemoryTemp;

  dcMemoryTemp.CreateCompatibleDC(pDC);//创建兼容的DC, dcMemoryTemp

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

  {

      dcMemoryTemp.SelectObject(m_hBitmap[i]);//将位图选进dcMemoryTemp

      dcMemory.BitBlt((i%3)*80+8,(i/3)*(80+6)+6, 64,64, &dcMemoryTemp,

          0, 0, SRCCOPY);//dcMemoryTemp中的位图贴到dcMemory中的位图上

}

pDC->BitBlt(0,0,240,266,&dcMemory,0,0,SRCCOPY);//dcMemory中的位图绘制到视//类窗口

ReleaseDC(pDC);

dcMemory.DeleteDC();

dcMemoryTemp.DeleteDC();

       这里BitBlt()是一个功能非常强大的函数,还可以实现位图的切分等多种功能。另外StretchBlt()还可以实现位图的压缩和拉伸。

这样九宫格菜单的图形界面就做好了,然后调用CDC::DrawText()在每个格子里图标的下面输出文本,界面就完全做好了。

3.2 对按键消息的响应

       还是用m_nSelected标识哪个项目被选中,并初始话为4。当有按键被按下时,视类会收到WM_KEYDOWN消息。添加消息响应函数。

       如果是方向键被按下了,则应该将m_nSelected对应的项目的大图片(m_nSelected+9号图片)擦除,并在该格子里绘制对应的小图片(m_nSelected号图片)。同时,m_nSelected更新后(具体参见2.4按键的处理的代码),应将m_nSelected对应的项目的小图片(m_nSelected号图片)擦除,在该格子内绘制大图片(m_nSelected+9号图片)。

       如果是确定键被按下了,则根据m_nSelected的值进行命令响应,参见2.4 按键的处理。

       当然,如果要实现处于选中状态的图标可以时大时小的变化,可以设置一个定时器,在WM_TIMER的消息响应函数中添加两幅图片交替显示的代码。

3.3 对触笔点击的响应

       当发生点击时,View类就会受到WM_LBUTTONDOWN消息。添加消息响应函数,根据点击的坐标可以判断出点击发生在哪一个小格子的内部,如果在某个格子的内部就证明该格子对应的菜单项目受到了点击并由该菜单项进行命令响应。其实在操作系统的内部也有一套类似的机制,当发生点击事件时,操作系统根据单击坐标点的位置和当前窗口的相对位置来确定点击发生在哪个窗口,并将相应的消息投送到对应的消息队列。这里还有一些绘图处理,可以参见3.2对按键消息的响应,这里就不再详述了。

4 小结

       以上两种方案均可以制作九宫格菜单,对比两种方案不难看出:第一种方案由于采用标准的控件,因而简单方便代码量少,并且不需要对windows mobile的绘图机制有深刻的理解,适合快速开发,但同时由于控件做了很多集成定制的东西,很难适应各式各样的要求。相比之下第二种方案就十分灵活,基本可以满足九宫格菜单的各种要求,但同时开发的工作量稍大一些。方法的优劣取决于用户的需求,如果开发时间较短并且用户不要求华丽的界面,那么第一种方案是很好的选择;反之则应选用第二种方案。

原创粉丝点击