Toob - 伦敦地铁出行应用





5.00/5 (13投票s)
Toob 是一款 Windows 8 伦敦地铁出行信息应用。
引言
Toob 是 AppInnovation 竞赛的一项 HTML5 入围作品,遗憾的是,它是一个闭源项目,因此我不会提供完整的源代码。 然而,在介绍这款应用的同时,我将解释开发 HTML5 Windows 应用的一些要素,并提供相关链接,以帮助任何开发此类应用的人员获得一个不错的开端。
应用概述
Toob 是一款 Windows 应用商店应用,提供最新的伦敦地铁出行信息。 在伦敦,乘坐地铁是大多数伦敦人生活的重要组成部分,提前了解其运行状况可以切实影响他们一天的计划。 伦敦交通局确实会发布这些信息,但 Toob 使这些信息触手可及,随时可用,并可与其他应用程序配合使用,提供我们都期望的现代设备上美观且集成度高的用户体验。
在当前状态下,该应用仅包含几个屏幕。 主屏幕是一个集线器,显示颜色编码的伦敦地铁线路及其当前状态。 线路根据伦敦交通局的指南排序,按问题状态严重性递减的顺序排列。 排序和醒目、清晰的图标相结合,使用户能够非常快速地查看网络状态和/或他们感兴趣的任何特定线路的状态。 车站问题和交互式地图等进一步信息将在稍后添加。
然后可以选择单个线路以查看更多详细信息。 在这里,将呈现关于线路状态的进一步信息,以及关于线路上存在问题的任何单个车站的信息以及未来周末计划停运的详细信息。 对于那些想消磨一点时间的人来说,还提供了一条线路的简要历史。
该应用支持各种 Windows 8 视图状态,例如下面显示的快照视图,并包含各种其他 Windows 8 功能,例如动态磁贴和搜索。 有关其中一些功能的更多详细信息将在下文各自的部分中提供。
背景信息
状态
在 Windows 8 中,系统负责管理哪些应用程序运行,而不是用户。 也就是说,当前的应用程序在前台运行,但所有其他应用程序都会被系统挂起或暂停。 如果机器资源变得有些紧张,系统最终可能会终止应用程序。 应用程序不是按先进先出顺序终止的,而是系统根据其资源使用情况确定最适合终止的应用程序。 当应用程序被挂起时,它会收到系统的通知,并有几秒钟的时间来保存其状态。 然而,在终止时应用程序不会收到通知,因此从开发者的角度来看,挂起应被视为终止。 应用程序被挂起而不是像以前版本的 Windows 中那样在后台运行的原因是为了确保系统资源占用更少,从而提高现代移动设备的电池续航能力。 它们最初被挂起而不是直接终止的原因是为了允许用户在应用程序之间即时切换,因为应用程序不需要每次都进行初始化。
下图表示 Windows 8 中发生的模型和转换。 对于开发人员来说,考虑应用程序在所有可能转换中的适当行为非常重要,特别是不同类型的激活。 ActivationKind
枚举了应用程序可以启动的不同方式。
开发工具
毫不奇怪,对于开发 Windows 应用商店应用程序而言,Visual Studio 2012 for Windows 8 目前是最好/唯一的 IDE 选择。 它包含了所有必要的库以及各种有用的功能和模板,这些功能和模板专门围绕 Windows 应用商店的 UX 和技术指南进行了设计,使开发过程更加轻松。 对于 HTML5 开发,所有感兴趣的预定义代码都位于 WinJS
命名空间下,从 UI 控件 到 动画 和 实用 库。 在调试和测试方面,Visual Studio 2012 支持在本地计算机、远程计算机或可配置模拟器上运行应用程序。 特别是模拟器,对于我们这些仍然使用笨重旧台式机的用户来说,对于测试触摸交互非常有用。 最后,对于那些喜欢以可视化方式编辑 HTML/CSS 的用户,Visual Studio 2012 附带了 Microsoft 的 Blend 工具。
有关开发 Windows 应用商店应用程序的更多一般性背景信息,请在此处找到:这里。
页数
在开发 HTML5 Windows 应用商店应用程序时,建议使用单页导航模型,而不是 Web 上更常用的多页导航模型。 也就是说,不是导航到另一个页面来显示另一个屏幕,而是一个页面根据需要动态加载和卸载屏幕。 在高级别上,这是为了提供更像应用程序的体验,在加载下一个页面时屏幕不会变为空白。 在更技术层面上,这是因为脚本上下文会在每次导航步骤时销毁和初始化,从而导致性能下降,并可能出现应用程序无法接收系统事件的窗口。 它还确保应用程序生命周期的管理自然地包含在单个位置。
要实现单页导航模型,可以使用 iframe(用于外部 Web 内容)、现有的第三方框架(您已经熟悉并喜爱)或自己实现解决方案(疯狂!)。 然而,WinJS 包含 PageControlNavigator
控件和 Page
类,以创建专门适用于 Windows 应用商店应用程序的单页导航模型。 Visual Studio 包含许多项目模板,它们生成必要的存根代码,让您可以快速启动并运行此框架。
此方法的要点是,您在顶层单页中添加一个 PageControlNavigator
,如下所示:
<!DOCTYPE html>
<html>
...
<body>
<div data-win-control="Application.PageControlNavigator"
data-win-options="{ home: '/pages/overview/overview.html' }"
>
</div>
</body>
</html>
具有 data-win-control
属性的元素是 WinJS 控件的声明方式。 在应用程序激活代码中调用 WinJS.UI.processAll()
可确保定义的 WinJS 控件在启动时添加到指定元素。 data-win-options
属性指定了将在注入的 WinJS 控件上设置的属性。 因此,在本例中,我们创建了一个 PageControlNavigator
实例,其中主页的 URL 已设置为 /pages/overview/overview.html
。 PageControlNavigator
还需要一些额外的 JavaScript 来添加各种交互行为。 Visual Studio 在使用其 Windows 应用商店模板时会自动为您生成这些代码,并且我会推荐使用这种方法来添加此功能。
单个应用程序屏幕由一个用于视图标记的 HTML 文件、一个用于逻辑的 JavaScript 文件和一个用于样式的 CSS 文件组成,统称为页面。 再次,Visual Studio 会在您添加页面控件项时生成预期的文件存根模板。 HTML 和 CSS 文件是完全标准的,但 JavaScript 文件更值得关注。
// declare the page as a JavaScript object mapped to the URL of its corresponding HTML file
WinJS.UI.Pages.define("/pages/page/page.html", {
ready: function (element, options) {
// called when the user navigates to the page
// element is the root element of the page
// options is the optional arguments object passed when navigating to the page
},
updateLayout: function (element, viewState, lastViewState) {
// called when the view state changes, e.g. from full screen to snapped view, allowing
// you to update the layout appropriately
// element is the root element of the page
// viewState is the type of the new view state
// lastViewState is the type of the previous view state
},
unload: function() {
// called when navigating away from the page
}
});
在开发单个页面时,许多 WinJS 控件都非常有用。 例如,ListView
控件创建了许多 Windows 应用商店应用程序(包括 Toob)中使用的集线器行为和布局,而 Visual Studio 2012 包含专门针对其使用的模板。
有关 WinJS 控件的更多详细信息,请在此处查看:这里。
数据获取
Toob 和大多数其他消费类应用程序在启动时需要做的第一件事就是获取新内容。 在 HTML5 Windows 8 应用程序中,这可以像在大多数 HTML5 应用程序或网站中一样完成:通过异步 HTTP 请求返回 JSON。 WinJS 提供了 xhr
函数来完成这项工作。 它接受一个对象,其中包含 HTTP 请求的常用必需和可选参数,例如 url
、type
和 headers
。 它还返回一个 Promise
实例,这是 Windows 8 的异步编程机制。 Promise
上最常用的两个函数是 then
和 done
,它们非常相似但又略有不同。 两者都接受三个处理函数作为参数:第一个用于响应 promise 的成功完成,第二个用于响应 promise 因错误而满足时,最后一个用于响应进度报告。 then
和 done
之间的区别在于,前者返回另一个 Promise
,而后者不返回任何内容。 这意味着可以链式调用 then
来根据需要添加行为。 值得注意的是,如果处理函数执行期间发生错误,则使用 done
时会抛出错误,而使用 then
时,则会导致返回的 Promise
被标记为错误状态。
有关 JavaScript 中 Windows 应用商店应用程序异步编程的更多详细信息,请在此处查看:这里。
以下是一个 xhr
调用及其异步结果处理的示例:
WinJS.xhr({ url: "http://www.example.com" }).then(
function onSuccess(request) {
// handle success response
var data = JSON.parse(request.responseText);
},
function onError(request) {
// handle error response
},
function onProgress(request) {
// report on request progress
}
);
在启动时加载数据时要考虑的一个问题是,Windows 只允许应用程序在显示启动屏幕时有几秒钟的启动时间。 因此,此时进行 HTTP 请求通常是个坏主意,因为对其完成速度的控制有限。 因此,建议的做法是在启动屏幕删除后延迟数据加载。 为了用户体验,值得使加载数据时显示的屏幕与前一个启动屏幕相同(这样用户就不会看到任何过渡),并添加加载动画。 您可以在标准的 Windows 应用商店应用程序(如天气)从头开始加载时看到完全相同的行为。
数据绑定
一旦数据加载完毕,就需要将其显示在用户界面中。 这可以通过标准的 JavaScript DOM 操作或 WinJS 中提供的更动态的绑定实用程序来完成。 大多数 WinJS 控件(如 ListView
)都支持开箱即用的绑定,允许您声明 HTML 模板来渲染 ListView
数据源中的单个项。 例如:
<!-- the list view -->
<div class="list win-selectionstylefilled"
data-win-control="WinJS.UI.ListView"
data-win-options="{ itemTemplate: select('itemTemplate;) }"
>
</div>
<!-- the template for rendering each item -->
<div class="itemTemplate" data-win-control="WinJS.Binding.Template">
<div class="item" data-win-bind="textContent: name"></div>
</div>
如前所述,WinJS 控件是通过创建具有 data-win-control
属性(标识控件类型)和 data-win-options
属性(声明将在控件上设置的属性)的元素来声明的。 在本例中,我们有两个控件:ListView
和 Template
,模板设置为列表的 itemTemplate
。 请注意,模板不会显示在页面上,因此您可以将其放在页面 HTML 正文的任何位置。 模板的有趣之处在于 data-win-bind
属性。 它包含一个分号分隔的名称/值对列表,其中名称是元素上的属性名称,值是要将模板应用于的数据项上的属性名称。 这创建了一个绑定,因此使用该模板渲染的任何项的指定值将自动设置为元素上指定属性的值。
JavaScript 端只需要做的是用数据填充 ListView
。 这是通过将 ListView
的数据源设置为新 WinJS.Binding.List
实例的 dataSource
属性来完成的(该实例通常包装数据对象的数组)。 例如,我们可以有以下代码与上面的 HTML 配合使用:
WinJS.UI.Pages.define("example.html", {
// set up the list view any time someone navigates to this page
ready: function (element, options) {
// the data items
var data = [ { name: "Bakerloo" },
{ name: "Central" },
{ name: "Circle" },
{ name: "District" },
{ name: "DLR" },
{ name: "Hammersmith & City" },
{ name: "Jubilee" },
{ name: "Metropolitan" },
{ name: "Northern" },
{ name: "Overground" },
{ name: "Piccadilly" },
{ name: "Victoria" },
{ name: "Waterloo & City" } ];
// the bindable list of data items
var list = new WinJS.Binding.List(data);
// get the list view control
var listView = element.querySelector(".list").winControl;
// set the bindable list on the list view
listView.itemDataSource = list.dataSource;
}
});
结果将如下所示(取决于您在 CSS 中如何设置样式):
这只是可用于数据绑定的基本构建块之一,但它构成了 Toob 概览屏幕如何实现的基础。 深入探讨绑定超出了本文的范围。
有关数据绑定的更多详细信息,请在此处查看:这里。
搜索
Windows 8 的搜索魅力(Search Charm)是一个搜索工具,与 OS X 的 Spotlight 功能非常相似。 它是一个基于选择的搜索系统,使用户不仅可以搜索应用程序或文件,还可以搜索应用程序内容。 这意味着应用程序可以挂钩搜索,在系统触发时提供最多 5 个自动完成的提示或直接的结果建议。 对于用户来说,这也是一种快速简便的方法,可以在不一定执行搜索的情况下启动应用程序或检查信息。
有关向应用程序添加搜索的进一步阅读,请在此处找到:这里。
在 Toob 的情况下,用户可能希望直接转到特定线路的状态页面,或者只是快速浏览特定线路的状态。 或者,他们可能只是想启动应用程序。 搜索魅力可以轻松地满足所有这些不同的目的,而只需很少的编码,尽管潜在的用例和入口点有很多需要考虑。
首先,要挂钩搜索魅力,必须将搜索声明添加到应用程序的清单文件 package.appxmanifest
中。 这是通过在打开清单文件时 Visual Studio 提供的 GUI 中的“声明”(Declarations)选项卡完成的,只需从“可用声明”(Available Declarations)中选择“搜索”(Search)并添加它即可。
然后,可以通过响应 SearchPane
的 onsuggestionsrequested
事件来添加搜索建议,如下面的代码所示。 应用程序可以提供两种类型的建议:查询和结果。 前者是基于到目前为止输入的搜索查询的纯文本建议,后者是应用程序中可用的直接结果。 最多允许 5 个建议,由查询和结果的任何组合组成。 通过在事件的请求对象中包含的 SearchSuggestionCollection
上调用相应函数来添加建议:appendQuerySuggestion
用于查询,appendResultSuggestion
用于结果。
以下是 Toob 中的一个示例代码,显示将线路添加为结果建议:
Windows.ApplicationModel.Search.SearchPane.getForCurrentView().onsuggestionsrequested =
function (args) {
var maxNumberOfSuggestions = 5;
var query = args.queryText.toLowerCase();
var request = args.request;
for (var i = 0; i < lines.length; i++) {
if (line.name.toLowerCase().indexOf(query) === 0) {
// the name to show in the result suggestion on the search charm
var name = line.name;
// the details to show in the result suggestion on the search charm
var detail = line.status;
// a unique identifier for the suggested result, this will be passed back to
// the app if the result is selected
var tag = line.id;
// an image to show in the result suggestion on the search charm, to ensure
// users can easily differentiate between query suggestions and result suggestions
var image = Windows.Storage.Streams.RandomAccessStreamReference.createFromUri(
new Windows.Foundation.Uri("ms-appx:///images/" + name + ".png"));
// alt text for the above image
var imageAltText = line.name;
request.searchSuggestionCollection.appendResultSuggestion(name,
detail,
tag,
image,
imageAltText);
// check if we have reached the maximum allowable number of suggestions
if (request.searchSuggestionCollection.size === maxNumberOfSuggestions) {
break;
}
}
}
};
取决于用户是输入查询(通过键入并执行搜索或选择查询建议)还是选择结果建议,会触发不同的事件供应用程序响应。 对于查询,它是 onquerysubmitted
,在 Toob 中大致如下处理:
Windows.ApplicationModel.Search.SearchPane.getForCurrentView().onquerysubmitted =
function (args) {
if (!args.queryText || args.queryText == "") {
// if no query text then we assume the user is using the search to launch the app
// so we navigate to the main screen
WinJS.Navigation.navigate("/pages/overview/overview.html");
}
else {
// otherwise we navigate to the search results page, passing along the search arguments
WinJS.Navigation.navigate("/pages/searchResults/searchResults.html", args);
}
};
当选择结果建议时,会触发 onresultsuggestionchosen
事件。 在 Toob 中,这意味着应该显示所选线路的详细信息,因此处理程序大致如下:
Windows.ApplicationModel.Search.SearchPane.getForCurrentView().onresultsuggestionchosen =
function (args) {
// navigate to the line details page, with the id of the selected line as the argument
WinJS.Navigation.navigate("/pages/line/line.html", { line: args.tag });
};
请注意,使用搜索进行启动是应用程序的 ActivationKind
之一,因此您可能需要在应用程序启动代码中将其作为特殊情况进行处理。
尽管添加搜索有许多部分和注意事项,但 Visual Studio 提供了一些帮助,即在添加新项时可用的搜索合同(Search Contract)项。 这会自动相应地更新清单文件,并添加存根代码来处理适当的事件、应用程序启动和搜索结果页面模板。 这是一个有用的起点,但根据我的经验,重构生成的代码以使其更紧密地集成通常是明智的。
共享
Windows 8 的另一个强大功能是共享魅力(Share Charm)。 顾名思义,共享魅力是一种机制,用户可以通过它轻松地在应用程序之间交换信息,非常像高级的复制粘贴功能。 与剪贴板(仍然可用!)不同,它提供了一个统一的 UI,特别适合触摸操作,因此用户不会因复制和粘贴方式的无休止变化而感到困惑,开发人员只需挂入一个简单的 API。 此外,通过包含此功能,可以通过设备魅力(Devices Charm)启用“点按发送”(Tap and send)功能,允许使用近场通信(Near Field Communication)轻松地与附近的朋友共享内容。 内容可以以各种预定义的格式共享,例如纯文本、链接、HTML、文件或图像,或者作为自定义格式。 任何应用程序都可以选择共享内容和/或将自己标记为来自其他应用程序的特定格式共享内容的消费者。
有关共享和交换数据的进一步阅读,请在此处查看:这里。
在 Toob 的情况下,用户可能希望快速告知其他人任何潜在的出行问题,无论是通过社交网络、电子邮件还是短信。 与其支持众多的不同 API 以及随之而来的所有 UI/UX 难题,不如让我们让用户的其他应用程序为我们处理这些事情,而只需通过共享魅力(Share Charm)提供 Toob 中的出行信息。 状态信息以纯文本格式共享,因为这已足够满足其需求,并将使共享能够扩展到最广泛的消费选项。

