65.9K
CodeProject 正在变化。 阅读更多。
Home

Palm 手持设备开发入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (4投票s)

2002年11月4日

CPOL

6分钟阅读

viewsIcon

167518

downloadIcon

155

使用经典的“Hello World!”示例编写您的第一个Palm应用程序。

Sample Image - palmintro_helloworld.jpg

引言

最近在Code Project的各个论坛里有几个人在问Palm掌上设备的问题,虽然Code Project主要是一个Windows编程网站,但我认为添加一些Palm的文章也会有所裨益。所以,废话不多说,先来点东西让您尝尝鲜。

Palm开发资源

演示应用程序和源代码是使用Metrowerk的CodeWarrior for PalmOS IDE(6.0版)创建的。资源是使用Constructor for PalmOS制作的。我相信您可以从Metrowerks下载试用版。此外,您可以从Palm下载Palm模拟器,在那里也能找到大部分开发信息。

如果您想要完全免费的开发工具,Palm也有一个GNU编译器。演示应用程序不是用GNU编译器设计的,所以我不知道它是否能工作。

当您编写生产级的Palm应用程序时,您需要注册一个唯一的4位数字ID,该ID将区分您的应用程序资源和其他任何资源(有点像GUID,但没那么复杂)。

Palm应用程序术语

所有操作系统都有自己的术语,PalmOS也不例外。以下是一些在PalmOS编程中更相关的术语列表:1

Window

一个矩形区域,应用程序在此区域中绘制对话框、表单和菜单等。
表单
一个覆盖整个屏幕的应用程序窗口。一个表单还可以包含控件、文本区域和菜单。一次只允许一个活动表单。
数据库
一组持久内存块。有资源数据库和记录数据库。
Resource
存储在资源数据库中的数据块。由资源类型和编号标识。
Record
由唯一记录ID标识的数据结构。应用程序数据通常存储在记录数据库中。
启动代码
传递给应用程序的参数,指定应用程序执行时应执行的操作。
警告
一个需要用户确认的警告或信息对话框。

与Microsoft Windows一样,PalmOS是一个事件驱动的操作系统。一次只能打开一个应用程序,所有应用程序都包含一个事件循环,该循环检索事件并对其做出反应。

Palm应用程序结构

每个Palm应用程序的入口点是PilotMain函数。
UInt32 PilotMain(UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) 2

第一个参数cmd是应用程序的启动代码。有几个可用的启动代码,但要正常启动应用程序,请使用常量sysAppLaunchCmdNormalLaunch

第二个参数cmdPBP是指向包含任何启动命令特定参数的结构的指针,如果没有启动代码,则为NULL。MemPtr本质上是一个void *。

最后一个参数launchFlags指示应用程序的全局变量是否可用,您的应用程序是否是当前活动应用程序,它是否已经是活动应用程序,等等。

/* Here is an example PilotMain */
UInt32 PilotMain( UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags)
{
	switch (cmd)
	{
		case sysAppLaunchCmdNormalLaunch:
			AppEventLoop();
			FrmCloseAllForms();
			break;
		
		default:
			break;
	}
	
	return 0;
}

PiltoMain函数调用主事件循环函数。在此函数中,我们不断地处理我们的应用程序和系统的事件(请参阅图1以获取说明)。我们首先使用EvtGetEvent函数获取事件,然后将该事件分派给4个事件处理程序。如果事件处理程序返回true,则事件已被处理,将不再进一步处理。如果未处理,它将被发送给每个后续的处理器进行处理。循环继续,直到收到appStopEvent
/* Here is an example main event loop */
static void AppEventLoop(void)
{
	UInt16 error;
	EventType event;

	do 
	{
		EvtGetEvent(&event, evtWaitForever);

		if (! SysHandleEvent(&event))
			if (! MenuHandleEvent(0, &event, &error))
				if (! ApplicationHandleEvent(&event))
					FrmDispatchEvent(&event);

	} while (event.eType != appStopEvent);
}
SysHandleEventMenuHandleEvent分别处理Palm系统事件和菜单事件,而ApplicationHandleEventFrmDispatchEvent是Palm应用程序中大部分实际操作的发生地。

ApplicationHandleEvent函数是您使用void FrmSetEventHandler (FormType *formP, FormEventHandlerType *handler)为所有表单设置事件处理程序的地方。表单事件处理程序例程的形式是:Boolean FormEventHandlerType (EventType *eventP)。您基本上是将一个回调函数设置为事件处理程序(就像Windows的WindowProcedure一样)。

/* Here is an example application event handler */

