GWT iPhone

Google 开发者关系团队

2007 年 7 月

本文已过时,但可能具有历史意义。

GWT 1.4 和 Apple 的 iPhone 发布已经过去了几个星期。我们花了一些时间学习如何为 iPhone 优化 GWT 应用程序。由于没有什么比真实的代码经验更有效,我们决定编写一个我们认为有用的应用程序,并展示 iPhone 的酷炫功能。结果就是 GWT Feed Reader,一个 RSS 阅读器,它使用 Google AJAX Feed API 以及为 iPhone 优化的用户界面。本文将讨论我们从编写这个 RSS 阅读器中学到的知识。

好消息是,编写针对 iPhone 的 GWT 应用程序与编写任何其他应用程序没有什么不同。另一方面,用户与移动应用程序交互的方式与他们与“桌面”应用程序交互的方式有所不同。即使您现有的桌面 GWT 应用程序可以在 iPhone 上运行,但使用起来可能不太方便,而且可能不像移动应用程序应该那样。对于不仅仅是偶尔使用的情况,您的用户会希望有一个针对其设备优化的界面。

在我们深入探讨之前,值得注意的是,打算针对 iPhone 的开发者应该参考 Apple 的 iPhone 开发指南。它涵盖了用户如何在 iPhone 上与 Web 应用程序交互,如何为 iPhone 优化您的应用程序,以及指向其他与 iPhone 相关的开发社区的链接。这些指南适用于静态内容以及使用 GWT 的客户端应用程序开发。

我们不会介绍编写 GWT 应用程序的基本知识,而是重点介绍我们为使 GWT Feed Reader 成为可用的移动应用程序而做出的设计决策。大多数设计都源于对设备限制的理解。

界面设计决策

iPhone 有三种主要的 UI 手势:点击(或指向)、滑动和双击。点击是主要命令手势,类似于鼠标点击,可以使用标准的 ClickListener.onClick() 事件进行处理。滑动,无论是在垂直方向还是水平方向,都用于在(有时更大)的虚拟页面上平移视窗。

当 UI 可以设计为垂直条带时,一个大小合适的 Panel,结合视窗元标记,可以轻松地适应,以提供“全屏”布局,从而消除水平滚动。为适应单个列中的所有内容而设计的应用程序应通过在主机页面的 部分添加 来设置视窗宽度为 320 像素。使用相对大小的 CSS 宽度规则将使用实际视窗的大小,而不是 980 像素的默认虚拟页面大小。如果您遇到不希望的水平溢出,可以使用 CSS 属性限制各种小部件的宽度。这对于限制可能不直接针对 iPhone 的来源的图像特别有用。在 GWT Feed Reader 的情况下,用户缩放被完全禁用,以通过指定视窗元标记 来降低用户界面导航的复杂性。

双击手势将放大文档,使包含目标点的元素填满屏幕。这与 GWT UI 工具包的“每个小部件一个元素”设计非常匹配。通过构建您的小部件层次结构,将相关的小部件集合分组到面板中(并且可能将相关面板嵌套在外部面板中),双击手势将允许用户以分层方式导航文档,并允许更有效地定位缩放区域。

重要的是要记住,手指在按下 iPhone 的显示屏时既不透明,也不为零大小。如果我们假设最小目标应该是大约按下的指尖大小,那么我们就有四分之一英寸的最小正方形。iPhone 的屏幕具有超过平均水平的 160 dpi 点间距,使最小有效目标大约为 40x40 像素。如果应用程序旨在用于 iPhone 以及桌面,则需要使用 CSS 媒体选择器来加载正确的样式表,如 iPhone 开发人员指南中所述。

“每个小部件一个元素”设计

文档对象模型 (DOM) 规范定义了一个分层框模型,用于组合在您查看网页时显示在屏幕上的图像。通常,Web 浏览器将从 Web 服务器接收的 HTML 数据转换为 DOM 结构,但是,也可以通过纯粹的编程方式,通过客户端 JavaScript 代码来操作 DOM。GWT UI 类提供了对它们所表示的底层 DOM 结构的抽象,允许您(开发者)考虑高级小部件和面板,而不是 DOM 元素的集合。每个小部件都与一个 DOM 元素相关联,该元素在 DOM 中表示该小部件。简单的小部件,如 Button,可以用单个 DOM 元素来表示。

