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

您的第一个 Palm 应用程序 - 涵盖 GUI 组件、警报和窗体。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (5投票s)

2002 年 11 月 5 日

CPOL

7分钟阅读

viewsIcon

139244

downloadIcon

281

在之前文章的基础上,我们开发一个简单的应用程序,并讨论一些可用于 Palm GUI 的组件。

Sample Image - Palm.jpg

欢迎阅读我这篇关于 Palm 开发的第二篇文章。在此期间,我看到 Jason Henderson 也提交了一篇关于 Palm 的文章,我当然也是在他和我的文章基础上进行构建的。特别要指出的是,我之前的文章没有涵盖 Palm 事件循环的细节,所以这次我将假定您已经通过阅读 Jason 的优秀文章而熟悉了它们。

在本文中,我们将开发第一个基于窗体的应用程序,并探索一些可用的 GUI 组件。更复杂的组件,如表格和列表,将在日后单独介绍。

窗体和警报

如前所述:Palm 将我们所说的窗口(window)视为窗体(form)。Windows 世界中所说的消息框(MessageBox)则被称为警报(alert)。处理这些东西的库是窗体库,因此其中的函数都以 Frm 为前缀。当使用编译器提供的帮助索引时,这实际上非常有帮助。在这个例子中,为了最小化所需代码量,我们稍微“作弊”了一下,但我会在这样做的地方明确指出。

本文提供的项目是用于 Falch.net developer studio IDE 的,因为那是我使用的工具。它使用了 PilRC 资源编译器和 GCC。因此,如果您直接使用这些工具,应该不会有什么问题。如果您使用的是 CodeWarrior,我不确定它是否能处理基于文本的资源。如果不能,恐怕您将不得不在您的编译器中创建它们。

PilotMain

传统上,一个 Palm 应用程序有特定的操作点。一般来说,PilotMain 的部分代码看起来会是这样。

Err error;
switch (cmd) 
{ 
	case sysAppLaunchCmdNormalLaunch: 
		// Application start 
		error = StartApplication(); 
		if (error) 
			return error; 
		// Maintain event loop 
		EventLoop(); 
		// Stop application 
		StopApplication(); 
		break; 
}

Err 类型是经常见到的一种,因为它被许多函数返回,并且 API 也为预定义的返回值提供了常量。当一个应用程序收到正常的启动命令时,通常会在一个名为 StartApplication 的函数中进行初始化,主事件循环是一个名为 EventLoop 的函数,然后在名为 StopApplication 的函数中进行清理。StartApplication 函数只是调用 FrmGotoForm 来初始化我们的主窗体,而 EventLoop 则是样板代码,看起来像这样:

static void EventLoop(void) 
{ 
	Err error; 
	EventType event; 
	// Main event loop 
	do 
	{ // Get next event 
		EvtGetEvent(&event, evtWaitForever); 
		// Handle event 
		if (!SysHandleEvent(&event)) 
		{ 
			if (!MenuHandleEvent(0, &event,	&error)) 
			{ 
				if (!ApplicationHandleEvent(&event)) 
					FrmDispatchEvent(&event); 
			} 
		} 
	} while (event.eType != appStopEvent); 
}

这是应用程序的核心消息泵,它依次给予系统、我们的菜单以及最后我们的应用程序处理事件的机会。如果所有这些函数都返回 false,那么该事件将被发送到我们为该特定窗体指定的事件处理程序。Palm 是一个严格的单线程环境,因此如果任何其他应用程序启动,appStopEvent 事件就会被发送,我们的事件循环终止,程序也随之结束。

控件

在我们的 IDE 中创建了一个窗体之后,我们需要填充它。Palm 有许多您会熟悉的 UI 控件,也有一些与 Windows 环境提供的不太相似。

Labels 标签(label)基本上是一个静态文本控件。我们当然可以通过编程方式设置它的值,但它不向最终用户开放编辑功能。
字段 字段(field)是一个编辑控件,用户可以在其中输入文本。它可以是多行的,也可以是单行的,可以带下划线,也可以被限制为只接受数字。演示应用程序中的字段是单行带下划线的,以便您能看到它的位置。
Slider Palm 提供了一个水平滑块(slider),用于在一定范围内设置值。
滚动条 尽管一些输入控件会自动提供滚动条,但我们也可以设置一个滚动条并手动响应它。
复选框 就像在 Windows 中一样,复选框(checkbox)控件包含用于解释其用途的文本,以及一个通过点击来切换勾选标记的方框。
Button 一个 GUI 提供可按下的按钮(button)以触发所需操作,这几乎是理所当然的。
选择触发器 (SelectorTrigger) 选择触发器(selectorTrigger)的工作方式有点像按钮。按钮和选择触发器都可以显示图像而不是文本。
下压按钮 (PushButton) 下压按钮(pushbutton)是一种按下后会保持下压状态的按钮。它们通常按顺序呈现,以便用户可以直观地从选项列表中进行选择。
弹出触发器 (PopupTrigger) 有点像组合框(combobox),弹出触发器(pop-up trigger)有文本和一个箭头。按下箭头会弹出一个选项列表,选择一个选项会改变触发器上的文本。
Graffiti 状态指示器 (GraffitiStateIndicator) 当使用 graffiti 系统输入文本时,有一些笔划不是输入文本,而是通知 Palm 接下来文本的性质。这个组件只是提供有关 graffiti 系统状态的图形信息。