共享通过 DataTransferManager
完成,通过监听其 ondatarequested
事件。 每当用户选择共享魅力时,都会触发此事件,并包含一个 DataRequest
对象,其中包含 DataPackage
实例,您可以在该实例上设置标题、描述和实际内容负载。
例如,在单个线路页面上,我们希望共享该线路的名称和状态。 该页面与共享功能相关的代码如下所示:
// an alias to make accessing the DataTransferManager easier
var dataTransferManager = Windows.ApplicationModel.DataTransfer.DataTransferManager;
WinJS.UI.Pages.define("/pages/line/line.html", {
...
_line: null,
ready: function (element, options) {
this._line = options && options.line
? LineDataManager.resolveLineReference(options.line)
: LineDataManager.lines.getAt(0);
// ...
// code setting up page ommitted
// ...
// hook up to Share Charm
dataTransferManager.getForCurrentView().ondatarequested =
this._onDataRequested.bind(this);
},
unload: function () {
// tidy up after ourselves
dataTransferManager.getForCurrentView().ondatarequested = null;
},
_onDataRequested: function (args) {
var request = args.request;
request.data.properties.title = this._line.name + " Status";
request.data.setText(this._line.name + ": " + this._line.statusDescription
+ "\n" + this._line.statusDetails);
},
...
});
进一步工作
Toob 计划的进一步工作有几个不同的方向。 首先,还有一些通用的用户体验功能需要实现,例如固定二级磁贴和设置。 还有大量相关且有用的内容需要设计并构建到应用程序中,例如车站信息和实时火车时间。 最后,应用程序将大大受益于某种形式的实时/交互式地图,并具有路线查找器功能。
历史
- 2012年10月5日 - 初始版本