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






4.50/5 (5投票s)
在之前文章的基础上,我们开发一个简单的应用程序,并讨论一些可用于 Palm GUI 的组件。
在本文中,我们将开发第一个基于窗体的应用程序,并探索一些可用的 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。