|
前面我用了很大的篇幅介绍如何使用J2ME开发运行于移动设备上的应用程序,并且在我第一篇文章中我在谈论开发Palm应用程序可以使用的解决方案时也提到了其他几种方案。本文中我们将开始学习Waba,学习使用它编写运行于Palm上的应用程序。Waba是一种源码开放、很像Java的语言。如果你了解 Java,你就能知道Waba的语法。
第一节 用户界面控件
让我们从用户界面类开始学习Waba,因为一个PDA上很少有程序在没有用户界面的情况下可用,然后我们再谈谈Waba种的数据持久性。构建图形用户界面的大部分类都包含在 waba.ui包中。这些类的继承层次结构非常像 Java的 AWT。当然,它们全部都是扩展 java.lang.Object和 waba.ui.Control。我建议你参照 waba.ui程序包的文档来弄明白用于执行像放置一个控件、使它可见等常见任务的静态数据成员和方法。下面的类扩展了waba.ui.Control:
Button --命令按钮
Check --复选框控件
Container --另外一个控件的容器 (类似于 AWT Panel或一个 Swing Jpanel )。
Edit --一个文本输入编辑控件 (类似于 TextField )
Label --标签控件
MainWindow --指向一个基于用户界面的应用程序的主要输入端。所有的 GUI应用程序都必须有。
Radio --单选按钮。
TabBar/Tab/TabPanel -- TabBar是用于选项卡的容器。
作为这些类的祖先, Control类包括用于事件处理和绘画控制的方法。让我们看一个简单的 Waba应用程序的例子。这个 Waba应用程序能够提示使用者一些信息,在本例中,在一个联系管理器中查寻一个名字。
这个类的代码是 import waba.ui.*; public class ContactManager extends MainWindow { Edit edit; Label label; Button button; public ContactManager() { setTitle("Contact Manager"); setBorderStyle(Window.RECT_BORDER); label = new Label("Enter name to search for:"); label.setRect(5, 65, 110, 15); add(label); Edit edit = new Edit(); edit.setRect(55, 80, 60, 15); add(edit); button = new Button("Find"); button.setRect(65, 110, 30, 15); add(button); }public void onEvent(Event event) { if (event.type == ControlEvent.PRESSED) { if (event.target == button) { ....... } } }}
首先,我们导入 waba.ui包,因为所有的用户界面类都在那里。这个类是扩展 MainWindow。每个有用户界面的 Waba应用程序都必须扩展 MainWindow。接下来,我们声明需要的变量:一个 Edit、一个 Label和一个 Button。在构造程序中,我们调用 setTitle和 setBorder,然后启动每一个 GUI组件。注意,我们还可以使用控件的绝对位置。这是 Waba和 Java之间的一个区别。这里没有布局管理器的概念,我们把 x和 y坐标以及控件的宽度和高度传递进程序,这个控件就被放置在屏幕上的这个位置。
Waba的另一特色是一个模拟的 Palm键盘,如下图所示:
最后, onEvent方法进行事件处理:首先,这个事件对象本身被查询,以确定事件类型。在本例中我们想知道它是否是一个 PRESSED事件。(其他类型包含 FOCUS_IN、 FOCUS_OUT、 TIMER和 WINDOW_CLOSED )。这些都定义在 ControlEvent类中,是扩展事件。如果这个事件和我们要寻找的事件类型匹配,然后我们就可以确定激活事件的对象。在我们的 ContactManager应用程序中,我们只有要响应按钮按下的动作。所以如果用户轻点按钮激活事件,然后我们就可以执行代码来查寻目录中的姓名。
定义在 MainWindow类中的其他有用的方法是 onStart和 onExit。onStart方法可能包括初始化代码;它在构造程序完成之后被调用。应用程序退出先要调用onExit方法,这时可以执行必要的清理工作比如写入未保存的数据。
编译应用程序
一个应用程序要在 Palm OS中运行,就要把 Waba类文件编译成.prc文件,你需要调用 Waba带的两个程序: warp和 exegen。这两个文件都可以在WABA安装目录下的 bin目录下找到。warp程序创建一个 WRP文件和一个 PDB文件。WRP文件对一个 Palm设备来说没有用,但是我曾在第一篇文章中提起过, Waba也可以运行于 Windows CE, WRP文件就用于此处。Warp程序将为 PDB文件产生一个默认的 creatorID,但是可以用 /c选项自己指定一个creatorID。这个创建者表示号是一个四字节的标识符,是 Palm OS使用来把应用程序和数据联系在一起的。如果你发布的是一个商业应用程序,最好使用唯一creatorID,这样你的应用程序和数据就不会和别人开发的其它应用程序有冲突,你还可以在 PalmOS网站上注册一个 creatorID。
下列命令可以从我们的 ContactManager类中创建一个 pdb文件。 warp c /c cnTc ContactManager *.class
第一个 c告诉 warp创建 PDB文件,另一个标志是l, 用于列出PDB文件的内容。另外一个标志是 L,用于列出 PDB文件的内容。/c设置cnTc为与 PDB文件关联的 creatorID。下一个参数是 PDB文件将要调用的内容,在本例中,是 ContactManager。最后必要的类文件也应包括在 PDB文件中。我们的 ContactManager应用程序只有一个类,因此没有必要规定通配符。可是如果这个应用程序有许多类,这个语法就能添加所有的类到PDB文件中去。
为了使你的应用程序在 Palm OS上可以执行,你还需要运行Exegen程序。这将创建一个PRC文件,它把你的应用程序的切入点提供给 Palm OS,Exegen命令行参数看上去像下面列出这样: Exegen [options] exefile main-window-class warpfile [warpfile2...]
对于我们的 ContactManager应用程序,我们应运行: Exegen ContactManager ContactManager ContactManager
这条命令规定, PRC文件名将为 ContactManager,我们扩展 waba.ui.MainWindow类得来的类称作 ContactManager以及 warp文件包括我们的类被称作 ContactManager。可见,我们必须先运行warp程序然后再运行 Exegen。
这些操作都完成后,我们将得到一个PDB文件和一个PRC,你可以把它们上传到 Palm设备中,但是也可以在 Palm OS模拟器中测试。Palm OS模拟器,简称 POSE,是一个推荐工具,有助于编译-改编-运行的整个环节。在模拟器中运行应用程序比在真正的设备中运行要简单多了,所以在调试阶段一般都使用模拟器。
当然在你运行这个应用程序之前,你必需先下载 Waba虚拟机,下载 waba.prc和 waba.pdb文件。
用于规定 Exegen的其他选项列于下表: /h 指定应用程序主窗口的高度 /i 指定 PalmOS PRC图标 (例如:/i sample.bmp) /l 指定类堆栈的大小 (例如:/l 10000) /m 指定对象堆栈的大小 (例如:/m 20000) /p WindowsCE下包含 warp文件的目录的全称路径 /s 指定堆积的大小 (例如:/s 2000) /t 指定本地堆积的大小 (例如:/t 50) /w 指定应用程序主窗口的宽度
可见,有好几个选项允许你优化应用程序使用的内存。根据经验,这些选项使用的好坏,可以产生很大的差异。例如,如果没有足够大的类堆栈,键盘就不会显示,而且抛出一个异常。
我们可以把我们刚才那个 exegen命令稍稍改一改,如下: exegen /q /i icon.bmp /t 1000 /s 2000 /m 65500 /l 40000 ContactManager
这就可以允许我们为虚拟机设置一些内存分配,以使我们的类运行得更加有效率。我们需要反复的实验不同的值,以找出对于我们的应用程序最佳的配置。这取决于我们有多少类以及同时初始化了多少对象。记住,在小型设备应用程序开发中,创建的对象尽可能少,这永远是至理名言。
其它的 GUI类
我们已经讨论了 waba.ui包中的一些基本的、重要的类。你当然可以猜出复选框和单选按钮的用法,因为这些都是标准的 AWT类,而且在 Waba中它们的功能于在Java中没有什么不同之处。可是,还有两三个类你不会在用于Palm的其他 Java实现中见到。
MessageBox是一个类,它在另一个窗口内打开一个模式窗口,从这个名字中你就可以确定它的用法。你还可以创建一个上面带有许多按钮比如 Yes/No/Cancel这样的消息框,getPressedButtonIndex()方法将知道用户按下了这三个按钮中的哪一个。
Tab控件允许你添加选项卡到窗口中,增加你的屏幕的内容,例如,如果我们增强我们的联系管理器,使之成为一种销售人员用来跟踪潜在的和当前的客户的需要的一种工具,我们的应用程序看上去应当如下所示:
下面是我们修改并增强功能后的版本: import waba.ui.*; public class ContactManager extends MainWindow{ TabPanel tabpanel; Contacts contacts; ContactManager(){ setTitle("ContactManager"); } public void onStart() { String tabs[] = new String[]{"Contacts","Prospects"}; tabpanel = new TabPanel(tabs); tabpanel.setRect(0, 15, this.width, this.height); tabpanel.setGaps(2,2,2,2); contacts = new Contacts(); contacts.setRect(10,10,160,110); tabpanel.getPanel(0).add(contacts); add(tabpanel); }
public void onEvent(Event event){ if (event.type == ControlEvent.PRESSED && event.target == tabpanel) { if (tabpanel.getActiveTab() == 0);// 保存联络信息else;} } }
class Contacts extends Container{ Label lblName = new Label("Name"); Edit edtName = new Edit(); Label lblAddr = new Label("Address"); Edit edtAddr = new Edit(); Label lblEmail = new Label("email"); Edit edtEmail = new Edit(); Button button = new Button ("OK"); public Contacts() { lblName.setRect(10,10,60,12); edtName.setRect(10,22,90,12); lblEmail.setRect(10,34,60,12); edtEmail.setRect(10,46,90,12); lblAddr.setRect(10,58,60,12); edtAddr.setRect(10,70,90,12); button.setRect(10,90, 30,12); add(lblName);add(edtName); add(lblEmail); add(edtEmail); add(lblAddr); add(edtAddr); add(button); } }
注意,我们还有一个单独的类 Contacts.java的一些接口,应当放置在第一个选项卡内,设置选项卡接口是相当简单地。我们声明一个 TabPanel,并设置它的边界,初始化 Contacts对象并设置它的边界,然后把 Contacts对象添加到TabPanel中,最后,我们把 TabPanel添加到主窗口中。
另外一个差别就是事件处理代码,我们需要使用 getaActiveTab()方法确定我们要处理哪一个选项卡。在此之后,我们可以继续进行处理,添加一个 contact或 prospect。
第二节 Waba的数据持久化
在上文中,我们初浅的谈了谈 Waba的一些 GUI组件。基本所有有用的Palm OS程序都要有 GUI接口。同样,没有程序不使用数据。不论是编写商业应用程序还是游戏程序,你都需要某个存储数据的机制。
如果你打算使用 Java (或一种像 Java的语言)作为所选的语言,你就面临在 Palm OS上保证数据持久的挑战。
Palm OS是由 C应用程序接口构成,它能够提供给你许多程序设计能力,包括 DataManager和Palm数据库(pdb)文件 (在你提出反对意见之前让我先说--我知道 Palm没有文件系统,但是我仍然使用这个术语)。
Sun的 KVM版本带有用于创建 Palm上简单的接口的类,但是这些主要引用实现,不适用于商用应用程序。在这些类之中是 com.sun.kjava.Database,它提供一个程序员读/写 pdb文件的能力。可是,如果kjava Database类提供了访问 pdb应用程序接口的全部权限,它就不能移植到其他的不运行Palm OS的资源紧张的设备。Sun的意图是公开一个 PDA简表,在理论上支持Palm设备和 Windows CE设备。
这些与Waba的数据持久性有关系吗?如果你还能记得, Waba也是一种跨平台的解决方案,它的创建者不想提供一种只能用于 Palm的数据机制。象 kjava.Database类, Waba能读懂的持久数据只有字节数组。这虽然有些原始,但是 Waba提供给你一些 KVM没有的帮助。
创建一个Catalog并访问数据
Waba提供数据持久性,主要是通过 Catalog类。Catalog类扩展了 waba.io.Stream(一个基于流 I/O操作的抽象超类)。为了在Waba中创建一个Catalog,需要写下面这样的代码: Catalog c = new Catalog("MyCatalog.MYxx.DATA", Catalog.CREATE);
这个构造程序需要两个参数: catalog名和打开 Catalog的方式。(其他方式是 READ_ONLY、 WRITE_ONLY和 READ_WRITE )
Catalog名实际上是三个东西的级连而成,第一部分是适当的名称,如果你选择菜单,然后选择信息并且滚动浏览所有的你安装在设备上的应用程序,它会显现在 Palm设备上;第二部分是创建者标识号,它是一个四字节标识符,用于把 Palm OS和设备上的应用程序资源联系在一起,它必须有至少一个大写字母,并且必须是设备上唯一的,如果你打算发布的应用程序版本能被众人所至,你应该在 Palm OS站点上注册创建者标识号,这将确保没有其他人选用与你的应用程序相同的名称;第三部分是数据库类型。
一旦你打开 catalog,你就能访问它了。在你实际调用任何写方法之前,你必需通过 addRecord( int recordSize)方法添加一个记录,它将分配一个给定大小的新记录来写数据。
大部分应用程序开发人员习惯使用关系数据库,因为它能提供很多的方法来操作你的数据,但是这些方法小型设备都没有办法使用。换言之,一个 Catalog决不是一个关系数据库。
Catalog支持关键字段的概念,没有像 Catalog.findRecord("Wayne")这样返回记录 id的方法。只是连续的访问数据,为了找到一个特定的记录,你必需搜索整个记录集,寻找一个匹配项。有一个实用方法 inspectRecord(byte[] buf, int recPosition),设计来提供一种快速查看数据看是否是你想要的内容。应用程序接口文档警告说,当参数无效时使用这个方法可能不可靠。此外,你还必须避免超出 Catalog的底部。
Waba提供一些额外的帮助用于处理这样的原始持久性设备。它允许你把Catalog包装在一个 DataStream类中,这将使我们的负担减轻 (事实上,一个 DataStream可以包装任何其他面向流的类,比如 SerialPort ),见下代码: int j = 0;
Catalog c = new Catalog("MyCatalog.MYxx.DATA", Catalog.CREATE);
if (!c.isOpen())
return;
DataStream ds = new DataStream(c);
j = c.addRecord(6);
ds.writeString("Fred");
j = c.addRecord(6);
ds.writeString("Lucy");
ds.close();
c.close();
我们打开这个 Catalog,然后在确保我们已经经过有效处理之后,创建 A新的 DataStream包装 Catalog实例。这之后,我们就可以添加记录了,然后调用 DataStream的 writeString方法把字符串转换成一个 Catalog可以接受的字节数组。
DataStream类中其他用于处理多种数据类型的方法包含 writeBoolean、 writeBytes、 writeFloat、 writeInt、 writeShort和 writeStringArray等。同样,也有与之匹配的读方法。有这些工具帮忙,我们就可以看另外一个从Catalog中读数据的例子。 c = new Catalog("MyCatalog.MYxx.DATA", Catalog.READ_ONLY);
ds = new DataStream(c);
if (!c.isOpen())
{
edtEmail.setText("Not open!");
return;
}
else
{
count = c.getRecordCount();
}
for (int i = 0; i < count; i++)
{
c.setRecordPos(i);
edtNames.setText(edtNames.getText() + " " +
ds.readString());
}
ds.close();
c.close();
我们打开 Catalog,现在使用 READ_ONLY方式,并把它包装在一个 DataStream中。在确定它被打开之后,我们使用 getRecordCount()方法获取记录数。当我们遍历这个记录集合时,获取每个姓名,再放入 Edit控件。注意我们使用 setRecordPos(record)方法定位每个记录的指针。这是遍历 Catalog的必要的步骤。然后,我们使用 DataStream,通过调用 readString()方法来获取数据。.
这说明了Waba中Catalog的基本的功能,不过,这些例子你在现实世界中编程时都不会见到,因为它们处理的都是非常简单的数据,前半篇文章中的联络管理器需要存储名、姓、地址电话、传真、电子邮件以及其他好几个字段。既然Catalog只能理解字节数组,那么我们如何确定一个字段在什么地方结束又在什么地方开始呢?可以使用 DataStream来示着一些变得更简单,如果你调用这个方法把数据写进 Catalog,那么把它取出也同样容易。例如,如果你象下面这样写数据: ds.writeString(edtFName.getText());
ds.writeString(edtLName.getText());
ds.writeString(edtPhone.getText());
ds.writeString(edtFax.getText());
那么就要像下面这样把数据从Catalog取出 edtFName.setText(ds.readString());
edtLName.setText(ds.readString());
edtPhone.setText(ds.readString());
edtFax.setText(ds.readString());
只要保持适当的顺序,数据将通过 DataStream类适当地分隔。在什么位置分隔数据完全由你决定,也可以通过相对位置或使用某些字符分隔,这是一个胜过 kjava.Database类的地方。
|