这些是本例中将出现在我们窗体上的控件,但这并不是一个详尽的列表。特别是,列表和表格将在未来的文章中介绍。

响应 GUI 事件

我们的主事件处理程序 ApplicationHandleEvent 处理许多事情,包括为我们的窗体设置事件处理函数。实现这一点的代码如下:

// Application event loop 
switch (event->eType)
{ 
	case frmLoadEvent: //Handle form load events 
		formID = event->data.frmLoad.formID; 
		form = FrmInitForm(formID); 
		FrmSetActiveForm(form); 
		
		switch (formID) 
		{ 
			case frmMain: // Set event handler for frmMain 
				FrmSetEventHandler(form, (FormEventHandlerPtr)frmMain_HandleEvent); 
				break;
			default: 
				break; 
		} 
		handled = true; 
		break; 
	default: 
		break;
}

这样一来,frmMain_HandleEvent 函数就成了处理那些被系统、菜单和这个主事件循环拒绝的事件的函数。在句柄事件函数内部,我们必须处理 frmOpenEvent 事件,代码如下:

switch (event->eType) 
{
	case frmOpenEvent: // Repaint form on open 
		form = FrmGetActiveForm(); 
		FrmDrawForm(form); 
		handled = true; 
		break;

这段代码只是确保我们的窗体被绘制,有点像 Windows 的 paint 事件。要响应控件,我们需要添加类似这样的代码:

case ctlSelectEvent: 
	switch (event->data.ctlSelect.controlID) 
	{ 
		case btnAlert: 
			FrmAlert(altHelloWorld); 
			break; 
		case trgr: 
			ShowSecondForm(); 
			break; 
	} 
	break;

在这种情况下,我将我们的按钮命名为 btnAlert,定义了一个名为 HelloWorld 的警报,所以当按钮被按下时,我们的警报就会显示出来。警报对于调试也很有用,FrmCustomAlert 函数能够显示一个带有我们传入信息的对话框。这个函数接受一个警报的 ID 和三个字符串值,然后这些值会替换警报文本中的 ^1、^2 和 ^3。注意,标题不会被解析,只有正文会。使用此函数时,对于不需要的值,必须传递空字符串而不是 null。

我们的选择触发器名为 trgr,它用于显示第二个窗体,该窗体上只有一个按钮。我们以模态方式显示这个窗体,按下任何按钮都会自动关闭它,所以不需要任何代码。为了不给这个例子增加更多代码,我们“作弊”地让这个窗体和主窗体使用同一个事件循环。ShowSecondForm 函数如下所示:

void ShowSecondForm() 
{ 
	// Create form 
	FormPtr frm = FrmInitForm(frmSecond); 
	// Cheat - use our event handler, as it won't be called upon to do anything anyhow 
	FrmSetEventHandler(frm, frmMain_HandleEvent); 
	// Show as a dialog - this returns the ID of the button pressed, 
	// just like windows, but in this case we don't care 
	FrmDoDialog(frm); 
	// Clean up 
	FrmDeleteForm(frm); 
}

最后,我们需要一些代码来设置我们的弹出菜单。实际上,我们完全在资源文件中完成这项工作,尽管也可以通过编程方式来完成。我们需要将弹出菜单与一个列表关联,并填充该列表。在 PilRC 中,它看起来是这样的:

POPUPTRIGGER "Select" ID popup AT (11 61 42 11) 
POPUPLIST ID popup popList 
LIST "Ozzy" "Sharon" "Aimee" "Kelly" "Jack" ID popList AT (11 61 44 0) NONUSABLE DISABLED FONT 0
		

我设置了一个包含奥斯本(Osbourne)家族成员名字的列表,将它放置在与触发器相同的位置,高度设为 0,这样它会自动计算高度,并使其不可见。POPUPLIST 项只是将触发器链接到列表。

结论

好了,我希望您觉得这些信息有用。我们以相当快的速度涵盖了不少内容。接下来我们将讨论数据库,以及如何将信息持久化到 Palm 中,然后我们就有能力讨论列表和表格了。一旦我们掌握了这些基本信息,我们就可以开始构建一个真正的应用程序了。我欢迎任何反馈,有些地方我讲得比较模糊,因为我不想偏向任何一个 IDE。但如果因此导致信息难以理解,我会做出修改,尽管这些修改将不可避免地偏向于我正在使用的 IDE。

© . All rights reserved.