图 1. 创建新 iPhone Project
图 2. 在 Eclipse 中生成的 iPhone Project
图 3. iPhone Preview 模式下的水平视图
使用 iPhone Preview 模式可以节省大量时间:可以快速测试新设计想法并重复进行开发而无需离开计算机。当应该在实际的 iPhone 上测试您的应用程序时,Aptana 的内置应用服务器将十分有用。单击 Eclipse 工具栏中的 Run 图标启动服务器。图 4 显示了在 Eclipse 中运行的应用服务器。
图 4. Aptana 的 iPhone Application Server 托管页面并创建带有 URL 的电子邮件
如果通过 WiFi 连接将 iPhone 连接到本地网络,则可以访问服务器窗口中显示的 URL。单击 E-mail this url 并向 iPhone 中的电子邮件帐户发送一条消息,这可以省去一个步骤。单击电子邮件中的链接,应用程序将在 iPhone 的 Web 浏览器中启动。
iUi 演示:剧院清单 Web 应用程序
虽然 Aptana 的启动应用程序包含特定于 iPhone 的 HTML 和 CSS 文件,但是这些文件的功能有限。一种更好的备用解决方案是使用 iUi 框架,此框架拥有一组丰富的自定义部件和 JavaScript 效果,可以精确地模拟本机 iPhone 界面。
解压缩已下载的 iUi 发行版 iui-0.13.tar,然后把文件复制到 Eclipse 的 iDoc Project 中。图 5 显示了包含 iUi 的项目。
图 5. 带有 iUi 框架和示例项目的 iDoc 项目
iUi 的演示 Web 应用程序可以在上面展开的 samples 文件夹中找到。其中包括音乐浏览器、剧院清单和类似 Digg 的站点。使用 Aptana 的 iPhone Preview 模式,我们可以在 Eclipse 中查看这些组件。图 6 显示了剧院清单 Web 应用程序 (samples/theaters/index.html) 的搜索页面。
图 6. iUi 的示例剧院清单 Web 应用程序
图 7. 手指不是鼠标
Apple 的 iPhone 人机接口指南(请参阅 参考资料)定义了三种类型的 iPhone Web 内容。
与 iPhone 中的 Safari 兼容
可以正确显示的任意类型的 Web 页面,即使页面的一部分依赖于诸如 Adobe Flash 或 Java™ applet 之类的不受支持的插件
最适于 iPhone 中的 Safari
为 iPhone 正确缩放内容并且不依赖于不受支持插件的 Web 页面
iPhone 应用程序
通过模拟 iPhone 界面外观来模拟本机应用程序的 Web 页面,并且在适当的时候,与诸如电话、电子邮件或 Google Maps 之类的 iPhone 服务集成
标准 Javadoc 页面属于第一种类型,与 iPhone 中的 Safari 兼容。这些页面可以正确显示,但是要求很好地处理轻压和轻弹才能找到相关信息。iDoc 针对的是完整的 iPhone 应用程序。虽然不能与其他服务集成,但是 iDoc 的接口能够在 iPhone 中很好地使用。
iDoc UI
针对 iPhone 进行开发时,必须只关注 iPhone。应用程序应当快速地完成特定任务,不应该尝试包括所有可能的功能。使用 iDoc,用户必须找到关于 Java 类的基本文档,例如类名、方法名、方法签名和注释。通过指向目标详细信息页面的三个导航级别显示这些信息。
包导航
顶层包
类导航
包内的类、接口、异常和错误
类细节导航
类中的描述、字段、构造函数和方法
详细信息页面
注释、签名和参数
为了使 iDoc 保持整齐并且以任务为目的,需要省略一些传统 Javadoc 功能。例如,不显示包描述注释。由于这些功能通常不包含有用信息(例如,acme.client 包含客户机代码),并且有时被同时省略,因此把它们从 iDoc 中移除并简化接口就显得十分有意义。
要获得三个导航级别,请使用边对边 (edge-to-edge) 列表。这是在本机 iPhone 应用程序中常用的构造,用于浏览联系人、电子邮件和音乐。边对边列表将在 44 像素的等高行中显示项,并且有助于在大量信息中滚动查找。Apple 的 iPhone 人机接口指南 提供了构造边对边列表的规格,包括字体、字体大小和边框间距。iUi 框架用 CSS 和 JavaScript 语言实现这些规格,允许您创建像本机 iPhone 组件那样显示的简单 HTML 列表。
清单 1 显示了页眉,以及 java.applet 和 java.rmi 包的前两个导航级别。
清单 1. 带有页眉和前两个导航级别的 HTML 文档
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>iDoc</title>
<meta name="viewport" content="width=320; initial-scale=1.0;
maximum-scale=1.0;
user-scalable=0;"/>
<style type="text/css" media="screen">
@import
"iui/iui.css";</style>
<style type="text/css" media="screen">
@import
"iDoc.css";</style>
<script type="application/x-javascript"
src="iui/iui.js"></script>
</head>
<body onclick="console.log('Hello', event.target);">
<div class="toolbar">
<h1 id="pageTitle"></h1>
<a id="backButton" class="button"
href="#"></a>
</div>
<ul id="home" title="Packages" selected="true">
<li><a href="#java.applet">java.applet</a></li>
<!-- more packages...-->
<li><a href="#java.rmi">java.rmi</a></li>
</ul>
<ul id="java.applet" title="java.applet">
<li class="group">Interfaces</li>
<li><a href="java.applet.AppletContext.html">
AppletContext</a></li>
<li><a href="java.applet.AppletStub.html">
AppletStub</a></li>
<li><a href="java.applet.AudioClip.html">
AudioClip</a></li>
<li class="group">Classes</li>
<li><a href="java.applet.Applet.html">Applet
</a></li>
<li><a href="java.applet.Applet.AccessibleApplet.html">
AccessibleApplet</a></li>
</ul>
<ul id="java.rmi" title="java.rmi">
<li class="group">Interfaces</li>
<li><a href="java.rmi.Remote.html">
Remote</a></li>
<li class="group">Classes</li>
<li><a href="java.rmi.MarshalledObject.html">
MarshalledObject</a></li>
<li><a href="java.rmi.Naming.html">
Naming</a></li>
<li><a href="java.rmi.RMISecurityManager.html">
RMISecurityManager</a></li>
<li class="group">Exceptions</li>
<li><a href="java.rmi.AccessException.html">
AccessException</a></li>
<li><a href="java.rmi.AlreadyBoundException.html">
AlreadyBoundException</a></li>
<li><a href="java.rmi.ConnectException.html">
ConnectException</a></li>
<li><a href="java.rmi.ConnectIOException.html">
ConnectIOException</a></li>
<li><a href="java.rmi.MarshalException.html">
MarshalException</a></li>
<li><a href="java.rmi.NoSuchObjectException.html">
NoSuchObjectException</a></li>
<li><a href="java.rmi.NotBoundException.html">
NotBoundException</a></li>
<li><a href="java.rmi.RemoteException.html">
RemoteException</a></li>
<li><a href="java.rmi.RMISecurityException.html">
RMISecurityException</a></li>
<li><a href="java.rmi.ServerError.html">
ServerError</a></li>
<li><a href="java.rmi.ServerException.html">
ServerException</a></li>
<li><a href="java.rmi.ServerRuntimeException.html">
ServerRuntimeException</a></li>
<li><a href="java.rmi.StubNotFoundException.html">
StubNotFoundException</a></li>
<li><a href="java.rmi.UnexpectedException.html">
UnexpectedException</a></li>
<li><a href="java.rmi.UnknownHostException.html">
UnknownHostException</a></li>
<li><a href="java.rmi.UnmarshalException.html">
UnmarshalException</a></li>
</ul>
图 8 显示了使用边对边列表生成的顶层选择包导航级别。
图 8. 像导航本机 iPhone 应用程序一样导航 Javadoc 包
图 9. 在 java.rmi 包中导航接口、类和异常
对于 iDoc 的目标详细信息页面,使用另一种 iPhone 构造:圆角矩形列表。这些列表对于分组信息十分有用,并且 iPhone 中的设置面板都使用这种列表。使用圆角矩形列表可以分隔方法签名与参数及异常清单。在 V0.13 中,iUi 只支持将圆角矩形列表用于表单输入;将其元素用于静态文本将生成格式错误的块。 使用定制的 iDoc.css 文件扩展这些 CSS(如清单 2 所示),以添加用于显示带有静态文本的圆角矩形列表的 textRow 元素。
清单 2. 定制 textRow CSS 扩展以正确显示静态文本
/* Adding a new row CSS style to iUi for displaying blocks of text */
.textRow {
position: relative;
border-bottom: 1px solid #999999;
-webkit-border-radius: 0;
text-align: right;
}
.textRow > p {
text-align: left;
margin: 5px 8px 5px 10px;
padding: 0px 0px 0px 0px;
}
fieldset > .textRow:last-child {
border-bottom: none !important;
}
清单 3 显示了 java.math.BigDecimal 的一种构造方法的详细信息页面。
清单 3. 使用 textRow 元素的 HTML 详细信息页面
<div id="java.math.BigDecimal(long,java.math.MathContext)" title="BigDecimal"
class="panel">
<fieldset>
<div class="textRow"><p><b>
public BigDecimal(long, MathContext)</b></p></div>
<div class="textRow"><p>Translates a
<code>long</code> into a
<code>BigDecimal</code>, with rounding according to the context settings.
The scale of the <code>BigDecimal</code>, before any rounding, is zero.
</p></div>
</fieldset>
<h2>Parameters</h2>
<fieldset>
<div class="textRow"><p><b>long val
</b>: <code>long</code> value to be converted
to <code>BigDecimal</code>.</p></div>
<div class="textRow"><p><b>MathContext mc
</b>: the context to use.</p></div>
</fieldset>
<h2>Throws</h2>
<fieldset>
<div class="textRow"><p><b>ArithmeticException
</b>: if the result is inexact but
the rounding mode is <code>UNNECESSARY</code>.</p></div>
</fieldset>
</div>
<fieldset> 标记内的所有内容都位于圆角矩形内,textRow <div> 用于分隔行。带有 <h2> 标记的标题显示为列表上方的组标签。图 10 显示了最终页面。
图 10. java.math.BigDecimal 中的构造函数的详细视图
拥有三个导航级别和目标详细信息页面后,UI 就完成了。iDoc 使用户可以专注于具体任务。借助 iUi 框架和一些定制的 CSS,它看上去很像本机 iPhone 应用程序。
开发 iDoc
现在已经设计了 UI,接下来需要编写代码来生成 HTML 文件。创建一个插入到 Sun 的 javadoc 命令中的简单 doclet。我们的示例将使用标准 java.* 包,但是 iDoc 可以从任何源代码生成 Javadoc。使用 OpenJDK 源代码,因为它可以公开获得并且 GNU Public License (GPL) V2 许可证允许我们生成和发布其 Javadoc。
使用 iDoc,只需迭代包和类并调用方法打印上述格式的静态 HTML 页面。清单 4 显示打印目标详细信息页面的方法。
清单 4. 输出详细信息页面的 Doclet 代码
private void printDetail(PrintStream p, ProgramElementDoc doc,
String id, String name) {
divHeader(p, id, name, "panel");
textHeader(p, null);
textRow(p, getSignature(doc));
textRow(p, getCommentText(doc.commentText()));
textFooter(p);
if (doc instanceof ExecutableMemberDoc) {
printMethodDetail(p, (ExecutableMemberDoc) doc);
}
divFooter(p);
}
private void printMethodDetail(PrintStream p, ExecutableMemberDoc field) {
if (field.parameters().length > 0) {
textHeader(p, "Parameters");
for (int i=0; i<field.paramTags().length; i++) {
textRow(p, "<b>" + field.parameters().typeName() + " "
+ field.paramTags().parameterName()
+ "</b>: "
+ getCommentText(field.paramTags().parameterComment()));
}
textFooter(p);
}
if (field.throwsTags().length > 0) {
textHeader(p, "Throws");
for (int i=0; i<field.throwsTags().length; i++) {
textRow(p, "<b>" + field.throwsTags().exceptionName()
+ "</b>: "
+ getCommentText(field.throwsTags().exceptionComment()));
}
textFooter(p);
}
}
代码是通用的,这样 printDetail() 将处理类描述、字段、构造函数和方法的输出。后两种类型是 ExecutableMemberDoc 的子类,因此打印关于其参数和已抛出异常的附加信息。
Aptana 的 iPhone Preview 模式将帮助调试输出文件。在每次迭代后,您可以快速单击该应用程序查找已设计界面中的不一致性。但是,使用 Preview 模式可以掩盖性能问题:现代计算机的运行速度比 iPhone 的 620-MHz ARM 处理器快三至五倍。此外,用户经常通过速度慢的蜂窝网络下载页面,因此必须用实际 iPhone 运行您的应用程序。
性能问题
当我在 iPhone 中测试查看 iDoc 时,我发现输出一个大型 HTML 文件将导致跳帧和性能下降。要解决此问题,创建一个用于导航包名/类名的主文件,然后为带有注释和方法细节的每个类创建独立文件(参见清单 5)。虽然此过程将生成大量文件,但是各个文件大小都非常小,并且应用程序运行得十分流畅。
清单 5. Doclet 代码将迭代每个包,然后为每个类创建文件
out = new FileOutputStream(index);
p = new PrintStream(out);
printHeader(p);
PackageDoc[] packages = root.specifiedPackages();
Arrays.sort(packages);
printPackages(p, packages);
for (int i=0; i<packages.length; i++) {
printPackageDetail(p, packages);
}
for (int i=0; i<packages.length; i++) {
ClassDoc[] classes = packages.allClasses();
Arrays.sort(classes);
for (int j=0; j<classes.length; j++) {
// Creating a separate file for each class.
PrintStream p2 = new PrintStream(new FileOutputStream(getFilename(classes[j])));
printClassDetail(p2, classes[j]);
p2.close();
}
}
printFooter(p);
p.close();
运行 iDoc
在经过该性能增强之后,就能运行 iDoc 了。为 OpenJDK 中的 51 个 java.* 和 javax.* 包(表示 1,304 个类)生成 Javadoc,然后把所有内容上传到 Web 服务器中。这包括超过 16 MB 的文件,但是主要导航页面只有 112 KB,并且每个单独类的详细信息页面平均为 13 KB。即使在使用 EDGE 网络时,应用程序也能够快速响应。如果您已经拥有了 iPhone,请访问 iDoc 站点(请参阅 参考资料)并尝试使用,也可以下载 iDoc 来为您自己的代码库生成特定于 iPhone 的 Javadoc。
图 11 显示了最终的应用程序。
图 11. 用于 iPhone 的全部 51 个包的 Javadoc
可能对 iDoc 进行的扩展包括支持显示 Java 5 泛型,以及更智能地捕捉 Javadoc 注释内的标记来实现页面之间的链接。如果您有兴趣添加 iDoc 的功能,则可以下载完整的源代码.
iPhone 开发的前景
2007 年 10 月,Steve Jobs 宣称 Apple 将于 2008 年 2 月发布 iPhone SDK。详细信息很少,因为这是在 2007 年 12 月编写的,但是 SDK 将允许您在不需要使用 Safari 的情况下创建能够在 iPhone 上本机运行的应用程序。给定 iPhone 底层架构之后,开发平台很可能是类似于 Mac OS X 的 Cocoa 和 Objective-C。Apple 主管人员的最新评论暗示第三方应用程序将需要接受某种类型的认证过程。
通过在本机运行,需要高级动画、图形和网络访问的应用程序将从中获益。不过,即使在发布了 SDK 之后,iPhone 的 Web 开发仍会是一个吸引人的主题。Web 应用程序很容易创建和部署。诸如 Aptana 和 iUi 之类的工具简化了开发,并且能够快速创建 Web 应用程序。正如 iDoc 所展示的,没有必要等待 SDK 的发布:使用现有的工具,您可以创建具有本机界面外观的全功能 iPhone Web 应用程序。