更复杂的小部件,如 VerticalPanel,有一个根元素,充当子小部件元素的容器。VerticalPanel 的各个行是元素,可以被缩放手势定位。如果 VerticalPanel 中包含的小部件宽度不均,缩放手势仍然允许用户近似地放大行的元素,即使在没有所需小部件的情况下也是如此。然后用户可以更准确地定位所需的小部件,因为它将以更大的可见尺寸显示。

改进交互

实际性能和感知性能都是设计用户交互时必须考虑的关键因素。如果应用程序在使用过程中暂停、挂起或以其他方式停滞,用户会很快感到沮丧。UI 中的渐进式(或延迟)渲染以及已渲染 UI 元素的保留增加了对响应能力的感知。应用程序在响应用户事件时立即发生某些事情时,通常“感觉更快”并且不会让用户感到厌烦,即使只是简单地显示“正在加载...”消息。使用 DeferredCommand.addCommand()IncrementalCommand 允许您实现“延迟迭代器”。这将避免在您从数据对象列表中创建 UI 元素时阻塞 UI 事件循环。

final List objects = ....;
  DeferredCommand.addCommand(new IncrementalCommand() {
    Iterator i = objects.iterator();
    public boolean execute() {
      Foo foo = (Foo)i.next();
      .... do something ...
      return i.hasNext();
    }
  });
  

GWT 的 历史支持 的稳健使用增加了应用程序的可用性。浏览器的后退和前进按钮始终显示在屏幕上,因此您最好利用它们在您的应用程序中。GWT Feed Reader 中的大多数状态更改都由历史记录标记控制。代码不是让用户生成的事件直接导致面板显示或隐藏,而是简单地执行一个 调用。这确保了外部驱动的行为(后退/前进、深层链接)与 UI 驱动的行为相同,并有助于将事件处理代码与表示代码分离。例如,通过将历史记录标记设置为提要的 URL 和文章的链接目标 URL 的组合来查看提要中的文章。请参阅 processHistoryToken() 函数。

程序数据

将服务器往返次数最小化也是 GWT Feed Reader 应用的设计重点。我们使用了来自新版 GWT Incubator 项目的 ImmutableResourceBundleStyleInjector。这种组合允许程序资源(如 CSS 和背景图像文件)被“永久”缓存或直接内联到 GWT 应用中。后者允许 Feed Reader 始终能够正确渲染,即使 iPhone 暂时无法访问互联网。对模块 Resources 的编程访问也有助于开发阶段,因为编译器会警告你缺少的文件。作为额外的部署优化,模块选择脚本已作为构建后步骤内联到主机 HTML 页面中。这些优化带来的最终效果是,整个 GWT Feed Reader 应用及其所有运行时资源可以在仅两次 HTTP 往返中下载。

Feed Reader 需要从某个地方获取其 Feed 数据。我们使用 GWT JavaScript Interop 项目,通过 Java 绑定导入足够量的 Google AJAX Feed API。这通过使用轻量级 API 声明绑定,消除了手动编写 JSNI 调用底层 JavaScript API 的需要。绑定类位于 com.google.gwt.ajaxfeed 包中。

由于这最终是一个演示应用,需要能够在没有服务器支持的情况下运行(例如,从本地文件系统运行),我们使用 GWT 的 浏览器 Cookie 操作支持 来存储配置和上次阅读信息。数据以 JSON 编码字符串的形式存储在 Cookie 中,并通过 Configuration 类访问。更完整功能的应用程序可能会包含服务器端支持来配置和跟踪上次阅读的文章。

总结

在确定 UI 布局样式后,实现 RSS 阅读器应用就像编写任何其他 GWT 应用一样。大部分主要功能集是在托管模式开发中完成的,然后使用 Safari3 和 iPhone 的组合来完成应用的最终完善。大多数情况下,测试应用是通过 EDGE 网络访问的,以模拟典型用例。针对高延迟、低带宽配置的测试,使得在 WiFi 网络上使用应用体验更好。

我们期待将来看到更多 GWT 应用出现在 iPhone 上。如果您对 GWT Feed Reader 的代码感兴趣,它是在 Apache 2.0 许可下发布的开源项目,在 Google Code 上有自己的 项目页面

编码愉快!

- GWT 团队