窗体在Windows编程中应该算是个比较泛泛的词,一般情况下都是指一窗口,如应用程序的主窗口、对话框等。但实际上在Windows中大部分显示界面都是窗体。比如按钮就是一个窗体,只不过经过编程后就可以有各种形态。
这个概念可能对于一些有经验的开发人员来说会比较好理解,而对于一些刚刚进入开发且主要是使用可视化IDE的朋友来说,可能会有些不好理解。因为在可视iDE中都是用拖放的,如Delphi,Vs studio等,使用这些可视iDE不需要懂得太深的编程知识就能够写出实用的程序。但是在使用m8sdk时这一点可能就要转变一下,因为M8SDK都是手工写代码,而且是经常要和API、消息等打交道。我也是初学者,只是说说体会而已,具体的还是靠个人去学习这些知识。实际上真正要在编程上有所造诣了解这些底层一点的知识是必须的。
回到M8上来,在M8sdk中已经为我们封装了窗体类,一般情况下我们只需要继承即可,CMzWndEx类就是窗体类。通过使用这个类我们可以不考虑窗体样式就可以和M8的系统界面一致,而且在和sdk提供的各种资源结合使用时也可以省去很多的代码。
M8SDK中的窗口使用起来也很简单,我们可以在SDK中提供的例程中学习到,只需要5步:
1、 从CMzWndEx继承
我们看一下魅族给的例子代码,我提取了关于窗体实现的代码部分:
- class CSample1MainWnd: public CMzWndEx
- {
- MZ_DECLARE_DYNAMIC(CSample1MainWnd);
- public:
- // A button control in the window
- UiButton m_btn;
- protected:
- // Initialization of the window (dialog)
- virtual BOOL OnInitDialog()
- {
- // Must all the Init of parent class first!
- if (!CMzWndEx::OnInitDialog())
- {
- return FALSE;
- }
- // Then init the controls & other things in the window
- m_btn.SetButtonType(MZC_BUTTON_PELLUCID);
- m_btn.SetPos(100,250,280,100);
- m_btn.SetID(MZ_IDC_TESTBTN1);
- m_btn.SetText(L"Exit Hello MZFC!");
- m_btn.SetTextColor(RGB(255,255,255));
- // Add the control into the window.
- AddUiWin(&m_btn);
- return TRUE;
- }
- // override the MZFC command handler
- virtual void OnMzCommand(WPARAM wParam, LPARAM lParam)
- {
- UINT_PTR id = LOWORD(wParam);
- switch(id)
- {
- case MZ_IDC_TESTBTN1:
- {
- if(1 == MzMessageBoxEx(m_hWnd, L"You have pressed Exit button, Really want exit?", L"Exit", MB_YESNO, false))
- PostQuitMessage(0);
- }
- break;
- }
- }
- };
- MZ_IMPLEMENT_DYNAMIC(CSample1MainWnd)
复制代码这是一个简单的窗体实现,先不要急着看全部的代码,先看第一行:
class CSample1MainWnd: public CMzWndEx
这一句代码就完成了两步工作:
一是定义了新类的类名CSample1MainWnd;
二是实现了从CMzWndEx继承。
另外我们看到在代码的第3行和最后一行有这样两句代码:
MZ_DECLARE_DYNAMIC(CSample1MainWnd);
MZ_IMPLEMENT_DYNAMIC(CSample1MainWnd); 其中的MZ_DECLARE_DYNAMIC和MZ_IMPLEMENT_DYNAMIC是两个宏,这个在C语言里见的非常多,宏简单的理解就是用了一个容易理解和记忆的名称代替了一大段的代码,从而减少代码量。呵呵,我就是这么理解的。
至此,有了类定义和上面的两个宏一个窗体的框框搭建好了,其他的代码将在其他的步骤中说明。
2、 申明需要使用的控件
有了窗口还不够,我们不能光打开一下光秃秃的面板吧,于是要增加一些元素,在本文引用的M8SDK例子代码里我们可以看到public部分有这一句代码:
- // A button control in the window
- UiButton m_btn;
复制代码UiButton就是m8sdk提供的按钮控件咯,这个例子就用到了这一个控件。与有些语言不同,C++有一种默认构造函数的东西,也就是说像上面这一句代码就完成了对象的实例化,具体的构造部分都由M8SDK完成了。要注意申明之后控件就已经创建了,不需要像C#一样要new一下,当然C++中也可以new,使用new后就要程序员去管理生命周期了。
至于不使用new和使用new主要是要了解堆栈的知识,我简单的说明一下:
每个应用程序可以获得的内存空间分为两种:
堆(heap)和栈(stack)。
堆称为“自由存储区”:其中的内存空间须由程序员来掌控。
栈称为“自动存储区”:其中的内存空间由编译器和系统自动完成的,程序员无需过问。
3、 重写OnInitDialog()方法
控件已经在内存里了,如何将控件显示在窗体上呢?OnInitDialog方法是一个虚方法,此方法会在窗体初始化时调用,于是通过在类中重写这个方法就可以将前面申明的按钮控件显示在窗口上了。先贴代码:
- virtual BOOL OnInitDialog()
- {
- // Must all the Init of parent class first!
- if (!CMzWndEx::OnInitDialog())
- {
- return FALSE;
- }
- // Then init the controls & other things in the window
- m_btn.SetButtonType(MZC_BUTTON_PELLUCID);
- m_btn.SetPos(100,250,280,100);
- m_btn.SetID(MZ_IDC_TESTBTN1);
- m_btn.SetText(L"Exit Hello MZFC!");
- m_btn.SetTextColor(RGB(255,255,255));
- // Add the control into the window.
- AddUiWin(&m_btn);
- return TRUE;
- }
复制代码这段代码中开头就调用了基类的OnInitDialog方法,如果这一步失败就退出。接下来的几行代码才是之前申明的m_btn的初始化代码,这里下三句是最重要的:
m_btn.SetID(MZ_IDC_TESTBTN1)
SetID是为控件设置唯一的ID值,这个值就有点像我们使用的身份证,不能重复。此ID会在很多地方使用到,记住必须保证唯一。
m_btn.SetPos(100,250,280,100)
SetPos就是设置控件的显示位置,这里面的四个参数分别是:x轴坐标、y轴坐标、宽度、高度。这些值都是相对于窗体而言的。
m_btn.SetText(L"Exit Hello MZFC!")
SetText是为按钮设置显示的文本。
其他的两句代码分别是设置按钮的显示样式和字体色彩。
接下我们会看到AddUiWin(&m_btn);这句代码很重要,只有使用了这句代码按钮m_btn才能真正的显示在窗口上。
4、 重写OnMzCommand;
在窗体上放置了一个按钮,那如何让这个按钮按下后会执行想要的操作呢?比如在按下按钮来退出应用程序,如何实现呢?
Windows是消息驱动的,所以必须响应一下按钮的点击消息,而按钮是放置在CSamlpe1MainWnd上的,所以必须从按钮的父容器中获得消息并响应。在M8SDK中可以重写窗体的OnMzCommand来实现这一点,看看在例子代码中主要做了哪些处理:
- // override the MZFC command handler
- virtual void OnMzCommand(WPARAM wParam, LPARAM lParam)
- {
- UINT_PTR id = LOWORD(wParam);
- switch(id)
- {
- case MZ_IDC_TESTBTN1:
- {
- if(1 == MzMessageBoxEx(m_hWnd, L"You have pressed Exit button, Really want exit?", L"Exit", MB_YESNO, false))
- PostQuitMessage(0);
- }
- break;
- }
- }
复制代码在这个方法中第一行代码是将传入的wParam参数值获取下来,这个值主是前面设置的m_btn的id。因为id是唯一的,所以在这里就可以知道是哪个控件触发的。在这个例子中只有一个按钮控件,但通常处理消息时都是使用switch,所以在这个例子中同样保留了这种规范。可以看到这里只有一个case,也就是用于m_btn的处理。
在case中使用了一个Yes_No对话框,这个对话框是M8SDK提供的,提醒用户是否要退出应用程序,如果点yes则执行PostQuitMessage(0),这个api就是告诉应用程序退出消息循环,没有了消息循环应用程序就会退出。最后记得每一个case都要跟一个break。
这样就实现了点击按钮退出应用程序的需求了。
5、 窗体实例化并显示
窗体部分的代码已经完成,接下来我们如何让这个窗体能显示出来呢?我们看下面的代码:
- // Application class derived from CMzApp
- class CSample1App: public CMzApp
- {
- public:
- // The main window of the app.
- CSample1MainWnd m_MainWnd;
- // Initialization of the application
- virtual BOOL Init()
- {
- // Init the COM relative library.
- CoInitializeEx(0, COINIT_MULTITHREADED);
- // Create the main window
- RECT rcWork = MzGetWorkArea();
- m_MainWnd.Create(rcWork.left,rcWork.top,RECT_WIDTH(rcWork),RECT_HEIGHT(rcWork), 0, 0, 0);
- m_MainWnd.Show();
- // return TRUE means init success.
- return TRUE;
- }
- };
复制代码这里同样写了一个类,但是从CMzApp中继承下来的,上一篇文章中说过CMzApp是M8SDK的应用程序基类,也就是说这里写的CSample1App是个应用程序类。在这个类中可以看到CSample1MainWnd已经申明,明白了吧,在此我们将CSample1MainWnd作为了应用程序的主窗体,而在应用程序类中需要重写Init方法来实现对窗体的调用及显示。Init即是initialization的简写,就是初始化。
代码中调用了窗体的Create方法,这个方法是设置窗体的显示大小和位置,调用了这个方法窗体才能正确的显示在M8手机的屏幕上。
调用Show方法是将窗体设置为显示,这个意思应该很明白吧。至此一个窗口就显示出来了。我们编绎并运行应用程序,打开后会显示CSample1MainWnd窗体,按下m_btn按钮即可以实现退出。
另外,对于窗体除了Show外还有另外一种显示方式,即DoModal();称为模态窗体。模态窗口显示后会接管消息循环,此窗体显示在应用程序的最上层,就像一个对话框一样。