// a form event handler
static Boolean MainFormHandleEvent(EventPtr eventP)
{
   	Boolean handled = false;
   	FormPtr frmP = FrmGetActiveForm();
   	
	switch (eventP->eType) 
	{
		case frmOpenEvent:
			FrmDrawForm(frmP);
			handled = true;
			break;
			
		default:
			break;	
	}
	
	return handled;
}
// the app event handler
static Boolean AppHandleEvent(EventPtr eventP)
{
	UInt16 formId;
	FormPtr frmP;
	Boolean bRetVal = false;

	if (eventP->eType == frmLoadEvent)  // is a form loading?
	{
		// Load the form resource.
		formId = eventP->data.frmLoad.formID;
		frmP = FrmInitForm(formId);  // load a form resource
		FrmSetActiveForm(frmP);  // make this the active form

		// Set the event handler for the form.  The handler of the currently
		// active form is called by FrmHandleEvent each time is receives an
		// event.
		switch (formId)
		{
			case MainForm:
				FrmSetEventHandler(frmP, MainFormHandleEvent);
				break;
	
			default:
				break;
		}
		
		bRetVal = true;
	}
	
	return bRetVal;
}

FrmDispatchEvent例程提供间接的表单特定事件处理。为了提供这一点,FrmDispatchEvent调用表单的事件处理程序(上面示例中的MainFormHandleEvent)。

“Hello World!”示例

作为编写第一个Palm应用程序的示例,我将使用永不令人厌倦的“Hello World!”示例。我将打开一个带有按钮的表单,当按下该按钮时,会在屏幕中央显示“Hello World!”。

演示源代码的PilotMain和主事件循环几乎与我们在“Palm应用程序结构”部分涵盖的内容相同,唯一的例外是FrmGotoForm函数调用。void FrmGotoForm (UInt16 formId)会向当前活动表单发送一个关闭事件,并向由fromId指定的表单发送加载和打开事件。应用程序事件处理程序将为给定formId的此表单设置表单事件处理程序。所以总而言之,FrmGotoForm关闭当前表单并打开一个新表单。

当表单上的“Say Hello”按钮被按下时,一个ctlSelectEvent事件被发送到表单,然后该事件被传递给表单按钮处理程序例程MainFormButtonHandler

static Boolean MainFormButtonHandler(FormPtr formP, EventPtr eventP)
{
	Boolean handled = false;
	
	switch (eventP->data.ctlEnter.controlID)
	{
		case MainSayHelloButton:
			SayHello();
			handled = true;
			break;

		default:
			break;      
	}
	
	return handled;
}
上面的switch语句会找到被选中的控件并调用我的SayHello函数(如下所示)。
static void SayHello()
{
	short nCharWidth = 0;
	short width = 0, height = 0;
	Char* pText = (Char*) MemPtrNew(13);  // allocate 13 bytes
			
	// copy Hello World! into the char ptr
	StrCopy(pText, "Hello World!");
	
	// get the width of the string		
	nCharWidth = FntCharsWidth(pText, StrLen(pText));  
	// get the width and height of the string
	WinGetWindowExtent(&width, &height);
	
	// draw the text in the center
	WinDrawChars(pText, StrLen(pText), (width/2) - (nCharWidth/2), height/2);

	// free the text memory
	MemPtrFree(pText);
}
大多数Palm代码与标准的C/C++类似,但字符串和内存函数针对该平台进行了优化。使用标准的C/C++函数可能会增加编译后可执行文件的大小,而在Palm上,大小很重要。

首先,我们使用MemPtrNew函数为“Hello World!”字符串分配13个字节,该函数的作用与malloc类似。

然后,我们使用Palm的StrCopy函数代替标准的strcpy

FntCharsWidth使用当前字体返回字符串的像素宽度。

WinGetWindowExtent获取Palm屏幕区域的宽度和高度。您不应再假定Palm屏幕为160x160(新的Palm Tungsten T拥有320x320的屏幕区域)。

在获取屏幕大小和字符串大小后,我们最终使用void WinDrawChars (const Char *chars, Int16 len, Coord x, Coord y)将字符绘制到屏幕中央。

然后,我们使用MemPtrFree释放为字符串分配的内存。

打完招呼后,演示应用程序将在此等待您的输入,直到您使用Palm上的标准Home按钮关闭应用程序。

结论

我希望您觉得这篇文章易于理解且有趣。Palm平台肯定会继续存在,并且很有趣去开发,所以我鼓励您尝试一下。

如果您对更多文章感兴趣,请告诉我,它们可能会包含有关如何创建和使用Palm数据库以及Palm“Conduit”开发的信息。

另外,我没有在演示项目中包含PalmOS.h文件,因为它可以在Palm SDK中找到,而Palm SDK可以从PalmOS网站下载。

来源

1 Rhodes, McKeehan: "Palm Programming The Developer's Guide, First Edition", 67-69。
2 Palm Inc., System Manager {online} 可从 http://lovecraft.die.udec.cl/prog_lang/PalmOS/SystemManager.html#941792 获取,2001年。

有用的链接
Palm OS®程序员API参考

© . All rights reserved.