安卓-Web-应用开发入门指南-全-
安卓 Web 应用开发入门指南(全)
零、简介
第一作者(乔恩)的父母都是艺术家。他们每个人都可以画出类似真实生活的幻想画,并且震惊地看到他们的儿子几乎不能画出简笔画。如果你一直认为你的内在艺术家是通过你在电脑和互联网的帮助下可以建立的东西来最好地表达的,那么这本书可以指导你的虚拟画笔。成品呢?适用于 Android 设备的移动 Web 应用,这反过来可以激发数百万潜在用户的创造力和生产力。我们希望这本书能给你所需要的一切,让你立刻开始工作,创作出你的杰作。
这本书是给谁的
这本书是以初学者的水平写的。在很大程度上,我们没有做任何假设,从 HTML 是什么到如何应用 CSS 来查询数据库和使用 JavaScript 显示内容。对一些人来说,这可能意味着他们想浏览某些介绍性的材料(肯定会错过许多糟糕的笑话)。然而,即使是高级用户也可能从我们展示的技巧中获得一些东西。
这本书的结构
我们把这本书的内容分成几章,有三个“非官方”的部分。
在第一部分中,我们向你介绍了网络的基本语言:HTML,CSS,JavaScript 等等。然后,我们快速进入两个应用(第二章–第三章),让你熟悉一下,然后回来讨论你在设计自己的应用时可能需要解决的规划问题(第四章–第六章)
在第二部,中,我们开始增加一些趣味。我们开始构建令人印象深刻的用户界面(第七章)和处理视觉内容(第八章)。然后,我们向您展示另外两个应用(第九章–第十章),它们讲述了移动应用的独特性质:使用位置信息来指导您的应用(和用户),以及利用云获取信息和数据。
最后,在最后一部分,我们将讨论添加到您的应用中的下一个交互级别。我们涉及添加音频和视频(第十一章),在用户背后做一些事情以提供令人印象深刻的功能(第十二章),并将其打包上传到网络或为您以前的浏览器绑定创作创建一个完整的应用(第十三章)。
虽然我们已经按照逻辑顺序对章节进行了分组,但在第一章之后,您应该可以自由探索其余的内容。虽然许多主题是建立在一个又一个的基础上的,但是首先阅读你感兴趣的内容可能会帮助你很好地理解你肯定想要检验的前几章中的概念。同时,每一章都有独立的信息,尤其是关于设计、心理学和用户体验的讨论!我们希望你旅途愉快!
下载代码
本书中所示示例的代码可从 GitHub 的[
github.com/jonwestfall/Beginning-Android-Web-Apps-Development](https://github.com/jonwestfall/Beginning-Android-Web-Apps-Development).
获得
联系作者
我们总是很高兴收到读者的来信,如果你对这本书(或生活)有任何问题、评论或想法,你可以通过我们的个人网站或社交媒体联系我们。
乔恩·韦斯特福尔:[
jonwestfall.com](http://jonwestfall.com)
推特:@jonwestfall
罗科奥古斯特:[
nerdofsteel.com/](http://nerdofsteel.com/)
推特:@therocco
格兰特·艾伦:[
www.artifexdigital.com](http://www.artifexdigital.com)
推特:@fuzzytwtr
一、利用移动网络的力量
欢迎阅读本书的第一章。在这一章中,我们不仅会尽力告诉你在这本书里你会发现什么,而且还会把它和以前的内容进行比较。你看,很简单,尽管“网络”在手机上以某种形式存在了 10 年,但直到现在,移动 Web 应用和移动优化网站的真正力量才被认识到。
在我们展示我们为这本书计划的整洁的东西之前,最好确保每个人都在同一页上,行话。所以我们要开始讨论网页设计中的基本术语。在第二部分,我们将讨论当今移动网络的前身。最后,在最后一节,我们将讨论指导本书的概念,并让您先睹为快一些我们将开发的应用!
网页设计基础
有几个概念最好先讨论一下。如果你以前听过这些,请原谅我们。然而,如果你对网页设计完全陌生(也就是说,你从未写过一个网页或博客),那么这应该是一个很好的起点。如果我们从头开始,那么我们应该从网络的通用语言开始:HTML。
入门:超文本标记语言(HTML)
20 世纪 80 年代末,我们今天所知的计算机语言 HTML 诞生了。HTML 本质上不是真正的编程语言,因为它不是编译的。更确切地说,HTML 是由一种叫做“??”网络浏览器“??”的特殊软件来解释的。桌面电脑上的微软 Internet Explorer、Mozilla Firefox 和谷歌 Chrome,Android 设备上的 Dolphin HD、Opera Mini 和 Skyfire 等浏览器从网络服务器下载 HTML 文件,解释并显示它们。整个过程相当简单。web 服务器可以是任何类型的计算机,它通过一种叫做超文本传输协议(HTTP,即网址开头的http://
,也称为 URL s)的东西向网络上的其他计算机提供文件列表。浏览器通过 HTTP 下载这些 HTML 文件并阅读它们,寻找被称为标签的特殊功能。这些标签的功能与旧的文字处理程序中的标签相同,即指定文本和页面的其他元素在查看器中显示时的外观。考虑图 1–1 中的网页。
图 1–1。【hello.html】一个名叫的网页实例
让我们来看看组成清单 1–1 中所示页面的 HTML 代码:
清单 1–1。hello.html??
`
<head> <title>This is the text that appears in the browser's Title bar!</title> </head> <body> This is normal text. However let's get fancy and make <strong>this bold</strong> (this is <em>italicized</em>). <br /> The tag to the left just made this a new line. <p> The tag to the left here just made this a new paragraph.</p> </body> </html>`代码可能看起来有点奇怪,但是让我们一行一行地看一遍。第一行简单地写着<html>,
让浏览器知道它正在读取一个 HTML 文档。您会注意到文档的最后一行</html>
也是类似的。这一行“完成”了 HTML 对象—关闭标签并告诉浏览器页面已经结束。有了这样的标签集,浏览器就知道要应用什么格式以及在哪里停止应用。
代码的第二到第四行被称为页面标题。这是程序员存储浏览器需要知道的重要信息的地方,以便正确地格式化页面。在本例中,我在标题中放置的唯一标签是一个title
标签,它指定了应该在用户 web 浏览器的标题栏中显示什么。标题是人们最常找到某些文档的地方,比如层叠样式表、JavaScript 和搜索引擎优化的元信息、不同浏览器的特殊说明、favicons(浏览器中书签条目旁边出现的小图标)以及其他与文档内容无关的页面重要信息,这就引出了第 5 行——body
标记。
标签告诉浏览器将要显示给用户的内容。从这里,我们看到的是纯文本——与图 1–1 中显示的渲染页面相同。但是,您会注意到我们添加了一些特殊的标签。第一个是<strong>
,它告诉浏览器它和它的结束标签</strong>
之间的文本应该是粗体的,以给它更强的视觉魅力。第二个标签<em>
通过强调内容或使内容倾斜来做同样的事情。 1 第三个标签<br />
,开始新的一行(br 代表换行符!).<br />
标签与大多数 HTML 标签略有不同。因为标签不要求自己像<strong>
和<em>
标签那样包含页面上的内容,所以这个标签自己关闭。最后,<p>
标签开始一个新的段落。
所有网页的核心都是某种形式的 HTML,尽管我们将在本书中讨论的大多数网页要复杂得多。值得庆幸的是,我们将带您浏览它们,这样您就不会不知所措了!
如果这是您第一次涉足 HTML 和 web 应用的世界,那么在开始阅读这本书之前,熟悉 HTML 的基础知识可能是个好主意。互联网上学习 HTML 和浏览基本代码示例的最佳资源之一可以在 W3Schools ( [
www.w3schools.com/](http://www.w3schools.com/))
)找到。一旦你对 HTML 有了一点了解,或者如果你已经从头到脚都湿透了,是时候继续学习一些我们将在本书中用到的 web 应用设计和技术的中级部分了。
变得时尚:层叠样式表(CSS)
想象一下,你正在写一个简单的网页来帮助你为人父母——一个家庭琐事清单。它可能类似于图 1–2 中的列表。
图 1–2。 家庭琐事清单
仅仅看一眼成品,这里似乎没有什么进展。我们有一个标准的无聊的黑白文件,完全缺乏任何风格或个性。让我们来看看清单 1–2 中显示的幕后代码。
清单 1–2。chores.html??
`
<head> <title> Family Chore List </title> </head> <body> <h1>Family Chore List</h1>` `<ul> <li><strong>Tommy</strong>: Take out the trash</li> <li><strong>Beth</strong>: Clean out the fridge. </li> <li><strong>Mittens</strong>: catch mice. </li> </ul> </body> </html>`1 值得注意的是,你可能习惯使用的< b >和< i >标签在 HTML 4 中的用途分别与< strong >和< em >相同。它们的使用在 HTML5 中已经被弃用,取而代之的是上面的标签。
让我们分解一下body
元素中的一小段代码。这里,页面上的无序列表是使用ul
标签创建的。当你想创建一个项目符号列表时,无序列表是很好的选择。如果你的列表需要更多的排序,你可以选择使用ol
,或者有序列表,HTML 标签。
虽然这个页面相当简单,但是你可能想增加它的趣味。也许在圣诞节前后,你想在你的家庭琐事页面上加点颜色,让最卑鄙的骗子精灵也能开心地笑一笑(见图 1–3)。
图 1–3。 圣诞杂务清单用绿色和红色增添节日气氛
也许在 7 月 4 日,你可能想让你的家人充满爱国热情(见图 1–4)。
图 1–4。 爱国杂务清单上有红、白、蓝三色
每次我们改变颜色时,我们通过添加适当的标签来修改 HTML 源代码。看看清单 1–3 中的爱国版 chores.html。
清单 1–3。 爱国的 chores.html
`
<head> <title> Family Chore List </title> </head> <body bgcolor=blue> <font color=red><h1>Family Chore List</h1></font> <font color=white> <ul> <li><strong>Tommy</strong>: Take out the trash</li> <li><strong>Beth</strong>: Clean out the fridge. </li> <li><strong>Mittens</strong>: catch mice. </li> </ul>` `</font> </body> </html>`直接对 HTML 进行修改对于小页面来说很好。然而,想象一下,如果有 12 个孩子和数不清的宠物要协调,添加那些字体标签可能要花多少时间。或者你有多个页面,每个孩子一个,如果他们的兄弟姐妹有很好的颜色组合而他们没有,你不想让他们感到被忽略了。不要担心——我们可以使用一种叫做层叠样式表(CSS)的东西来控制这一切。基本上,CSS 文件是一个小文档,由一组应用于 HTML 文档的样式组成,这些样式可以随时更改,影响它所连接的每个页面,而不必编辑原始的 HTML 文档。清单 1–4 提供了一个示例 CSS 文件。
清单 1–4。 爱国. css
body {background-color: blue} h1 {color: white} li {color: red}
请注意,文件的格式只是您希望编辑的 HTML 标记(例如 H1 和您希望赋予它的属性)。在这种情况下,我们希望h1
中的文本颜色为白色。我们可以简化 chores.html 来包含一个到这个 CSS 文件的链接,如清单 1–5 中的代码所示。
清单 1–5。chores.html同 CSS 参考
`
<head> <title> Family Chore List </title> <linkrel="stylesheet" type="text/css" href="patriotic.css" /> </head> <body> <h1>Family Chore List</h1> <ul> <li><strong>Tommy</strong>: Take out the trash</li> <li><strong>Beth</strong>: Clean out the fridge. </li> <li><strong>Mittens</strong>: catch mice. </li> </ul> </body> </html>`我们将得到与图 1–4 中所示完全相同的输出。现在,想象一下如果我们向上扩展这是如何工作的。首先,家长不再需要直接编辑 HTML 标签来改变样式。根据假期的不同,他们可以简单地链接多个 CSS 文件(简单地修改清单 1–5 中的第四行代码)。其次,他们可以进一步扩展 CSS 来指定间距、字体(Mittens 讨厌衬线)等等。最后,如果他们有不止一个页面,他们可以简单地将每个页面顶部的 CSS 表链接到他们当前的“主题”,所有的页面看起来都一样。虽然上面的例子非常简单,但是它们展示了 CSS 的强大功能。我们将在本书中更详细地研究 CSS!
互动:JavaScript
有时候伟大的设计不足以表达你的观点。有时候你想做一些华而不实的事情,或者一些独特的事情,或者一些非常有用的事情。最简单的方法之一是使用 JavaScript。JavaScript 是一种脚本语言,在浏览者的网络浏览器中运行。例如,也许你以前访问过一个网站,得到一个类似于 Figure 1–5 中的弹出消息。
图 1–5。 一个 JavaScript 警告
通常,您会在填写表单页面时看到这些消息,或者可能在网上购物车中看到这些消息,告诉您您的商品缺货或类似的恼人消息。虽然您可能习惯于在您的计算机上的网页上看到这些消息,但它们也可以在移动网络浏览器中显示(参见 Figure 1–6)。
图 1–6。 安卓手机上的 JavaScript 警告
创建这些消息的代码非常简单。清单 1–6 将代码集成到我们在上面的 CSS 例子中看到的 chores.html 页面中。
清单 1–6。chores.html用 JavaScript 引用
`
<head> <title> Family Chore List </title> <linkrel="stylesheet" type="text/css" href="patriotic.css" /> <script type="text/javascript"> function ShowWarning() { alert("Mittens - your mousing numbers are down this week - NO CATNIP FOR YOU"); } </script> </head> <body onload=ShowWarning();> <h1>Family Chore List</h1> <ul> <li><strong>Tommy</strong>: Take out the trash</li> <li><strong>Beth</strong>: Clean out the fridge. </li> <li><strong>Mittens</strong>: catch mice. </li> </ul> </body> </html>`让我们从 CSS 链接下面的新代码部分开始,在head
部分内,带有标签script
。script
标签告诉浏览器将要给出一段脚本代码(在本例中,类型为text/javascript
)。然后浏览器解释代码。因为它在head
部分,浏览器只是存储这些代码以备后用。这段代码被称为一个函数,你可以把它想象成一个用“快捷方式”包装的命令列表。这里的命令是另一个名为alert
的函数。可以想象,JavaScript 函数会变得非常复杂,函数包括其他函数并与用户输入交互。
一旦函数被加载到浏览器的菜单中,我们需要告诉浏览器我们什么时候想执行它。在这种情况下,我已经更改了body
标记,以包含行onload=ShowWarning();
。这告诉浏览器,当页面被加载时,我希望它运行函数ShowWarning
。两个括号表示我可以包含传递给函数的信息的地方。这对于创建计算器或检查表单中的输入非常有用。例如,我可以写出类似清单 1–7 的内容。
清单 1–7。chores.html用 JavaScript 引用传递一个变量
`
<head> <title> Family Chore List </title> <link rel="stylesheet" type="text/css" href="patriotic.css" /> <script type="text/javascript"> function ShowWarning(catname) { alert(catname + " - your mousing numbers are down this week - NO CATNIP FOR YOU"); } </script> </head> <body onload=ShowWarning("Mittens");> <h1>Family Chore List</h1>` `<ul> <li><strong>Tommy</strong>: Take out the trash</li> <li><strong>Beth</strong>: Clean out the fridge. </li> <li><strong>Mittens</strong>: catch mice. </li> </ul> </body> </html>`清单 1–7 中的代码将产生与清单 1–6 中的代码完全相同的消息。然而,在清单 1–7 中,我将猫的名字作为变量传递。函数ShowWarning
现在希望我传递一个名为“catname”的变量,它可以在代码中使用这个信息。当我调用body
标签中的ShowWarning()
时,我简单地添加了要传递给函数的猫的名字。如果我想的话,我可以通过多项考试。如前所述,这可能会变得相当复杂,取决于我有多想惩罚可怜的米滕斯。
正如您所看到的,将 JavaScript 与 HTML 和 CSS 结合在一起可以产生外观漂亮、易于更新、可以与用户交互的页面。但是有时你可能需要制作一个不给出样式信息的文档——它只给出一般信息。下一节将给出一个主要的例子,因为我们开始进入 XML 的奇妙世界了!
获取信息:可扩展标记语言(XML)
如果你在网上呆过一段时间,你可能会注意到一些页面上有一个奇怪的小图标,看起来像这样。
图 1–7。 一个 RSS 图标
这个橙色的小图标告诉读者当前网站有一个 RSS 源。对于用户来说,RSS 提要看起来很无趣,难以理解(请看图 1–8 了解 RSS 提要的开始)。然而,其他网页和脚本可以使用它们从一个来源获取大量信息,并以不同的方式显示给用户。
图 1–8。 博客的 RSS 提要开始,显示新条目
例如,图 1–9 是我的个人博客的 RSS 提要的开始。每个元素都包含各种各样的数据,这些数据看起来并不美观,但提供了人们可能想要在一个叫做 RSS 阅读器的特殊软件中查看我的博客的所有信息。虽然某些应用,如 Microsoft Outlook,有内置的 RSS 阅读器,但许多人更喜欢使用专用的阅读器客户端。一个流行的 RSS 阅读器是 Google Reader,它可以链接到我的 RSS feed,并生成我的博客的漂亮视图,这样 Google Reader 用户可以很快看到我最近发布了什么文章。
图 1–9。 我的个人博客,显示在谷歌阅读器内
现在,你可能会问,为什么我想让人们在其他地方而不是通常的网址上浏览我的网站。简单的回答是,对于我的用户来说,在一个软件中查看他们阅读的所有博客(我的和其他人的)可能更方便。谷歌阅读器(Google Reader)等软件可以跟踪数百个 RSS 订阅源,包括新闻源、博客,甚至只是简单的状态更新,比如我的 Twitter 订阅源。所有这些信息都由 Google Reader 以一种称为可扩展标记语言(XML)的格式检索。XML 不是您希望让人类观众看到的格式,但是如果您在网页之间或 web 服务之间共享信息,您会希望使用这种格式。
虽然上面的例子将 XML 显示为输出,但是支持我的博客的 web 应用(WordPress)生成 XML,以便其他网站如 Google Reader 可以使用它。XML 也可以用作输入。例如,我可能想获取数据(如体育比分)并在我的网页上显示它们。最有可能的是,这些体育比分将会以 XML 的形式出现,然后我的网页就可以打开它并对其进行解析。解析只是一个花哨的术语,意思是“读取、解释和显示”。我的网页会读取分数,在必要的情况下解释分数(即计算一些东西,聚合一些东西),然后以某种有意义的方式显示给用户。
概括地说,我们现在已经看到了如何构建一个基本的网页,如何使它看起来漂亮(容易),以及如何使它与用户互动。最后,我们讨论了网页和程序如何使用 XML 在彼此之间获取数据。在我们阅读这本书的过程中,我们将深入讨论每一个领域,并给出大量如何使用它们的例子。事实上,在接下来的第二章中,我们将讨论如何从一个非常流行的 web 服务中获取数据,并在我们创建的第一个完整的应用中显示它!
JSON:人类可读的数据交换
如果你有一个绝妙的想法,要开发一个依赖于其他服务的应用编程接口(API)的移动 web 应用,比如 Twitter 或 Foursquare,那么很有可能你会很快被介绍给 JSON (JavaScript Object Notation),这是我最喜欢的技术之一。
JSON 是一种人类可读的、基于 JavaScript 的超轻量级数据交换技术。基本上,它是一个 JavaScript 对象,可用于传输简单的数据结构和信息数组。当在 web 应用中处理外部数据时,我爱上了 JSON,因为它比 XML 等其他技术更易于使用。不过,和所有技术一样,你的收获可能会有所不同。图 1–10 展示了一个 JSON 文档的例子。
图 1–10。T3nerdofsteel.com 罗科·奥古斯托 JSON 格式的作品集
移动网络的昨天和今天
我们中的许多人在 20 世纪 90 年代末开始使用互联网,或者,如果你不在 20 世纪 90 年代末,也许你已经用了一辈子了!虽然我们可能非常熟悉桌面上的互联网,但将它放在一个可以放入我们口袋的小屏幕上可能会有点奇怪,尤其是在谈到网络时经常听到的不同的行话和营销用语。我们将从讨论数据多快到达你的手机开始,然后讨论什么样的数据可以被发送。
了解速度(或者“3G 到底是什么?”)
在最新智能手机的广告中,你经常会听到“4G”或“比 3G 更快”这样的数字。“G”代表技术的产生。你很少听说 2G,第二代,这是有原因的。智能手机上数据的冲击与第三代蜂窝网络数据标准的出现相吻合。然而,1G 和 2G 确实存在,如果你拥有第一部 iPhone(2007 年发布),你只有 2G 速度,使用的协议包括 GPRS、EDGE 和 1X。2G 网络上的数据传输速度大约是拨号调制解调器的两倍,约为 115KbpsSo,虽然电子邮件和基于文本的网页加载速度相当快,但任何包含太多图像或多媒体的内容都将花费大约永久的时间 2 。
大约在 2001 年,我们所认为的 3G(或第三代)数据网络的最初设计被起草,理论速度可以达到 7Mbps 以上。这些网络,包括 UMTS、WCDMA 和 EV-DO 等协议,可以比 2G 网络更快地传输数据。这是第一次,允许创新,如电影和音乐直接传输到手机上。显示复杂网页的限制因素不再是数据连接的速度,而是手机的速度。到 2007 年,大多数电信提供商已经采用并“推出”3G 网络和设备,如移动宽带卡,变得很普遍。
在过去的几年中(2008-2010 年),当前 3G 技术的新版本和批准版本已经出现。虽然对于我们之前所熟知的 3G 技术与这种被称为“4G”的基于 3G 的新技术之间的确切差异存在相当大的争议,但很明显,较新的协议,如 asHSPA、HSPA+、WiMAX 和 LTE,比它们的 3G 前辈更快。不幸的是,虽然所有主要运营商都在逐步推进其提高数据速度和网络容量的计划,但这些更新和变化不会立即对最终用户变得明显,直到他们购买了具有正确内部硬件的电话来利用这些众多变化。
许多以移动设备为目标的 web 开发人员可能会陷入的一个常见陷阱是,开发移动应用时,速度是唯一需要考虑的因素。如果我知道我的用户需要一个 3G 网络来使用我正在开发的功能,考虑到我知道我会有一个更快的数据连接,这可能会吸引我跳过简化应用的其他部分。然而,如上所述,速度不是唯一需要考虑的因素。设备的实际处理能力和软件能力(即网络浏览器的能力)也会降低应用的速度。如果你想要这方面的证据,使用你自己的智能手机或平板电脑的 Wi-Fi 而不是蜂窝数据,观察某些网站和应用在连接到互联网时如何仍然滞后,尽管连接速度(通常)比 3G 或 4G 快得多。我们将讨论如何避免编写臃肿、反应迟钝的应用。
语言和协议,昨天和今天
既然我们已经知道我们能走多快,我们也许应该谈一谈过去 10 年里网页是如何呈现给手持设备用户的,以及移动世界的现状。
起初,手持设备,如最早的现代个人数字助理(PDA),没有直接连接到互联网。这意味着用户想从网上阅读的任何内容都需要先下载,然后存储在设备上或缓存起来。一个非常受欢迎的服务 AvantGo 在 2009 年关闭之前运营了十几年。虽然这些服务有些烦人(因为你需要定期手动将你的 PDA 与你的电脑同步,以获取你想要的内容),但它们通常以非常基本和易于阅读的方式呈现内容。具有讽刺意味的是,这种类型的呈现已经经历了一点复兴,因为今天的用户在在线或其他繁忙的时候(即在工作中)找到内容,并希望保存它以供以后阅读。一个受欢迎的服务,Read It Later ( [
readitlaterlist.com/](http://readitlaterlist.com/)
),甚至有一个移动客户端,以类似于 20 世纪 90 年代流行的老式“离线缓存”系统的格式显示这些保存的网页!图 1–11 显示了一篇稍后阅读的缓存文章。
至少感觉是这样的。
图 1–11。 读完之后,在安卓系统上,显示了一个雅虎的缓存版本体育文章
随着内置 Wi-Fi 无线电和智能手机的 PDA 的出现,用户可以直接连接到网页并查看其内容。然而,在直接接入互联网之前,许多电信公司提供带有 WAP 浏览器的手机。WAP,即无线应用协议,是一种极其简单的数据服务,它允许用户通过简单的菜单找到他们想要的信息。这些菜单通常都是文本,可能到处都有 1-2 张图片,旨在成为基于网络的电子邮件、电影时间或天气信息等内容的快速入口。装有 WAP 浏览器的手机本身并没有连接到互联网,因为它们只能看到提供商在菜单上放了什么,但至少它们可以在有手机覆盖的任何地方查看,而不是下载并离线阅读。
与 WAP 类似的概念是 Java 平台微型版,通常缩写为 j2me。j2me 允许用户在手机上加载小的 java 小程序,这些小程序可以像 WAP 一样连接到特定的服务,提供类似的体验。虽然 j2me (Java Platform,Micro Edition)和高通的 Brew MP 操作系统在 2-3 年前就可以在手机上使用,但它受到提供商可能在手机上设置的奇怪的安全设置和预防措施的限制。奇怪的是,它可以在已经安装了可以去任何地方的网络浏览器的手机上使用。这不禁让人想知道,当你可以简单地访问移动版的 Gmail 时,为什么你会加载一个特殊的 Gmail j2me 小程序。
最后,到 2005 年左右,市场上的大多数智能手机都包含了一个相当不错的网络浏览器,用户可以打开它,键入一个 URL,并查看实际的网页。这些网页通常是以下两种类型之一:塞进较小屏幕的普通网页(见图 1–12)或专门创建的网站移动版本(见图 1–13)。两者各有利弊。普通页面通常在小屏幕上看起来很糟糕,信息从页面上流出,不可读,或者用技术术语来说,不可呈现(呈现是网页在浏览器中显示的过程)。虽然信息通常显示得很糟糕,但如果一个人有足够的耐心和技能,他通常可以找到他们需要的东西。
图 1–12。 我的个人博客,桌面视图,显示在手机浏览器上
另一方面,移动版本通常更容易浏览,因为它们是专门为使用 XHTML 或 CHTML 等 HTML 语言变体的小屏幕设计的。这些页面看起来很好,非常有用,但通常缺乏具体的内容。
图 1–13。 我的个人博客,显示在移动浏览器的移动视图中
这本书的目标是帮助你创建一个网站的移动优化版本,包含与桌面版本相同的内容,同时仍然可用。正如你所料,那些觉得你的页面易于使用的用户更有可能忽略一些缺失的部分。但是,需要 20 分钟的用户(或者像我们的一位编辑指出的,20 秒!)寻找最新的比赛比分或天气预报可能会涌向其他地方。
我们喜欢的概念——以及未来的发展!
在我们结束这一章的时候,我们想花一点时间来讨论一些我们想要记住的指导原则,并希望你不仅在阅读这本书的时候,而且在开始编写自己的 web 应用的时候也能记住。
理念一:像用户一样思考
想想你拥有的一个网站,一个你为之做出贡献的网站,或者只是一个你经常访问的网站,问问你自己:大多数人在访问这个网站时都做些什么?答案可能很快就会出来。然而,更深入地思考这个答案到底有多“平均”可能是值得的。
例如,我的一个朋友最近在《赫芬顿邮报》上发表了几篇博客文章,该网站以快速和直言不讳的方式对一篇文章发表评论而闻名。当他发给我他的文章的链接时,我把它拉了出来并通读了一遍。完成后,我关闭了浏览器,开始了我的一天。本周晚些时候,我的朋友问我是否看到了那篇文章,在我说我看到了之后,他问我“你对那些评论有什么看法?”答案很简单。我对那些评论没有任何想法,当然甚至不足以阅读它们。然而,在他看来,由于它们是对他自己作品的反馈,评论比文章本身更有趣(毕竟,对一个作者来说,他们自己的作品似乎很熟悉,也很无趣)。现在想象一下,如果我的朋友设计了发布他作品的网站。如果你看到用户的评论被突出显示,甚至以较小的字体显示在文章的旁边或底部以外的其他地方,你可能不会感到惊讶。对我的朋友来说,“普通”用户对评论比对文章更感兴趣,而事实上,情况可能正好相反。
谢天谢地,我的朋友不是程序员。但是作为程序员,我们必须跟踪我们的用户在用我们的网站或应用做什么。如果我们把所有的注意力都集中在用户认为微不足道的功能上,而忽略了用户认为必要的核心功能的更新或增强,我们可能会失去这些用户。
概念 2:不要惹恼用户
我能给出的概念 2 的最好例子来自于“货币化”这个邪恶的词,或者简单地说,“嘿,我做了很多工作,我想以法定货币的形式得到某种回报!”货币化通常来自销售广告;用户理解的东西是生活的事实。如果你想要免费的东西,你会期望以某种方式时不时地受到广告的影响来保持它的免费。大多数人认为这比为内容付费更可取,事实上,许多出版商报告说通过广告比通过销售他们付费的产品赚得更多!
然而,仅仅因为你在向人们做广告,并不意味着你需要在他们面前插入广告!你需要使用有品位的广告,激起用户兴趣的广告,不惹恼用户的广告。每一段或者每一页的顶部、侧面和底部的广告很少是有效的。
类似地,不必要的内容或特殊效果也可能使用户烦恼。例如,最近我遇到一个页面,它使用了一个特殊的登录按钮,有一个很好的凸起边缘,在我的桌面浏览器上看起来很好。然而,在我的 Android 智能手机上,浏览器很困惑:这是一个按钮还是一个图像?当我敲它的时候,什么也没发生。最后,通过一个非常奇怪的动作,轻敲、轻握和移动手指,我让它登录了。如果此类网页的作者只需执行以下操作之一,问题就会得到解决:
- 用移动浏览器测试了页面
- 使用标准的 web 表单按钮
在这种情况下,形式优先于功能。不要误解我的意思,花哨的设计很好,只要它们不降低可用性。
概念 3:重测信度
在我(乔恩的)日常工作中,我研究个人如何做决定以及这些决定的影响。在我的研究生涯中,重测信度的概念是指一个给定任务一次运行的结果应该与第二次(以及随后)运行的结果相同。我将借用研究方法论中的概念来讨论一个关于测试你的内容的观点。
和概念 1 类似,用户都不一样。他们中的大多数人会有一个稍微不同的设置,比你在设计网页或应用时。例如,现在我使用的是 27 英寸的显示器,作为一名设计师,我觉得这非常有用,但我不希望大多数人都有。因此,如果我设计一个网页,我可能不应该假设人们的浏览器窗口是 1565 像素宽,912 像素高,就像我现在这样。当使用较小的屏幕时,这变得尤其重要。一些 Android 手机和平板电脑的屏幕相对较大,而另一些则较小。在各种设备上测试你的应用或网页(也许在朋友的帮助下)可以确保你的用户每次访问网站时都能获得最佳的体验。人们还必须小心重复访问,因为浏览器可能会缓存它应该更新的内容。另一方面,我们将在未来章节中重点介绍的一些技术利用了模板或框架,这些模板或框架将使浏览器缓存您的朋友。这些技术重用用户已经下载的内容!
理念四:保持简单愚蠢!
如今,你走在街上,一定会看到一些父母把他们的智能手机放在他们蹒跚学步的孩子面前,让他们在长途乘车或在商店里忙碌。在这个神奇的基于触摸、支持互联网的手机和平板电脑时代到来之前,在宽带互联网时代到来之前,我(罗科)也喜欢称之为“前时代”,进入一个基础计算机班被认为是一种巨大的特权。如果一个人想在高中毕业时拥有一份成功的、热爱电子表格的工作,这是他们在小学时就应该做的事情之一。
在每周和父亲一起看《星际迷航》的宗教般的成长过程中,你可以想象当我进入六年级,终于能够上我的第一堂正式的计算机课时,我是多么的欣喜若狂。我是说,拜托!你怎么能不想像斯科特一样,在几秒钟之内就在电脑上完成透明铝的配方呢?就在一秒钟之前,你还在对着鼠标说话,并试图给它发出语音命令。!这就是为什么开学前的整个夏天我都去图书馆查阅我能找到的每一本计算机书籍,为我即将到来的计算机初学者课程做准备。当我了解到计算机世界并不像《星际迷航》那样迷人和令人兴奋,而且每一部有电脑黑客的电影看起来都是如此时,这种兴奋很快变成了失望。相反,我从那门课中学到的最多的是如何复制贝弗利山警察主题曲并把它放在软盘上,以及如何打开 HyperCard 应用(尽管奇怪的是我们从未学会编写 HyperCard 应用。)
第一年,虽然很失望,但却成了我对计算机的决心/痴迷的催化剂,这使我在七年级时再次报名参加了计算机班。我不知道的是,这门计算机课,我生命中每天这微不足道的一小时,最终会导致我一生的痴迷。看,这是美国在线在美国开始真正流行的一年。最重要的是,今年见证了 HTML 2.0 的诞生。我的老师当时看到了这项技术的发展方向,并对信息自由交换的未来充满憧憬。每天在课堂上,他都会反复强调,总有一天“创建网页的人”(这是在 Web 开发人员这个术语被创造出来之前)会获得和医生、律师一样的报酬,并且同样有价值和重要。当然,我们都认为他是一个疯狂的老人,但他对未来的兴奋足以让我着迷,最终我开始相信它并宣扬它。他对这个愿景深信不疑,以至于我们全年唯一要做的工作就是创建学校的整个网站。
在我从那堂课上学到的所有东西中,我学到的最重要的规则是 KISS 原则,这是“保持简单愚蠢”的首字母缩写尽管我很喜欢这位老师教给我的知识,但不可否认,他是个爱发牢骚的老混蛋。每次我们为我们学校的网站想出了一个绝妙的主意,比如添加动画 GIF 火焰和海狸和大头鱼的精灵,他总是说“保持简单,愚蠢!”作为一个孩子,不断被拒绝,基本上每天都被称为白痴,这很烦人,但它教会了我耐心和克制的艺术。这让我意识到,即使特性 X、Y 和 Z 在我从事的任何给定项目中都很棒,但更重要的是将它们放在次要位置,这样我就可以对特性 A、B 和 C 进行编码和交付。现在,当我在做一个项目时,我会坐下来,详细规划我需要做些什么来启动我的项目。我把我对额外功能的任何酷想法放在一边,这些额外功能不是发布时必须的,所以我可以在以后手头的任务不会转移我的注意力时再去做。
不管你是谁,也不管你从事设计或开发有多长时间,有一点永远是正确的。任何项目最难的部分是开始那个项目。你的项目中不需要的功能越多,你最终添加的功能就越棒(例如,给热棒添加火焰使它跑得更快),你就越难决定从哪里开始你的项目。所以请记住,无论你刚刚想到的想法多么不可思议,如果不是绝对有必要让你的项目起步并引起人们的兴趣,那么在将它添加到项目中之前,请试着三思。对这个想法做一个心理记录或物理记录,稍后再回来讨论。一个完美的例子就是像 Twitter 这样的程序。几年前 Twitter 发布的时候,它是一个非常稀疏的程序,围绕着一个非常简单的想法“你现在在做什么?”随着时间的推移,更多新的和令人兴奋的功能被按需添加。Twitter 现在已经从一个你可以告诉世界你午餐吃了什么的愚蠢的小网站发展成为一个大量投资的 Web 应用,它打破了像迈克尔杰克逊之死这样有新闻价值的事件,甚至在从内战中互联网匮乏的国家获取信息方面发挥了重要作用。
记住罗马不是一夜建成的。这是一个不断发展的帝国,每天都在自我建设,你应该以同样的方式看待你的下一个网络项目。从小处着手,尽可能地增加,同时不要让自己在这个过程中不知所措。本质上— 保持简单,笨蛋!
即将到来
既然我们已经讨论了网页设计的具体细节、首字母缩写词、奇怪的故事和概念,那就让我们来谈谈你会在这本书里发现什么。
从下一章开始(只差几页了!),我们将利用当今网络上最流行的服务之一 Twitter 开发一个简单的应用。Twitter 是一个伟大的网络现象,所以我们将在第三章的另一个小应用中继续使用它。然后,我们将后退一步,看看更大的画面——比如你如何首先开发一个应用(第四章),以及如何处理不同的屏幕分辨率和浏览器平台(第五章 & 6 )。在第七章中,我们将展示 jQuery Mobile——更重要的是,展示如何用它让你的应用看起来更专业和“漂亮”。第八章将继续这个主题,展示一些在用户界面设计中使用的好资源。这样一来,我们就可以玩玩基于位置和基于云的应用,然后讨论如何使用音频和视频、Ajax 以及如何准备产品的“发货”来结束这本书。有很多材料要涵盖,但我们知道你已经准备好了!此外,如果你还没有注意到,我们的趋势是通过例子给你很多信息,然后回到更像教科书的叙述。通过这种方式,希望你能在这本咬指甲的电脑书上保持专注!(好吧,这有点讽刺,尽管我们真心希望这种方法能让阅读变得“有趣”。)
所以,不再拖延,让我们从第一个应用开始吧!
二、推特应用:那条推特是谁的?
既然我们已经涵盖了本书中将要学习的内容,那么是时候开始动手创建我们的第一个移动 web 应用了。这两个应用都是与 Twitter API 交互的非常简单的应用。API 或应用编程接口是一种接口,用于根据用户提供给应用的一组规则或参数与特定应用进行交互。人们通常会使用 API 从应用的基础设施中收集和解析数据,而无需直接连接到应用的数据库来获取这些信息。
我们将构建的第一个应用是“那条推特是谁?”,它将解析一个预定义的已验证 Twitter 用户的小列表,并在页面上显示一条随机的推文,以及可能创建该推文的四个可能个人的列表。
我们将构建的第二个小型 Web 应用名为“我爱火腿”,它也将借用 Twitter 的数据消防水管,为移动用户创造一种快速而有趣的娱乐形式。《我爱火腿》将关注非常基础的游戏机制。用户可以从两个预定义的押韵 Twitter 搜索中进行选择。如果他们选择收到最多结果的 Twitter 搜索,那么他们就是赢家。这两款手机网页游戏都将严重依赖 HTML5、JavaScript 和一项名为 JSONP 的神奇技术。
在这一章中,我们将让你在自己的个人电脑上建立并运行一个开发环境(把它想象成你自己的迷你互联网,它将连接到真实互联网上的 Twitter,但大多数情况下只是在你的电脑上运行),并讨论那条 Tweet 是谁?应用。在下一章,我们将继续讨论我爱火腿应用。我们开始吧!
JSONP
你可能记得我们在第一章谈到 JSON。JSON 是一项很棒的技术,如果你在过去几年中在 web 开发领域工作过,你可能会遇到一两次。如果你在过去的一天里浏览过网页,你也可能浏览过使用它的网站!如果您曾经使用过 Flickr、Twitter 或 Gowalla 的 API,那么您可能也非常熟悉 JSON。对于那些完全跳过第一章的人来说,JSON 是一种人类可读的数据交换技术,它是轻量级的,也是一种开放标准。作为一项技术,JSON 还相当年轻,它的使用可以追溯到世纪之交,2002 年JSON.org
( [
www.json.org](http://www.json.org)
)网站上线。
如果您熟悉使用 JavaScript 和创建对象,那么 JSON 应该对您来说比较熟悉。然而,不要将 JSON 与 JavaScript 对象混淆,因为它们是不同的。虽然我很喜欢 JSON,但是有一件很小很烦人的事情,大多数开发人员直到花了几个小时拔掉自己的头发,让自己过早地秃顶,才意识到,这是一件非常讨厌、很小、很烦人的事情,叫做跨域脚本。
要理解什么是跨域脚本以及为什么你的浏览器中有硬编码规则来阻止你这样做,你必须首先理解什么是跨站点脚本(XSS)。XSS 是网站和应用中的一种漏洞,它允许攻击者或入侵者将自己的脚本和内容注入到网站或系统中。入侵者可能会利用 XSS 漏洞将脚本上传到您的服务器,这将允许他们在自己的浏览器中为您的服务器模拟 Bash shell(常见的 UNIX shell 或命令行界面),或者更糟的是,可能会利用该漏洞从您的数据库中窃取数千个用户名和密码。
出于这些考虑,在大多数情况下,您的浏览器不会从站点服务器之外的域加载 JSON 数据。为了获取和使用这些数据,必须在加载页面并提供给用户之前,在后端服务器端获取这些数据。这看起来没什么大不了的,直到你开始构建拥有成千上万用户的应用。那么解析外部数据最终会影响服务器的性能。为了解决这个性能问题并将一些外部数据解析需求委托给最终用户,我们将使用 JSONP 或“带填充的 JSON”,这将允许我们的应用请求我们需要的 JSON 数据,因为它将被包装(或填充)在一个 JavaScript 对象中。
设置您的开发环境
在我们开始编码之前,我们需要建立一个开发环境。为了简单起见,我将假设我们使用的是基于 Windows 的操作系统,所以我将只介绍设置一个基本的 WAMP (Windows、Apache、MySQL 和 PHP)服务器的步骤,以便我们在上面测试我们的代码。我知道并不是每个人都使用基于 Windows 的系统,但是如果你使用 Linux 或者 OS X,那么你可能已经熟悉了建立一个*AMP 测试环境。
对于我们的测试环境,我们将使用开源的免费 WAMP 服务器 Uniform Server ( [
www.uniformserver.com/](http://www.uniformserver.com/)
)。你可以从他们的网站下载 WAMP 服务器。我正在使用的版本,以及将在本章截图中显示的版本,将来自版本“7 . 1 . 2–Orion”
设置测试环境相当简单。只要找到你从网站上抓取的下载文件,运行自解压 EXE 文件。完成后,打开文件夹。在运行服务器之前,我想看一下 Uniform Server 团队为了让我们的生活更轻松而提供的一些有用的程序。第一个应用称为预检查(Run_pre_check.exe)。 1 让我们运行这个应用,看看图 2–1 中的结果。
图 2–1。 统一服务器的预检查应用显示端口 80 和 443 当前正在使用
如您所见,如果我试图在此时运行服务器,它将会失败。我目前打开了 Skype,它运行在我运行服务器所需的经典 HTTP 端口(端口 80 和 443)上。在网络中有一个程序到端口的规则,所以我们需要做一些简单的调整。虽然您可以编辑一些配置文件来更改您将使用的端口,但为了简单起见,我将关闭 Skype,以便我们可以在默认设置下运行。
现在我们已经释放了端口,我们可以运行“Start.exe”应用,它将打开并在您的系统托盘中放置一个图标。右键单击系统托盘中的统一服务器图标。它看起来像一个蓝色的盒子,里面有数字 1,如下面的图 2–2 所示。选择菜单顶部的“安装并运行所有服务”选项。现在,如果您打开 web 浏览器并将其指向http://localhost/,
,您应该会看到默认的统一服务器启动页面(localhost 只是您当前工作的计算机的另一个名称,IP 地址是 127.0.0.1)。
您可能需要以 Windows 管理员的身份运行这个命令和其他命令。为此,右键单击命令并选择“以管理员身份运行”
图 2–2。 系统托盘中看到的统一服务器菜单
现在我们的服务器已经启动并运行,我们已经差不多准备好开始写一些代码了。我们必须做的最后一件事是熟悉服务器的默认 WWW 根文件夹。如果你打开解压统一服务器的文件夹,你会看到一个名为 www 的文件夹(见图 2–3)。任何通过浏览器查看您的站点的用户都可以看到您放在此文件夹中的任何内容。由于我们将使用我们自己的项目文件,您可以随意删除该文件夹中的所有内容,重新开始。稍后,您可能会在设置生产服务器时使用这些文件。但是,出于开发目的,并不需要它们。
图 2–3。 默认的 WWW 文件夹视图,将存放您的应用项目文件
现在,您已经成功地为本地开发建立了自己的 web 服务器,并清理了根文件夹,是时候开始一些编码了!如果你需要任何关于统一服务器的帮助,或者只是想更深入地挖掘一下,看看你能用这个服务器做些什么,你可以运行统一服务器目录中的“help.bat”批处理文件,它会给你提供一些非常深入的文档(见图 2–4)。
图 2–4。 统一服务器快速帮助文档
你的第一个移动 Web 应用
给自己倒杯咖啡,做点伸展运动,因为现在是开始编码的时候了。我们正在构建的第一个应用,“那条推特是谁?”,将是一个小而简单的游戏,收集经过验证的 Twitter 用户和名人,并随机显示他们的一条推文。然后,用户将会看到一个由四个可能创建该 Twitter 帖子的 Twitter 用户组成的列表。如果最终用户选择了正确的 Twitter 用户来支持该帖子,那么他们的分数将增加 100 分,并进入下一个随机帖子。如果没有,他们会得到一个视觉提示,表明他们的猜测能力较差,并将继续随机发布下一篇 Twitter 帖子。
我们要做的第一件事是为我们的游戏创建一个非常基本的 HTML 布局,或者说基础。在那之后,我们将继续用一点 CSS3 来设计这个平淡无奇的框架布局,一旦它看起来很漂亮,我们将最终继续编写我们的 JavaScript,它将在我们的第一个移动 web 应用中把一切联系在一起。
HTML
清单 2–1 包含了我们游戏的基本 HTML。
清单 2–1。 那是谁的推文?
`
Player 1
页面的 HTML 标记非常简单。从head
元素开始,您可能注意到的与标准的基于桌面的 web 开发略有不同的第一件事是使用了名为“viewport”
的meta
元素。当涉及到移动设计和开发时,这个元素是极其重要的,我们将在第十章中详细讨论。通过它,您可以控制用户是否可以缩放页面上的内容,甚至可以控制用户可以放大页面的多少。在这里,我们将视口的缩放设置为默认值,并关闭了允许用户放大和缩小页面的选项,因此我们不必担心最终用户无意中双击显示器会导致您即将开发的 web 应用放大或缩小,同时让您的用户感到沮丧。
一旦进入body
元素,你可能会注意到我们使用了几个“新”HTML5 元素,比如header
和section
。header
元素是一个较新的标签,用于从语义上构建您的代码,以指示对 web 上的页面或文章的介绍。在我们的header
区域,我们用它来存放玩家的当前分数,我们会在每一轮之后用 JavaScript 更新分数,以及玩家的名字。考虑到这个演示的简单性,我们将把玩家命名为Player 1
。
接下来我们有section
元素,它用来存放我们演示的主要内容。像header
一样,section
元素是一个新的 HTML5 标签,用于语义标记文档或页面的部分。回到过去的 xHTML 时代,我们通常会用潮水般的无休止的div
标签来指定页面的各个部分,这可能会让搜索引擎和视障人士的屏幕阅读器感到非常困惑。现在,通过正确使用section
标签,搜索引擎和屏幕阅读器应该很容易就能猜出页面上哪些信息是应该大声朗读或搜索的重要信息。
在页面的主要内容部分,我们有一个 ID 为tweet,
的div
容器,它将用于存放我们从已验证用户的数组中提取的随机 tweet,以及一个空的ul
(无序列表)元素,它将用于包含我们将向最终用户显示的四个随机验证的 Twitter 用户。
在#tweets
容器内部,我们看到两个div
容器—.avatar
和.content
。对于那些不熟悉 CSS 的人来说,在本书中,每当你提到代码时看到散列标签(#),就意味着我们指的是元素的 ID。如果我们通过一个标签的 class 属性来引用它,那么它前面会有一个句号,比如上面的.avatar
。
现在我们已经有了基本的结构设置,是时候进行整个过程中我最喜欢的一部分了——用 CSS 来设计页面的样式!
CSS
在上一章中,我们讨论了 CSS。在这里,您将通过实际操作来了解更多信息。然而,如果你没有跟上正在发生的一切,也不要气馁。我们将在这一章和以后更多地讨论它。
有了这里的 CSS,我们不打算做任何超级花哨的东西。然而,我们将利用 Android 的 Webkit 2 渲染引擎来使用一些在过去几年中变得流行的较新的 CSS3 功能。让我们从一些基本的样式重置和一般布局样式开始,然后一节一节地浏览我们的 CSS 文件。在清单 2–2 中,我们将梳理页面上的基本 HTML 元素,并重置它们的边距和填充,或者它们在页面上的间距,以及重置整个文档中使用的字体大小和类型。
清单 2–2。 那是谁的微博?CSS 文件–第一部分
`/*** RESET ***/
html, body, header, section, div, p, ul, li, h2 {
margin:0; padding:0;
}
ul, li {
list-style:none;
}
/*** GLOBAL CSS ***/
html, body {
width:100%;
overflow-x:hidden;
}
body {
font-size:14px;
font-family: 'Droid Sans', Arial, Helvetica, sans-serif;
background:#06c;
}
h2 {
font-size:14px;
}`
如果你过去写过 CSS,这第一点样式代码应该看起来非常基本和熟悉。我们首先将几个页面元素的边距和填充重置为零,以确保我们分配给页面的任何样式都可以在大量 Android 设备上正确定位。这一点非常重要,尤其是当你记住 Android 是一个快速发展的平台,每年会发布两次或更多的新版本。
接下来我们要做的是删除任何可能附加到无序列表(ul)
和列表项(li)
元素)的项目符号,然后设置页面的默认字体和字体大小,以及应用的背景颜色。
最后但同样重要的是,我们将使用“Droid Sans”网络字体。看,在过去的几年里,随着网络的发展,设计师和开发人员一直在尖叫和乞求一种新的和更容易的方式来使用那些你在他们的应用和网站上的印刷广告中一次又一次看到的华丽字体。我是认真的,谁想整天盯着 Arial 或者 Times New Roman?CSS3 令人难以置信的特性之一是在你的项目中使用了 True Type 字体。从现在开始,你不必依赖于压缩不良的图像或可怕的侵入式 Flash 解决方案来利用漂亮的排版。
2 Webkit 是一个开源的网页浏览器引擎,这意味着人们可以使用它制作网页浏览器软件。安卓使用它的定制版本,苹果的 iOS 也是如此。
谷歌甚至想尽办法帮了我们一个忙,创建了一个免费开放的网络安全字体数据库,供你随心所欲地使用。您可以在[
www.google.com/webfonts](http://www.google.com/webfonts)
访问 Google Web Fonts 网站。一旦你找到你想要使用的字体,谷歌会给你一些 HTML 代码添加到你的布局中,将字体加载到你的浏览器中,并从你的 CSS 文件中调用它,因为我们已经将“Droid Sans”称为我们页面的默认字体(见图 2–5)。
图 2–5。 在谷歌网络字体网站上查看 Droid Sans 字体
在清单 2–3 中,我们将为用户提供一个时尚的标题,它将停留在他们浏览器的顶部,并将包含玩家的假名以及他们的分数。
清单 2–3。 那是谁的微博?CSS 文件,第二部分
/*** HEADER ***/ header { width:100%; font-weight:bold; text-transform:uppercase; text-shadow: 1px 1px 1px rgba(255,255,255,0.5); line-height:40px; background-image: -webkit-gradient( linear, left bottom, left top, color-stop(0, rgb(135,135,135)), color-stop(0.8, rgb(201,201,201)) ); -webkit-box-shadow:0 0 15px rgba(0,0,0,0.5); box-shadow:0 0 15px rgba(0,0,0,0.5); } header h2 { margin-left:10px;
} .score { float:right; margin-right:10px; }
CSS 的 HEADER 部分是我们享受乐趣和利用 CSS3 魔力的地方。在广泛使用 CSS3 之前,如果你想创建一个使用渐变背景的页面部分,那么你必须依赖于在图形设计程序中创建图像并拼接它们以满足你的需要。现在,就像魔术一样,你可以在你的 CSS 中添加一些不那么简单的代码,只需要几行代码就可以创建出和 Photoshop 一样的渐变效果。除了这些漂亮的渐变,我们还可以用一行小小的 CSS 代码给我们的header
添加阴影。就像魔法一样!
清单 2–4 可以被认为是我们 CSS 文件的主菜,因为它包含了使我们的页面看起来很棒的大部分属性。
清单 2–4。 那是谁的微博?CSS 文件,第三部分
`/*** SECTION ***/
section {
margin:20px 10px;
padding:10px;
border-radius:7px;
background:#aaa;
-webkit- box-shadow:0 0 20px rgba(0,0,0,0.5);
box-shadow:0 0 20px rgba(0,0,0,0.5);
}
section.fail {
background:#990000;
color:#fff;
}
section.win {
background:#009900;
color:#fff;
}
section.fail li, section.win li {
border-bottom:1px solid #fff;
}
section.fail li:first-child, section.win li:first-child {
border-top: 2px groove #fff;
}
section #tweet {
width:100%;
margin-bottom:10px;
padding-bottom:10px;
}
section .avatar {
float:left;
width:48px;
height:48px;
}
section .content {
margin-left:58px;
min-height:48px;
}
section .person {
margin-left:42px;
}
section ul {
width:100%;
clear:both;
}
section li {
margin:0;
padding:5px 5px 5px;
height:32px;
line-height:32px;
border-bottom:1px solid #666;
}
section li .avatar {
width:32px;
height:32px;
}
section li .avatar img {
width:32px;
height:32px;
}
section li:first-child {
border-top: 2px groove #666;
}`
CSS 的section
部分,就像它之前的 HTML 文档一样,是大部分奇迹发生的地方。你应该注意到的第一件事是 CSS3 的另一个漂亮的新特性——border-radius,它是为了让我们的生活变得更简单而开发的。现在,我们可以将这项工作交给浏览器的渲染引擎来完成,而不是花费数小时来尝试将图像正确拼接在一起,以使文档具有圆角,或者更糟,依赖臃肿的 JavaScript 库来满足我们的边界半径需求。
这里我们利用 border-radius CSS 属性给我们的section
容器一个漂亮的现代智能手机外观,然后用一个漂亮的阴影来结束它。之后,我们为.win
和.fail
类定义了两组样式,我们将通过 JavaScript 调用每一轮样式,作为用户选择是否正确的视觉指示器。
图 2–6、2–7 和 2–8 分别显示了我们的游戏视图、错误答案后的视图和正确答案后的视图。现在,让我们了解一下生成用户反馈视图所需的 JavaScript。
图 2–6。 那是谁的推特?运行安卓 2.3 网络浏览器
图 2–7。 那是谁的微博?未能选择正确的推文所有者后显示
图 2–8。 那是谁的推特?选择正确的推文海报后显示
JavaScript
现在我们已经设计好了页面的布局和样式,是时候用一些 JavaScript 让它动态起来了。本书中的所有应用演示都将利用 jQuery JavaScript 框架。如果你不熟悉 jQuery,它是一个奇妙的 JavaScript 库,世界各地都在使用,与 Internet Explorer 6 占主导地位的旧时代相比,它使跨平台 JavaScript 开发变得微不足道。
这里有一点 JavaScript 代码,所以我会像之前处理 CSS 代码一样分解它,一次解释一个部分。在编写这个演示时,我在代码中添加了大量有用的注释,这将进一步帮助我们当中的新开发人员理解正在发生的事情。
清单 2–5 将向我们展示一组将在我们的应用中使用的默认变量,并给我们一个非常丰富的数组,其中包含一些 Twitter 最受欢迎的验证用户。
清单 2–5。 那是谁的微博?Javascript 文件,第一部分
`/**
* Who's That Tweet
* @author: Rocco Augusto
* @file: js/main.js
**/
/* This array holds a list of verified
Twitter accounts that we will use
to pull in random tweets for each
round
*/
var defaultIcon =
'https://si0.twimg.com/sticky/default_profile_img/default_profile_3_normal.png';
var currentRound = [];
var correct = '';
var tweets = [
'jimmyjohns',
'sugarsammyk',
'wilw',
'JeriLRyan',
'pattonoswalt',
'trutriciahelfer',
'AndrewWK',
'ChristianKane01',
'charliesheen',
'levarburton',
'edwardjolmos',
'Rosie',
'kevinrose',
'jason',
'leolaporte',
'aplusk',
'StacyKeibler',
'LilianGarcia',
'nicolerichie',
'rainnwilson',
'ericschmidt',
'pennjillette',
'nerdist',
'Scobleizer'
];`
这里我们定义了几个将在整个应用中使用的全局变量。第一个是defaultIcon
变量,它包含默认用户 Twitter 头像的长 URL,我们将在整个脚本中使用。我们可以不使用变量,只在需要时将代码添加到脚本中,但是在编码时尽可能多地缓存代码以供重用始终是一个好的做法。现在,当我们想在脚本中的任何地方添加defaultIcon
时,我们不必记住一些又大又长又复杂的 URL,在大多数情况下,这些 URL 只会在视觉上搞乱我们的代码。
接下来,我们创建一个名为currentRound
的空数组,它将存放我们从 Twitter 上获取的当前回合的所有数据。我们创建一个预定义的验证用户阵列,从中随机选择。对于这个列表,我刚刚在谷歌上搜索了经过验证的 Twitter 用户,并复制了我在谷歌搜索的第一页上找到的所有用户的名字。
如果这是一个基于 Twitter API 的真实游戏,而不是一个演示,我们会希望利用一个包含所有 Twitter 验证帐户列表的在线服务器,但是,因为我们是从小规模开始,所以现在一个数组就可以了。
清单 2–6 显示了主 JavScript 文件的第二部分,这是关于那条 Tweet 是谁的。
清单 2–6。 那是谁的推特?Javascript 文件,第二部分
`// this function will grab a random user from
// the tweets array.
function getRandom() {
var l = tweets.length; // grab length of tweet array
var ran = Math.floor(Math.random()*l); // grab random user
// check if randomly selected user is in list
if(currentRound.indexOf(tweets[ran]) == -1) {
if(currentRound.length == 0)
correct = tweets[ran]; // make first random user the correct user
//push random user to currentRound array
currentRound.push(tweets[ran]);
// grab three additional random users
if(currentRound.length < 4)
getRandom();
}
else {
// if random user is already in list then start over
getRandom();
}
}`
在清单 2–7 中,我们的下一个函数将遍历并从我们的 tweets 数组中选择一个随机用户,然后抓取另外三个随机用户供用户选择,如函数getRandom()
所示。在这个函数中,我们使用 JavaScript Math 对象来选择随机用户。
清单 2–7。 那是谁的微博?Javascript 文件,第三部分
`// grab the user avatars for users in currentRound array
function buildQuiz() {
var l = currentRound.length;
for(i=0;i<l;i++) {
$.getJSON('https://twitter.com/status/user_timeline/' + currentRound[i] +
'.json?count=10&callback=?', function(data) {
var img = data[0].user.profile_image_url;
var name = data[0].user.screen_name
var h = '';
// crete the html that will be injected into the game screen
h += '
h += '
h += '
h += '
// add users and images to page
$('section ul').append(h);
});
}
}
function init() {
//reset the correct answer and currentRound array
correct = '';
currentRound = [];
(′ul,section′).removeClass();('ul').empty();
getRandom();
//display the default twitter account icon
$('#tweet .avatar').html('');
//grab a random tweet from the correct user
$.getJSON('https://twitter.com/status/user_timeline/' + correct +
'.json?count=10&callback=?', function(data) {
var ran = Math.floor(Math.random()*data.length);
console.log(data.length);
console.log(ran);
$('#tweet .content').html(data[ran].text);
// randomize the currentRound array so the correct user isnt always first
currentRound.sort(function() {return 0.5 - Math.random()});
// grab avatars and display usernames
buildQuiz();
});
}`
在接下来的两个函数中,我们将通过调用函数init().
来启动应用。该函数重置所有变量以启动一个全新的游戏,解析随机选择的验证 Twitter 帐户的 JSONP Twitter API 提要,然后通过函数buildQuiz()
将所有要构建和显示的内容传递给用户。
一旦文档加载到浏览器中,DOM(文档对象模型 HTML 文档中所有对象的跨平台表示)准备好被操作,下面的代码(清单 2–8)就会运行。在 jQuery 中,可以在文档准备好的时候运行代码 JavaScript 中的ondocumentready
事件——使用下面的简写方式$(function() { ... code goes here });
一旦文档准备好,我们将调用init()
函数,该函数将重置页面上的所有内容,随机获取一个用户,并像我们之前所经历的那样构建测验。我们还将把click
事件分配给所有将被写入页面的列表项,这样它们最终会像按钮一样工作。一旦用户点击列表项,应用将使用发布推文的用户的正确头像替换defaultIcon
,如果用户选择正确,则更新分数,并将gameover
类分配给游戏,如果屏幕再次被点击,则应用将告诉应用开始新一轮游戏。
清单 2–8。 那是谁的微博?Javascript 文件,第四部分
`$(function(){
// run the init() function and start the application
init();
// check for correct user on user selection
$('ul li').live('click', function() {
var name = $(this).attr('rel');
var img = (′li[rel="′+correct+′"]img′).attr(′src′);varscore=('.score span');
// restart the game if game over
if(\(('ul').hasClass('gameover')) {
init();
}
else {
// swap out default avatar for correct avatar
\)('#tweet .avatar img').attr('src',img);
if(name == correct) {
score.text(parseInt(score.text())+100);
\(('section').addClass('win');
}
else {
\)('section').addClass('fail');
}
// add gameover class to page
$('ul').addClass('gameover');
}
});
});`
这就是了!对于那些正在编写代码的人,恭喜你,你已经正式完成了你的第一个移动 web 应用。你现在可以启动你信任的 Android 设备(如果你还没有这样做的话),连接到你的电脑所在的网络,并在上面查看你的新应用。为此,只需将您设备的浏览器指向您计算机的 IP 地址。您可以通过打开命令提示符(Windows)并运行ipconfig
命令(参见图 2–9)或打开终端(OS X/Linux)并运行ifconfig
命令来找到您的 IP 地址。
图 2–9。Windows 7 上的命令提示符显示我们服务器的 IP 地址
这就是你的第一个应用。
总结
我们在这里已经讨论了很多“应用”领域,比如设置开发服务器、编写 CSS 和 JavaScript 文件,以及测试新的应用。我们将在下一章的另一个例子中继续这种“应用”写作,然后停下来多谈一点网页设计的核心概念。现在来说说第三章里的火腿!
三、推特应用:我爱火腿
现在我们已经有了一个移动 web 应用,我想让我们探索使用 Twitter API 来做一些与老一套经典的“显示 Twitter 消息应用”有所不同的事情。正如我们在第二章中讨论的,使用 Twitter API 可以让你接触到总是充足的数据“消防水管”,这些数据可以用于所有类型的应用和游戏——基本上是你能想象到的任何东西。
我一直喜欢使用 Twitter 的一个想法来自几年前的一系列旧网络节目。虽然这个游戏的名字随着节目的变化而变化,但游戏背后的主要原则始终是一样的。用户会看到两个不同的押韵的搜索词,他们必须猜测哪个词会产生更多的推文。虽然规则很简单,但这个我们将构建并命名为“我爱火腿”的小游戏可以带来数小时的乐趣。所以事不宜迟,让我们启动本地开发环境,开始编写一些代码吧!
HTML
下面的清单 3–1 建立了基本的 HTML 文档,它将成为我们应用的基础。
清单 3–1。 HTML 为我爱火腿
`
Which one of these fabulous tweets has more search results?!
我们在这个应用中使用的 HTML 标记将比“那条推文是谁?”中的要少一些,我们以前构建的应用,它看起来也非常相似!为了简单起见,我保留了几乎相同的文件夹树布局的命名约定(参见图 3–1):
/index.html
/css/styles.css
/js/main.js
图 3–1。 一个清晰易读的文件结构,就像 Adobe Dreamweaver CS 5.5 中看到的那样,将为您节省大量的时间,并减少后续的挫折!
在创建文件和组织项目的文件夹结构时,我发现坚持人类可读、一目了然且在整个项目中保持一致的命名方案通常很有用。如果您最终将文件命名为 " supercooldocument12-667.html
",您会使最终用户更难找到这些文档,如果您将来回到应用进行更新和编辑,这可能会导致问题。(相信我们:你会完全忘记那些文件是做什么的。)另一个技巧是用当前日期命名文件,或者使用版本控制系统来保持事物的完整性。所以请记住,在命名文件时,要保持简单和/或有意义!
CSS
我们的 CSS 也将看起来类似于我们之前构建的应用。这里和那里有一些关键的区别,我们在这个应用中展示了一些简洁的 CSS 属性,例如text-shadow
CSS 属性,它允许您在客户端浏览器中对文本应用一些非常奇妙的阴影效果。这并不依赖于让一个设计者用新的花哨的文本来制作一些过度压缩的图像,就像我们在过去所做的那样(过去,我指的是几年前!).
清单 3–2 涵盖了在我们的应用中使用的常用元素的间距和字体大小的基本重置。
清单 3–2。 CSS 为我爱火腿——第一部
`/*** RESET ***/
html, body, header, section, div, p, ul, li, h2 {
margin:0;
padding:0;
}
ul, li {
list-style:none;
}
/*** GLOBAL CSS ***/
html, body {
width:100%;
height:600px;
overflow-x:hidden;
}
body {
font-size:14px;
font-family: 'Droid Sans', Arial, Helvetica, sans-serif;
background:#993366;
}
h2 {
font-size:18px;
color:#fff;
text-shadow:1px 1px 5px rgba(0,0,0,0.9);
}`
我们的h2
标签将成为我们text-shadow
的幸运接收者。如果您查看这个应用的 HTML 标记,您可以看到这个h2
标签包含了一个将呈现给用户的文本简介。根据游戏的当前状态,文本将改变多达四次,并将由应用的 JavaScript 更新。如前所述,如果我们用老方法做这件事,我们将不得不创建四个包含我们想要的文本的单独的图像,或者在需要时手动将这些图像插入页面,或者根据需要将它们作为背景图像分配给要分配给我们的 blurb 容器的 CSS 类。虽然这并不难做到,但这需要编写更多的代码,并在设备上加载更多的图形资源,如果用户在一个数据连接缓慢的区域,可能会导致明显的延迟或导致用户在情感上与你的应用断开连接!
在清单 3–3 中,我们将设置列表项:
清单 3–3。 我爱火腿的 CSS 第二部
`/*** SECTION ***/
section {
margin:20px 10px;
padding:10px;
}
sectionul {
width:100%;
margin:25px 0;
}
section li {
margin:10px 0;
padding:10px 5px 10px;
min-height:22px;
line-height:32px;
border-radius:7px;
background:-webkit-gradient(linear, 0% 0%, 0% 100%, from(#a1a1a1), to(#A1A1A1), color-
stop(.6,#8A8A8A));
-webkit- box-shadow:0 0 20px rgba(0,0,0,0.5);
box-shadow:2px 2px 10px rgba(0,0,0,0.8);
}`
你也会注意到我们对 CSS 中的列表项做了一些不同的处理。这里,我们给按钮添加了一个漂亮的渐变背景,让它们看起来更有魅力。不同于之前我们在第一个应用中创建的渐变背景,这一个使用了三色渐变系统来给它更多的斜面效果。此外,就像我们的text-shadows
一样,使用 CSS 有助于减少浪费我们客户的资源,例如加载我们不使用的图像。我知道我确实在反复强调这一点,但那是因为这是一条必须遵守的真正重要的规则!永远不要不小心浪费你客户的资源,尤其是当你生活在一个古怪的数据上限或缓慢的数据连接的世界里。最后但同样重要的是,在我们的 CSS 文件中(参见清单 3–4,我将向您介绍两个非常方便的 CSS 选择器来让您熟悉一下:beforeand :afterselectors
。
清单 3–4。 我爱火腿的 CSS 第三部
`sectionli.fail {
background:#990000;
color:#fff;
}
sectionli.win {
background:#009900;
color:#fff;
}
sectionli.fail, section li.win {
border-bottom:1px solid #fff;
}
sectionblockquote {
margin:0;
padding:0;
}
sectionblockquote:before {
content: "\201c\ ";
font-size:26px;
float:left;
margin-right:5px;
padding-left:5px;
}
sectionblockquote:after {
content: "\201e\ ";
font-size:26px;
margin-left:5px;
padding-left:5px;
}`
现在,这些选择器很简洁,因为它们允许您在它们所附加的元素之前或之后添加内容。在这里,我将它们附加到 blockquote 元素,这是一个较老的 HTML 元素,用于在语义上将该内容标记为应该在页面上脱颖而出的引用。
现在,如果您查看content
属性,您可能会有点困惑。在大多数情况下,人们通常会在这个字段中添加一些有意义的东西,比如一个“>”来代替无序列表中的项目符号,或者在链接到一篇文章的内容简介后添加一个省略号。然而,没有多少人知道,您还可以在这个属性中包含十六进制代码,以显示键盘上不常见的字符,例如我想在我的blockquote
内容的开头和结尾添加的引号!
JavaScript
既然我们已经很好地理解了所有东西是如何布置的,那么是时候开始研究如何将所有的齿轮、滑轮和链轮放在一起,并让这个应用正常工作了!首先,就像在我们之前的应用中一样,我们首先在这里声明将在整个应用中使用的全局变量。在 JavaScript 中,理解使用全局变量和局部变量的区别是非常重要的。
简而言之,我想花点时间强调这是最基本的解释,局部变量是一个可以在函数中使用,但不能在不同的函数中使用或作为脚本的一部分的变量。功能基本上是可以重用的“捷径”。例如,我可能会创建一个函数,对给定的输入(称为“函数参数”,即在函数调用后放入()中的信息)进行一些数学运算,并在整个代码中重用该函数。局部变量在功能块内部使用,但在外部不可访问。就这些变量而言,把函数内部想象成一个与代码其余部分分离的小世界!
另一方面,正如您现在可能已经猜到的,全局变量是全局可访问的,因此您可以在 JavaScript 文件中的任何函数或对象中调用它。虽然有些人会嘲笑全局变量的使用,因为它们会引起问题。例如,一个全局变量可以从各种函数中更新,这就造成了在调试过程中变量如何变化的混乱。但是,为了学习,由于这是一本针对初学者的书,我们将在我们的示例中使用它们。
清单 3–5 设置了一个在整个 JavaScript 文件中使用的全局变量列表,以及一个充满押韵演示选项的数组,让我们的用户开心几个小时...或者分钟。
清单 3–5。Javascript for I Love Ham-Part 1
`/**
* I Love Ham
* @author: Rocco Augusto
* @file: js/main.js
**/
var correct = '';
var choice = '';
varresultLength = 0;
var tweets = [
['I love ham','Earthquake in Japan'],
['Android is awesome', 'I just hit an opossum'],
['Ice cream sandwich','I fell out of my hammock'],
];`
与我们之前的游戏不同,我们将有更少的选择呈现给我们的用户。虽然我很想添加 50 到 100 个不同的押韵例子,但不幸的是,我只能机智地不假思索地创造三个例子。就目前而言,我认为三个是好的,但是如果你在家跟着做,试着尽可能多的放进去!我敢肯定,你们中的很多人比我更机智!
清单 3–6 遍历我们应用的init()
函数,从 API 获取我们的 Twitter JSON 提要。
清单 3–6。 我爱火腿的 Javascript 第二部分
`functioninit() {
window.scrollTo(0, 1);
//reset the correct answer and currentRound array
correct = '';
choice = getRandom();
(′ul,section′).removeClass();('ul').empty();
//find out which item has more search results
for(i=0;i<choice.length;i++){
var position = i;
$.getJSON('http://search.twitter.com/search.json?rpp=100&q=' +
choice[i].replace(/\s/g,'%20') + '&callback=?', function(data) {
//check the length of results for each search
//then set them to the "correct" variable
correct.push(data.results.length);
});
}
// generate the buttons for this round
buildQuiz();
}`
这里有我们的init()
函数,它将在 DOM(文档对象模型)加载后在应用中被调用和运行。当使用 Twitter API 时,你可以通过搜索发送几个参数。让我们快速浏览一遍!
- 告诉 API 我们希望每页返回多少结果。在这种情况下,我们每次搜索最多获得 100 个结果。
Q
是容纳我们的搜索查询的参数。Callback
被触发以确保 API 以 JSONP 格式发回数据,这样我们就不会遇到任何跨域安全问题!
你会注意到上面的另一件事是搜索查询中的 I,我们在那个时候调用这个术语进行搜索。在那一部分,我使用了replace()
方法在我们的字符串中搜索所有空格,并用%20
替换它们,这是一个空格的 URL 编码版本。这使得搜索可以通过普通的 HTTP 请求发送,而不会弄乱网页真正指向的位置。为了更好地理解为什么这是必要的,想象一下——有人给你发了一个[
someawesomesite.com/some](http://someawesomesite.com/some)
的链接。由于 URL 是不完整的,如果你发送链接的人点击了它,那么他们很可能会被导航到一个 URL 为[
someawesomesite.com/some](http://someawesomesite.com/some),
的页面,而 URL 的其他部分会被忽略,但是点击一个看起来像[
someawesomesite.com/some%20awesome%20page](http://someawesomesite.com/some%20awesome%20page)
的 URL 会把用户带到他们想要去的地方。始终确保您创建的 URL 中没有中断(参见图 3–2)!
图 3–2。 注意前面来自 Android Thoughts 的 URL 使用连字符而不是空格来呈现给用户一个更加友好和人类可读的 URL
从 Twitter 获取 JSONP 数据后,我们将解析data.resultsfield
的数据,然后获取该数组的长度、其中的记录数,然后将它们推送到我们之前创建的正确数组中,如清单 3–7 所示。
清单 3–7。Javascript for I Love Ham-Part 3
`// this function will grab a random user from
// the tweets array.
functiongetRandom() {
var l = tweets.length; // grab length of tweet array
var ran = Math.floor(Math.random()*l); // grab random user
return tweets[ran];
}
functionbuildQuiz() {
var h = '';
for(i=0;i<choice.length;i++){
h += '
h += '
' + choice[i] + '';
h += '
}
// write buttons to the page
$('ul').html(h);
}
$(function(){
// run the init() function and start the application
init();
// check for correct user on user selection
$('ul li').live('click', function() {
var id = $(this).index();
var not = (id == 0) ? 1 : 0;
var result = '';
// restart the game if game over
if(\(('ul').hasClass('gameover')) {
init();
\)('h2').text('Which one of these fabulous tweets has more search results?!');
}
else {
if(correct[id] > correct[not]) {
//congratulate the player
result = 'Congratulations! you are a total rock star!';
\(('sectionli:eq('+ id + ')').addClass('win');
}
else if(correct[id] == correct[not]) {
//if it is a tie
result = 'It is a tie! You\'re a winner by default!';
}
else {
//shame the player into playing again
result = 'Boo! You failure!';
\)('sectionli:eq('+ id + ')').addClass('fail');
}
// addgameover class to page
(′ul′).addClass(′gameover′);('h2').text(result + 'Tap a button to play again!');
}
});
});`
在我们继续之前,值得一提的是,我们在“你是赢家”一行中加了一个反斜杠。这是因为我们使用了撇号,JavaScript 通常将其解释为字符串的结束。通过在它前面放置反斜杠,我们已经“转义”了它,告诉 JavaScript 对后面的撇号使用“替代”含义(在这种情况下,替代含义就是简单地将其视为字符串的一部分)。
其余的函数与我们已经创建的非常相似,所以我们将跳过它们,直接跳到控制按钮被按下时会发生什么的代码。在这段代码中,我们获取被按下按钮的索引,或者无序列表中的位置。然后,我们将这个搜索的值与另一个搜索的值进行比较。如果你的选择是数字较大的那一个,那么你就赢了,页面顶部的广告会变成一条祝贺你成功的好消息,或者如果你选择得不好,会变成一条失败的消息。在我们的逻辑语句中,我们也有在平局和两次搜索得到相同票数的情况下该怎么做的说明。
现在继续保存您的工作,启动您的浏览器,运行您新设计的应用(参见图 3–3)!虽然这很简单,但我相信您已经开始了解如何使用 HTML5、CSS3 和 JavaScript,实时创建一个非常有趣且响应迅速的 web 应用了!
图 3–3。 我喜欢在安卓 2.3 设备上运行的火腿
总结
不算太寒酸,如果我自己这么说的话!我们设法用一点代码和一个古怪的想法,很快就把它变成了一个好看的概念验证。我们已经展示了另一个通过 JavaScript 利用 Twitter API 的例子,并且我们已经建立了将它扩展到其他方面的平台——不仅仅是搜索 Tweets,还可以使用趋势主题和提及。只要再多花一点时间和精力,我们就可以在这个非常基础的 Twitter 应用的基础上,把它变成一个成熟的游戏,从我们用户的生活中吸取时间!
四、应用的基本规划和结构
到目前为止,我们已经让您直接开始编写一些应用,给了您一些关于屏幕分辨率、JavaScript 和 CSS 的背景信息,并且我们给了您一大包免费(而且便宜)的“东西”,以各种资源的形式供您在开发时使用。有了这些信息,你就不难完全疯狂地编写各种各样的好东西,并把它们公之于众。唯一的问题是,如果你不真正考虑你在构建什么,这个世界可能不会太热衷于实际使用你已经构建的东西。没有什么比在一个只有你(也许还有几个朋友)会看到的项目上花费大量时间更让人沮丧的了。
在这一章中,我们将以两种不同的方式来谈论应用开发:规划你的应用,这涉及到理解你正在构建什么,以及如何确保你不会忽略重要的部分;结构化你的代码,这是一个更简单的想法,但同样重要的原因我们将讨论。这是“将秩序带入混乱”的章节,我们希望你能注意到我们的警告和建议!
了解你的听众
如果你对创造东西有任何热情,那么你可能有过这样的经历:“我希望我有一个工具,它可以[在此插入机智的想法] …我知道,我只是要建造它!”此时,您很有可能出去为自己的使用一起做了一些东西,然后意识到其他人也可能对使用它感兴趣。我知道,每当我创建一个新的 web 或移动应用时,我总是这样做,为我自己的需要或有我这样需要的人而构建。也许这些人是你的观众。
或者也许你只是那些能看到别人看不到的问题的人之一。你知道,如果你把某样东西放在那里去解决某个问题,其他人会抓住不放,称赞你的名字。也许他们是你的观众。
或者最后,也许你是一个创新者,认为你已经进入了下一个伟大的游戏、服务或程序,人们会吵着要用。如果你付出一点努力(也许只是 50-100 个小时的编码),你可以看到上瘾的大众的金钱符号在增加——他们当然是你的观众,对吗?
好吧,也许你的激情、独创性和创新就足够了,观众就会神奇地出现。但是让我们思考一下这个问题。在前面的每个例子中,你假设你的激情、问题和快乐会延伸到其他人身上,这是一个相当大的飞跃。如果你在一家公司,向其他人推销这些产品,第一个问题会是,“我们怎么知道我们有受众?”简单地回答“我会买”是不够的。你必须知道你的目标是谁,他们是否感兴趣。
给人们想要的东西
有很多方法可以让你开始研究你的下一份申请。有时候,一个需求突然出现在你面前,或者你看到了一个明显的机会。其他时候,你可能认为某样东西会很受欢迎,但不知道它是否真的会受欢迎。虽然你可以在 Twitter 和脸书上寻找可以补救的抱怨,或者利用某种调查来了解你的观众想要什么,但我们将在这里介绍一个非常简单的方法,它符合一个普遍的规则:知道人们在寻找什么。下面的方法应该能让你了解人们是如何花时间上网的,从而让你知道你在哪里有机会。
我们从网络上流行的广告系统开始,谷歌的 Adwords。注册是免费的,即使你从来没有投放过一个广告,Adwords 提供的一个你可能会发现非常有用的工具是外部关键词工具([
adwords.google.com/select/KeywordToolExternal](https://adwords.google.com/select/KeywordToolExternal)
)。这个工具告诉你有多少人在谷歌上搜索特定的关键词。这是一个很好的方法来看看人们是否真的对你想做的东西感兴趣。
例如,我考虑了一段时间的一个项目(从某种意义上说,实际上已经建立了)将是一个为有抱负的作家磨练技能的网站。我设想一个网站,在那里一个人可以得到一个主题,写一些散文形式(短篇小说,散文,诗歌等)。)并让其他人投票。题目的获胜者会得到一些“荣誉”,也许会赢得真正的奖励。
我怎么知道这个有没有市场?嗯,我可能会使用关键字工具来做一些简单的搜索,就像图 4–1 中的那些一样,搜索关键字“写作比赛”和“写作帮助”
图 4–1。 谷歌关键词搜索“写作大赛”和“写作帮助”
人们应该记住,关键字搜索是为广告商设计的,所以对“营销 SEO 语言”这种不友好的混乱语言进行一点解读是很重要的。首先,我知道“写作竞赛”的流量(按谷歌每月搜索量计算)是“写作帮助”的 2.5 倍。这是有用的信息,因为它表明有更多的人寻求竞赛和认可,而不是对他们的作品的建设性评论。如果我的网站或应用将同时具备这两个元素,那么竞赛的角度可能会更好。然而,如果我不完全确定我的应用或网站会是什么,我可能会发现关键词创意列表(图 4–2)特别有用:
图 4–2。 上面为搜索词的部分关键词创意列表
这里的“竞争”一栏可能有助于告诉我去哪里。根据 Google Adwords,你会看到“竞争”是指当人们搜索列出的关键词时,有多少其他广告商试图让他们的广告出现。对于“写作竞赛”(或者列表中更靠后的另一个项目,“免费帮助撰写商业计划”),我会有激烈的竞争,但对于“论文写作竞赛”(或者另一个未显示的项目:“说服性写作竞赛”)竞争很少。
使用 Adwords 中的外部关键字工具,我不仅可以了解人们在搜索什么,还可以了解其他公司在做什么广告。这可以帮助你更好地了解你的产品是否有受众(通过使用你怀疑他们会在谷歌中使用的关键词),以及是否已经有很多你要面对的替代品。虽然口碑广告对某些产品很有效,但你不应该低估搜索流量的力量!
谁是我的用户?
假设你知道你的产品有受众,你现在必须弄清楚谁是你的用户。最近,在参加一个充满光明、年轻的未来 MBA 高管的课程时,我惊讶地发现,他们对“瞄准”或找到他们的利基用户并专门针对他们进行营销的想法感到恼火。他们对不向“每个人”营销的想法犹豫不决,没有意识到:a)“每个人”可能都不感兴趣,b)目标用户比一般的“每个人”更有动力实际使用你的产品。
了解你的用户最好的方法是研究你自己的产品(在其早期阶段)或者其他已经存在的产品或网站。如果你正在开发一个应用来跟踪通勤交通的到达和离开时间以及延误,也许可以看看竞争应用或服务的目标是谁。很有可能你的目标是同一个群体,所以花些时间和精力考虑这个群体对产品的需求是值得的。
让我们通过一个例子来讨论这个问题,这个例子是我前面提到的虚构的 transit 应用。确定受众可能很困难,您可能需要考虑不止一组人。忙碌的通勤者或商务旅行者似乎是一个合理的考虑群体。如果我想瞄准这个群体,我可能会问并回答几个问题,例如:
- 我的应用或网站中的哪些功能会吸引忙碌的人?
- 我的应用中的哪些功能、细微差别或程序会让快速移动的人慢下来?
- 我的应用有哪些对使用公共交通工具的人有用的功能?
- 我怎样才能方便地向忙碌的用户发出信号,告诉他们有重要的事情正在发生(例如,一辆晚点的公共汽车或火车)?
- 我的应用将如何使用?它会被用来计划一次旅行,还是在最后一刻改变路线?还是两者都有?
这个列表可以从这里开始,但是你得到了图片。通过开发一个假设的“用户”,并设身处地地为他/她着想,你可以更好地了解你需要做些什么来让他/她的生活更轻松。让它变得更容易,你将获得一个用户或客户!然后简单地对你的下一批人进行同样的研究,也许这次是“休闲旅行者”或“游客”
并不是所有的研究都必须在你的头脑中进行;资源的存在是为了挖掘你的潜在用户在说什么。或许看看你潜在竞争对手的评论,看看人们对这些应用的爱憎。您还可以查看用户组和论坛,甚至进行简单的 Twitter 搜索,以了解特定领域的“问题”。
正如我在前面几段提到的,您可能也已经准备好了这些信息。如果你过去开发过一个网站或应用,你可能用过 Google Analytics ( [
google.com/analytics](http://google.com/analytics)
)或 Flurry Analytics ( [
www.flurry.com](http://www.flurry.com)
)这样的产品。这些产品可以跟踪谁使用了您的应用,并为您提供宝贵的信息,您可以在以后使用这些信息来定义您的“用户”
例如,在我写的一个应用中,我有一个简单的表格,人们可以用它来告诉我更多关于他们自己的信息。从那里,我发现我的应用的平均用户年龄在 25 到 34 岁之间,如图 4–3 中的所示。
图 4–3。 申请的年龄分布
这些信息在很多方面都很有用。首先,我可能会考虑这个团队会觉得重要的特性,并关注未来版本中的特性。或者,我可能会将小额广告预算从青少年转向 20 岁出头的市场,更多地关注 25 岁至 34 岁的人群,以吸引新用户。我对我的用户了解得越多,我就能更好地理解他们的需求,我的网站或应用就能更好地为他们服务。
不过需要注意的是:您可能注意到我的应用只有 17%的用户的已知信息。碰巧的是,我的应用中记录这些信息的部分可能对某些人口统计更有用,这可能会扭曲我的数据。在不深入讨论这个特定程序的情况下,最好注意不要依赖于一个来源,尤其是如果您只有少量数据,或者觉得您的样本可能有偏差。
一旦我从各种渠道获得了关于我的受众和用户的信息,我就可以制定一个计划。
有计划的
作为一个渴望游戏、建设和盈利的新开发者,规划很可能是最容易被忽视的事情。在这一节中,我们将讨论构建什么类型的应用,应用应该是什么样子,以及用户应该如何浏览它。通过从一开始就理解所有这些,我们可以保持我们的优先次序,并且在编码时,简单地实现我们已经解决的问题。让我们先来看看我们可以构建的不同类型的移动应用或界面。
所有手机都不一样
这本书的标题包括“web 应用”这几个字,这本质上承认了还有其他种类的应用。让我们来谈谈移动界面或应用的三种类型。
本机应用
如果你已经拥有或使用了一部安卓手机(如果你正在阅读这本书,我希望你已经拥有了),你可能已经使用了手机上的一些应用。web 浏览器是这些应用中的一种,联系人应用是另一种,计算器应用又是另一种。这些 Android 应用通常使用 Java 编写,并使用称为 Android 软件开发工具包(SDK)的特殊工具进行编译。这些工具可以免费下载和使用,用来创建可以在 Android Marketplace 上发布的应用。
这些应用被称为“本地”应用,因为它们直接从电话上执行。用户有一个专用的图标,点击它打开应用(见图 4–4)。这些应用可以轻松地在手机上存储数据,如果用户允许,还可以相互连接。通常,它们必须通过市场或出版商的网站下载和安装,但也可以通过在手机上下载和运行应用的 APK 包来“侧装”或安装在设备上。
图 4–4。 FAILboard Pro:原生安卓应用
本地应用往往速度更快,因为应用的所有部分都存在于手机的内存中,而且它们还可以更深入地访问手机的硬件,这是网络浏览器所不具备的。出于同样的原因,本地应用会给最终用户带来风险;刚刚安装的应用可能真的是一个试图窃取信息的特洛伊木马,或者开发人员急于推出更新,没有注意他们正在做什么,留下了一个安全漏洞。这类似于 2011 年 Skype 发生的事情,当时有人发现用户名和密码以未加密的人类可读文本保存在用户的手机上,任何人都可以轻松检索。幸运的是,很快就有了解决问题的方法。对于开发人员来说,开发一个原生应用可能需要更多的时间,也可能需要更陡峭的学习曲线,尤其是如果开发人员已经精通 HTML、CSS、JavaScript 和其他 web 技术。
移动网站
移动网站,或为移动设备优化的网站,通常在用户直接导航到该网站或常规网站后被用户浏览,常规网站将用户重定向到移动版本。这些仍然是网站,通常不会在用户的设备上存储数据或与之进行非常密切的互动。从本质上说,他们只是在那里做一个演示,并不是真的与你的用户进行激烈的互动。这通常是公司或网站改善移动用户体验的最基本的方法。移动网站允许你进行一些基本的操作,但是除了提供一些简单的文本页面和一两个表格之外,不与你交互。
正因为如此,这些网站缺乏桌面上的“完整功能”(即完整的网站),也可能不是特别适合智能手机或平板电脑屏幕(例如,平板电脑可能会看到过大的版本,智能手机可能会看到过小的版本,这取决于网站针对的是哪种设备)。虽然开发人员可能喜欢这种方法,因为它的创建、更新和测试相当简单,但最终用户并不认为这些移动网站是应用。如果你的目标是构建一些交互性的东西或者被看作是一种工具,那么你可能会想要避免这种方法的简单性,而倾向于 Web 应用。
Web 应用
最后,我们将讨论前两种应用的联合,以及我们在本书中开发的应用的类型。Web 应用试图将本地应用的无缝性与来自移动网站的相对容易的编码和更新结合起来。例如,我们在本书中的早期应用(见第二章第一章和第三章第三章第三章)有用户可以轻松按下的按钮,就像图 4-4 所示的原生应用一样,但是存在于网络浏览器中。如果用户要创建应用的快捷方式(或者应用本身可以询问用户是否愿意创建一个快捷方式),那么应用可以像本地应用一样拥有自己的图标。也有一些技术可以让 web 应用像本地应用一样将自己集成到操作系统中。
最好这样想:web 应用,就像普通的应用一样,是用来交互的,而不仅仅是浏览的。
那我该造哪个呢?
现在,你可能在想你真正想去的方向。老实说,虽然这本书关注的是 web 应用的好处(因为它们通常比本地应用更容易编程,比移动网站功能更全,并且初学者也可以访问),但您可能无法只使用一种解决方案。我们再次提到了解你的用户的想法。如果你的用户所在的区域总是有手机服务,那么也许一个 Web 应用会像本地应用一样有用。如果用户使用各种不同的设备(例如,Android、Windows Phone 或 iOS powered),那么 web 应用可能是同时在这三个平台上运行的最简单的方法。
最后,你必须发展你的移动策略来适应你的用户。在本书的后面(第十三章),我们将告诉你如何“欺骗”一点,只需要很少甚至不需要工作就可以将一个 web 应用转换成一个本地应用(从而让你至少拥有一个 web 应用和一个 Android 本地应用)。
一旦你选择了如何构建你的移动战略,你就应该考虑避免直接投入并编写代码的冲动。一个好的开发人员会规划他或她的应用,无论是本地的、基于 web 的还是移动网站。这一步可以按照构建应用的思想进行分组,我们将在接下来讨论。
构建您的应用
在这一节中,我们将讨论从头开始设计应用的过程。这采取三个步骤的形式,线框图或模型阶段,图形设计阶段,和代码构造阶段。先说设计。
线框/实体模型
有了一个想法,然后立即开始编码,而不考虑最终产品会是什么样子,这是非常诱人的。这可能是个坏主意,原因有很多:
- 它可能会让你陷入一个最终看起来并不那么好的设计中(例如,它太杂乱了)。或者你可能意识到,如果你没有直接创建按钮和框,你的设计就不会有效率!
- 它可能会阻止你创新,因为你不“想”改变事物来增加新的特性。
- 它可能会阻止你征求反馈,直到项目结束,那时改变事情可能太费时间了。向用户展示你的设计,看看他们是否觉得直观,然后调整一个图形,比向他们展示一个应用,然后重新编码以使设计更好更容易。
为了防止这些事情,许多设计师创建他们的应用的线框图表或“模型”。例如,我们之前讨论过的一个非常粗糙的交通应用模型可能是这样的(Figure 4–5)。
图 4–5。tran zit 应用的原始模型
正如我提到的,这是一个相当粗糙的模型;事实上,它甚至可能不是我最终拥有的设计的. 0001 版本。不过,在模仿它的过程中,我确实意识到了一些事情。我制作了运输时间、提醒和偏好的按钮;我为警报留出空间;我甚至在底部留出了一些空间做广告。然而,我还有另一个屏幕,标记为“E”,我不知道我可以用它来做什么。有了模型,我可以简单地删除和重画,以获得正确的。但是,如果我在现实生活中开发它,我可能会在那里留下一片空白,直到它接近 1.0 版本时才真正意识到这一点。
显然,你可以随意创建模型。图 4–5 是用我平板电脑上的一个应用创建的,这个应用可以让我徒手绘制(你可能已经看出来了)。人们也可以在 web 编辑器中用实际的图像、按钮等等来设计一个模型(即使按钮实际上不会工作)。另一种选择是使用 PowerPoint 或 Keynote 等程序,在这些程序中很容易绘制方框、制作动画和创建许多类似的绘图。
HotGlooand 和其他模型工具
如果你正在寻找一个基于网络的工具来创建你的模型,那么你可能想看看 HotGloo。产品主页如图 4–6([
www.hotgloo.com/](http://www.hotgloo.com/)
)所示。这个工具附带了一个月订阅,可能是我用过的最好也是最容易使用的线框工具之一。我用过的一些线框图工具只为你提供了基本的线框图,但是 HotGloo 允许你同时做很多视觉设计和线框图,节省你的时间。
你可能想考虑的其他产品包括 lucid chart(www.lucidchart.com
)和 Pencil Project ( [
pencil.evolus.vn](http://pencil.evolus.vn)
),它们可能会集成到你已经在使用的现有工具中,比如 Microsoft Visio。
图 4–6。hot gloo 网站,我们可以有把握地认为它是在 HotGloo 被框住的
用户移动:导航还是故事板
一旦你完成了你的线框,是时候坐下来把这些点连接起来,弄清楚你将如何在你的网站上构建内容,绘制出所有的页面和它们所属的部分。这个过程的这一部分被称为站点地图,如果你花时间去做的话,会非常容易和有趣。如果你的应用不是特别线性的(例如,也许是一个游戏),你也可以把这看作是故事板。用最简单的话来说,就是以一种有组织、有逻辑的方式来布局内容。
创建地图时,最好尝试在相似的部分或组中保留相似的内容。这将使构建应用变得更加容易,因为您一眼就能看出应用中的哪些页面需要相互关联。在一个游戏的例子中,把这想象成以一种逻辑的方式呈现你的信息:把所有的“黄金”和你可以用它购买的东西放在一起,或者所有的“生命点”和影响它们的增强功能放在一起,等等。
看看下面的图 4–7 可以更好地了解传统网站地图的样子。注意到它和我们在文法学校被迫做的那些家谱项目非常相似吗?嗯,这是非常相同的原则。站点的父页面(或部分)都有与该部分或类别相关联的子页面。
图 4–7。 演示应用的简单站点地图
对于这个网站地图,我使用了一个图形设计工具来创建这本书的模型;但是每当我在做一个个人使用的项目时,或者当我坐下来和一个想快速看到一些东西的客户在一起时,我会在一张餐巾纸或一张笔记本纸上画出网站地图和线框。
尽你所能创建你的网站地图。不管它是如何创建的,重要的是你要坐下来,仔细考虑你的页面是如何组织的,以使你的构建和开发更容易。
任何开发人员都不想做的最后一件事就是完成一个项目的 80%,却不得不回去重新做一些事情,因为由于缺乏适当的规划而犯了一个错误!
既然我们知道了应用的外观和内容是如何组织的,我们就可以开始编码了。让我们用本章的最后几页来讨论如何最好地构建我们的开发环境以获得最大的生产率。
构建你的发展
有了这些高级信息,你还需要考虑一些实用的开发规则,让你在编写应用的时候保持有条理和高效。接下来,我们将讨论一些组织你的代码和保存它的目录的想法。
代码结构
现在你已经完成了你的线框图,并且对你想让你的页面上的所有元素去哪里有了一个粗略的想法,是时候开始弄清楚你将如何组织你的代码,这样你就不会在构建你的应用时做更多的工作。
你现在可能正在挠头想,“这能有多难?我只是在一页纸上写文字。”你可能是对的。然而,假设您的应用中有十个不同的页面。您对 web 应用进行了更新,并意识到您的页脚有问题。
如果准备不充分,我们不幸的开发人员可能会在组成应用的十个不同的 HTML 页面中复制并粘贴同一个页脚。现在是时候更新代码了,我们虚构的开发人员必须用新的页脚信息更新十个不同的 HTML 文件。
正确构建这段代码的一个更简单的方法是获取页脚信息,它在整个站点中都是相同的,并为它创建一个单独的文件。然后,在您的代码中,您可以将该文件包含到您的页面模板中,并像以前一样在每个页面上显示它。如果您将来不得不更改代码,使用这种方法,您只需在一个文件中更改代码,这会让您的工作变得更加轻松。
让我们看看下面的例子。在清单 4–1 中,我们有一个非常稀疏的页面,它使用 PHP 来获取我们希望在每个页面上都有的额外页面代码。
清单 4–1。 使用 PHP 包含语句,我们将拉进额外的代码,以包括在我们的页面
`
Fusce luctus accumsan odio. Cras vel sodales mi. Suspendisse et arcu quis magna feugiat ultrices sit amet non erat. Etiam malesuada dui venenatis eros gravida aliquet. Aliquam erat volutpat. Nullam dapibus cursus ultricies. Suspendisse congue accumsan purus non scelerisque. Phasellus ut sapien libero, vel luctus velit.
如果你看清单的底部,你会注意到我正在拉进页面footer.php
(见清单 4–2)。我是通过将我的include
函数包装在<?php ?>
标签中来做到这一点的。这告诉您的页面执行 PHP 代码(如果您的服务器支持的话),并在这段代码中查找与前面列出的文档位于相同相对路径的页脚文档。如果您使用的是我们在本书前面讨论过的统一服务器本地开发服务器,那么这应该非常有用。否则我们可能需要指定一个相对路径;例如,如果我将我所有的“包含”文件放在一个名为“includes”的目录中,我的代码可能是:include('includes/footer.php')
。我们一会儿将讨论目录结构,所以一旦你在这里完成了,你将知道相对路径看起来如何到你的文件。
现在我们知道了使用 PHP 在文档中包含一个文件是什么样子,我们将看看这个 PHP 文件的内部结构是什么样子,这样您就可以看到以一种简单的、可重用的方式来构造您的代码是多么简单。
清单 4–2。footer.php 档案里面的一看
`
`正如您在这里看到的,本文档中的代码非常简单。我们有一个浮动在文档右边的无序列表和一些浮动在文档左边的版权文本。我们将想象在远处的某个地方有一个为这个文档创建的样式表,它使一切都变得很漂亮。
PHP 最酷的地方在于它运行在服务器上,而不是客户端。这意味着你在 HTML 文档中包含的 PHP 代码(无论这是指将其他文档包含到你的模板中,还是自定义创建的类、函数等。)将永远对客户端可见。当客户端加载页面并查看源代码时,他或她看到的只是一个无缝的 HTML 文档(参见清单 4–3)。
清单 4–3。 在浏览器中查看我们的人造文章会显示一个完整的 HTML 文档,没有服务器端 PHP 代码
`
Fusce luctus accumsan odio. Cras vel sodales mi. Suspendisse et arcu quis magna feugiat ultrices sit amet non erat. Etiam malesuada dui venenatis eros gravida aliquet. Aliquam erat volutpat. Nullam dapibus cursus ultricies. Suspendisse congue accumsan purus non scelerisque. Phasellus ut sapien libero, vel luctus velit.
文件夹结构
在前一个例子中,我们讨论了将文件分割成小的结构,使它们更易于管理。对于更多的文件,您可能还想创建目录来存储按功能分组的特定文件。
在图 4–8 中,我为一个虚拟应用创建了一个模拟文件夹结构。就像在你的文档中分离你的代码一样,整齐地安排你的文件夹结构也是一个很好的实践,这样你就可以在你的应用的整个开发过程中很容易地找到你需要的文件。
图 4–8。 在 Adobe Dreamweaver CS5.5 的文件浏览器中看到的组织整齐的模拟文件夹结构
这里我们坚持一个非常基本的结构,其中所有的资源目录(比如我们的css
、img
和js
文件夹)都在站点的文档根文件夹中,并且有一个template
文件夹将保存我们的 web 应用的不同页面的模板代码。
在 template 文件夹中有一个 includes 文件夹,我们将使用它来存放在整个应用中使用的文档,例如页眉或页脚的标记代码,甚至可能是一些随机的内容块。参考我们在上一节中关于相对路径的例子,在图 4–8 中的index.php
文件中的包含行将引用页脚文件为include('template/includes/footer.php')
。这不仅保持了事物的分离,也具有逻辑意义。页脚是模板的一部分,它包含在几乎所有内容中,因此它位于模板目录中的 includes 目录中。
现在,当需要开发我们的应用或在未来进行更新时,我们不必试图记住所有代码的位置,因为我们可以很容易地从逻辑上一眼就找到它。
如果我们想编辑联系页面上的信息,我们只需编辑位于/template/contact.php
的模板文件。如果我们发现头文件有问题,我们可以很容易地跳到/template/includes/header.php
中,对我们心中的内容做任何我们需要的更改,然后让这些更改在整个应用中得到反映。
总结
希望现在你已经到了另一个章节的结尾,在实际编码开始之前,你对创建一个 web 应用,甚至是一个 web 站点的思维过程和工作有了更好的了解。如果你在一家代理公司或工作室工作,或者其他以开发人员为中心的企业,那么你可能会在一个团队中,每个人负责自己的任务,并在交给你作为开发人员之前负责计划的不同部分。(在测试团队、开发里程碑、构建等方面,您可能还有更多。然而,如果你是为自己工作,你现在知道该怎么做才能让你的生活变得更容易忍受,让你的工作量变得更整洁。
五、使用 CSS 3 处理多个屏幕分辨率
设计网站时最难处理的事情之一是你可能会遇到无数不同的用户配置。现代 web 浏览器和操作系统是高度可配置的,不同的选项会影响您的 web 应用的查看方式。在这一章中,我们将专门讨论屏幕分辨率——或者屏幕上的像素数量——以及如何使用 CSS 3 适应不同的分辨率设置。
眼睛疲劳和分辨率进化的历史
如果你曾经在深夜使用电脑,也许是在一整天的工作之后,你可能会发现一个有用的技巧:你可以改变屏幕上对象的大小,使你的眼睛更容易阅读它们。这通常包括更改计算机的显示分辨率。分辨率就是屏幕显示的像素数量,由两个数字表示,第一个是像素宽度,第二个是像素高度(即高度)。通过改变分辨率,最常见的是将其从较高的分辨率,如 2560 × 1600(顺便提一下,我在晚上 7 点输入本文时使用的分辨率)缩小到较低的分辨率(可能是 1600×1200——一个不错的 11 点分辨率),您会带走单个像素,这意味着您屏幕上的项目会显得更大。
20 世纪 90 年代末,随着越来越多的人开始上网冲浪,标准分辨率被称为 VGA(视频图形阵列),通常设置为 640 × 480。随着时间的推移,显示器变得更大、更便宜、更高效,大多数用户都采用了 SVGA(老实说,S 代表超级!)!),在 800 × 600。这让位于 1024 × 768 和更大的 XGA(扩展图形阵列——谢天谢地不是 Xtreme)。今天我的显示器显示的是前面提到的 2560 × 1600 的 WQHD(宽四高清)。
你可以想象,自从 640 × 480 不再流行以来,网页设计师们已经为屏幕分辨率奋斗了很多年。他们最终分成了两个阵营——一派支持为一种分辨率设计(“固定宽度”),另一派支持为多种分辨率设计(“灵活宽度”)。固定宽度的人认为,最好的网页设计是你绝对确信所有用户看到的你的网站尽可能接近你设计的方式。这意味着选择一个分辨率,比如 1024 × 768,并设计你的页面来利用这个空间(见 Figure 5–1)。如果用户有一个更大的分辨率设置,你的页面会在两侧有额外的空间,但是表格和位置会保持在你想要的位置,在你的 1024 像素宽度内(见图 5–2)。如果用户的分辨率非常小,那么你的内容将会占据屏幕的大部分空间,他们将会被迫使用非常可怕的水平滚动条。
图 5–1。 分辨率为 1024 × 600 的固定宽度网页示例
图 5–2。2048×1536 分辨率的同一页面。注意两边的宽黑条
相比之下,灵活宽度的布局将试图适应所有尺寸的分辨率(参见图 5–3 和 5–4)。
图 5–3。1024×600 分辨率的可变宽度网页示例
图 5–4。 同一页 2048 × 1536 分辨率
现在,你可能已经开始看着你的智能手机或平板电脑,心想,“嗯……想知道这个东西的分辨率是多少?”你猜怎么着这也是大多数手机网页设计者想知道的。目前,随着 Android 每天激活大约 350,000 个新设备,设计人员和开发人员都意识到他们需要对 web 应用开发方法进行重大改变,以便与我们每天用来浏览 web 的大量设备保持联系。如果你喜欢固定宽度的方法,这是一个很难玩的游戏:我为什么分辨率构建我的移动应用?有一些简单的答案(“好吧,大多数人都有 iPhones,我只是为它而建”),但它们也有自己的缺点(“什么?!?他们做了一个更大的 iPhone,叫做 iPad…呃哦…”)。通过瞄准特定的设备,你也容易惹恼除了你的目标之外的任何设备的用户。
在这一章中,我们将为您提供一点“屏幕分辨率”也就是说,我们将尝试通过讨论您的移动 web 应用如何能够真正智能地理解用户正在使用的设备的分辨率,甚至方向,并自动适应它来解决这个问题。这就好像你将编码一个小大脑,说“嗯,这个设备有很大的宽度;让我们展示我们能展示的一切”或者“哦,不;这是一个小屏幕。我们不能并排显示这些信息!”作为一名设计师,我们可以利用小大脑,让它变得更加智能,让它以各种不同的方式向不同的设备显示相同的内容,进而向不同的用户显示。让我们从一个例子开始。
日常机器人
想象一下,你已经决定开始为 Android 用户发行一份日报,并且想出了一个很棒的标题:每日机器人。唯一的问题是,你们有些用户用的是一个摩托罗拉 Xoom,分辨率 2048×1536;有的在用三星 Galaxy Tab(1024×600 分辨率);还有一些人使用 Roccwest XTreme,出于某种奇怪的原因,它的分辨率为 1280 × 800。如果你的目标是这些设备中的一个,那么使用固定的宽度,另外两个看起来就不太对了。令人欣慰的是,我们可以编写一个在三种情况下看起来都不错的页面——参见图 5–5 到 5–7:
图 5–5。1024×600 分辨率的日常机器人
图 5–6。??【每日机器人】1280 × 800 分辨率
图 5–7。 每日机器人 2048 × 1536 分辨率
将这些数字放在一起比较可能会非常令人惊讶——内容完全相同,但布局发生了相当明显的变化。更令人惊奇的是,这个是同一个页面——我们的开发人员能够创建它一次,并让它足够智能地对这三种不同的分辨率 1 做出适当的反应。
但是我们没有平板的朋友怎么办?嗯,只要做一点点工作,我们也可以用日常的 Droid 智能手机版来容纳它们,如图图 5–8(纵向)和图 5–9(横向)所示。
图 5–8。 每日机器人 480 × 859 分辨率
在这种情况下,开发人员(Rocco)不知道编写叙述的人(Jon)会选择什么分辨率,但页面看起来仍然很棒。
图 5–9。??【每日机器人】859 × 480 分辨率
让我们来看看 Daily Droid 背后的代码,了解页面是如何构建的,以及我们如何确定它应该显示“智能手机”还是“平板电脑”版本。我们将在下一节讨论 HTML,然后在下一节讨论 CSS。通读下面的代码,根据注释,您应该对每个部分的功能有所了解。
每日机器人的基本 HTML 代码
我们已经把 HTML it 分成了几个部分,所以你可以在可管理的片段中浏览它。在第一部分,如清单 5–1 所示,我们创建了初始的 HTML 布局并指定了两个不同的标题。
清单 5–1。 每日机器人的 HTML 代码第一部分
`
The Daily Droid
Smartphone Edition
Tablet Edition
从清单 5–1 中的代码可以看出,智能手机版和平板电脑版文本都出现在 HTML 中。CSS(在本节后面的“每日 Droid CSS”中讨论)将选择正确的一个来显示。在 HTML 的第二部分,如清单 5–2 所示,我们将布局内容和文章空间,然后完成 HTML 结构。
清单 5–2。 每日机器人的 HTML 代码,第二部分
`
<!--
SECTION: FEATURED ARTICLES
The featured article would be the main article on the page
that will be featured. Think of it in this sense as the
front page story on a newspaper - which makes sense since
this demo's completed code would be a mock vintage newspaper
layout.
The article contains several sections. You have the header
section which contains the title of the article as well as
a "figure" or image that would be displayed with the article
and any caption text that would go along with it.
-->
Title goes here...

Article content goes here...
Title...
Body Content...
现在让我们转向 CSS 和日常 Droid 的视觉呈现。
每日机器人的半魔法 CSS 代码
下面是用来设置日常 Droid 整体外观的 CSS 样式表。如您所知,CSS 将采用上一节中解释的普通 HTML 结构,并按照我们的指定对标签应用各种样式。我们将 CSS 分成几个部分来帮助解释它的作用。第一部分,如清单 5–3 所示,设置正文和一些标题的外观。
清单 5–3。 每日机器人的 CSS 代码,第一部分
/* CSS reset code only for the elements that will be used in our code. We could use a more robust CSS reset solution, but I am a firm believer that you should not riddle your stylesheet with code that you have no intention of using in your markup. */ html, body, h1, h2, h6, p, article, figure, figcaption header, hgroup, section { padding:0; margin:0; } /* General global styles to be used throughout the demo */ html, body { width:100%; overflow-x:hidden; } body { font-size:14px; font-family:"Times New Roman", Times, serif; line-height:20px;
` background:url(bg.png);
-webkit-box-shadow: inset 0 -5px 300px rgba(153,99,38,1); /* This inset box-shadow
adds gives the page a nice vintage feel to it /
box-shadow: inset 0 -5px 300px rgba(153,99,38,1);
}
h1, h2 {
font-weight:normal;
}
h1 {
font-size:36px;
line-height:42px;
}
h2 {
font-size:20px;
}
h6 {
font-size:16px;
text-transform:uppercase;
}
p {
margin:10px 0;
}
/
Header/Mast CSS code
*/
mast {
padding:20px 0 00;
text-align:center;
letter-spacing:1px;
}
mast h1 {
font-family:'UnifrakturCook', Georgia, "Times New Roman", Times, serif;
font-size:62px;
line-height:48px;
}`
现在我们已经有了一些 CSS,是时候看看在“平板电脑”和“智能手机”版本之间切换的代码了。默认情况下,我们希望显示 Tablet Edition,因此代码如清单 5–4 所示:
清单 5–4。 每日机器人的 CSS 代码,第二部分
`#mast h6 {
display:none; /* hiding both of the pages subheaders */
}
mast h6:nth-child(3) {
display:block; /* displaying the "Tablet Edition" subheader by default */
}
mast time {
display:block;
margin:10px 0;
border-top:double 6px #000;
border-bottom:double 6px #000;
text-transform:uppercase;
font-size:16px;
line-height:24px;
}
/*
Article/Content styles.
This section will rely heavily on two new features
of CSS3: Flexible Box Model and Columns.
The Flexible Box Model is probably one of my favorite
new features of CSS3. In a nutshell, it allows one to
take control of how their page is laid out, using a grid
of flexible boxes and essentially eliminating the need to
hack together layouts by improperly using floats in one’s
code.
CSS3 Columns are another time saving new feature of CSS3
and allow a designer/developer to take a block of code
and automatically convert it into a column based layout
that is just perfect for a newspaper demonstration.
*/
content {
padding:0 10px;
display:-webkit-box; /* here we are using the -webkit-box argument instead of
plain old "box," so our code will work across newer and
older Android browsers*'
/
-webkit-box-orient: horizontal; / setting the box orientation to horizontal
displays the content in this container from
left to right instead displaying the content
in the traditional way of top to bottom */
}
featured {
max-width:50%; /* our featured article will take up half the width of the display /
height:100%; / our featured article will take up all of the available height of the
display /
box-flex:1; / tell our child elements to be evenly sized and take up one "box"
space */
-webkit-box-flex:1;
}
featured .entry {
-webkit-column-count: 2; /* this will display our featured content article text in 2
columns /
-webkit-column-gap: 20px; / here we add a hearty 20px gap/spacing between our
columns /
-webkit-column-rule: 1px solid rgba(91,58,21,0.5); / here we are adding a border to
our columns */
}
regular {
margin-left:5px;
padding-left:10px;
max-width:49%;
box-flex:1;
-webkit-box-flex:1;
-webkit-column-count: 3;
-webkit-column-gap: 20px;
-webkit-column-rule: 1px solid rgba(91,58,21,0.5);
border-left: 1px solid rgba(91,58,21,0.5); /* here we are adding a border to the
regular container to match the rest of the columns' borders */
}
regular article {
display:inline; /* displaying our articles inline prevents our articles from
stacking on top of each other /
}
article h1, article h2 {
margin-bottom:10px;
font-family:Bevan, "Times New Roman", Times, serif;
}
article .entry {
text-align:justify; / to give the page a more realistic feel we will justify the
column text */
}
article figure {
width:90%;
padding:0;
margin:10px auto 20px auto;
}
articlefigcaption {
font-style:italic;
text-align:right;
}`
现在我们将设置智能手机代码(在清单 5–5 中)并相应地调整布局。
清单 5–5。 每日机器人的 CSS 代码,第三部分
`/*
Android Smartphone Devices
Here we will use CSS3 media queries to determine
the resolution of our screen and present the user with
a completely different layout if their viewing
does not meet certain requirements.
Here we are targeting smartphone devices that will,
on average, have a width of 320px (portrait) and up
to 569px (landscape).
This layout will display the content in a more
commonly used smartphone style layout, presenting the
user with a list of articles that they can scroll up
and down to view.
/
@media screen and (min-width: 320px) and (max-width: 569px) {
body {
-webkit-box-shadow: inset 0 -5px 50px rgba(153,99,38,1); / lessen the shadow on
the page to adjust to the screen's new dimensions /
box-shadow: inset 0 -5px 50px rgba(153,99,38,1);
}
h1 {
font-size:20px; / lower the size of the header font to accommodate the smaller
screen resolution /
line-height:24px;
}
h6 {
font-size:12px; / same as the h1 above it /
}
#mast h1 {
font-family:'UnifrakturCook', Georgia, "Times New Roman", Times, serif;
font-size:42px;
line-height:42px;
}
#mast h6:nth-child(2) {
display:block; / since we are dealing with a smaller screen we will show the
"Smartphone Edition" subheader /
}
#mast h6:nth-child(3) {
display:none; / and hide the "Tablet Edition" subheader /
}
#mast time {
font-size:12px;
line-height:24px;
}
section#content {
-webkit-box-orient: vertical; / here we are telling this content to display
vertically instead of horizontally /
padding-bottom:15px;
}
#featured {
max-width:100%; / take up the entire width of the screen instead of half of it
/
}
#featured .entry {
-webkit-column-count: 1; / only display our text in a single column, which is
more appropriate for our screen real estate /
-webkit-column-gap: 0; / remove the 20px padding around columns /
-webkit-column-rule: none; / remove the border off our columns /
}
#regular {
margin-left:0px;
padding-left:0px;
max-width:100%; / like our featured article we will now take up the entire
width of the page /
-webkit-column-count: 1; / like our featured article we will display a single
column of text /
-webkit-column-gap: 0;
-webkit-column-rule: none;
border-left: none;
}
#regular article {
display:block; / display our articles as blocks so they appear vertical /
}
article .entry p, article figure {
display:none; / hide all of our article content so the user is not stuck
scrolling into oblivion /
}
article .entry p:first-child {
display:block; / display only the first paragraph of an article for the user */
}
article {
margin-bottom:10px;
border-bottom:2px solid rgba(0,0,0,1);
}
}`
对于一份简单的报纸来说,这是很多代码;然而,它的美妙之处在于它的多功能性:一个 HTML 页面和一个 CSS 页面来管理所有设备——手机、平板电脑、Android 媒体播放器,以及其他任何有人安装 Android 的设备!让我们花一点时间,通过媒体提问来讨论这种神奇是如何发生的。
媒体查询
当您查看前面的代码时,您可能注意到的第一件事是,编程的非常流畅的老式报纸布局只包含两个相对较小的代码文件。五到七年前,在一个 web 开发人员必须戴着两顶不同的帽子(web developer hat 和 Internet Explorer 6 hacker hat)的地方,创建这样一个布局会是一个非常麻烦的任务,很可能包含两个(如果不是更多的话)完全不同的布局。每个布局都必须有自己的一组 HTML 和 CSS 文件,由一些后端服务器代码显示给客户端,这些代码查找设备的用户代理字符串。移动设备将被提供专门为移动设备设计的 HTML 和 CSS 模板。台式计算机将被提供等同于桌面的模板。代码版本之间没有融合或流动性。
快进到未来几年——我们正处于 HTML5 和 CSS3 的黎明——前景完全不同。目前,在我们生活的世界里,桌面互联网浏览器的开发和发布速度比大多数用户安装最新更新的速度还要快。在移动领域,它几乎同样进步,智能手机操作系统的新版本,以及相应的互联网浏览器的新功能丰富版本,每六到十二个月发布一次,如果不是更早的话!
这对你,移动 Web 开发者来说意味着你不会停留在过去,为十年前的浏览器编码。作为一个以 Android 用户体验为目标的开发者,你最多只需要担心过去一年半的 Android 版本的开发。专注于新设备而不必担心过多关注过去的技术会妨碍用户的体验,这种能力给开发人员带来了我们以前没有的自由。随着自由而来的是创新,随着创新而来的是更好的技术和功能,如 CSS3 媒体查询,这种技术让我们能够创建我们之前构建的美丽的“The Daily Droid”演示。
自 HTML4 时代以来,媒体查询已经以某种形式成为 web 开发的一部分。在字体标签和嵌套表格的黑暗时代,媒体类型通过控制与用户用来查看内容的媒体相关的样式和样式表而声名大噪(见图 5-10 和图 5-11)。例如,开发人员将为“打印”媒体类型分配一些样式,以从博客上的打印文章中删除所有广告,这样用户在尝试打印博客内容时就不会不必要地耗尽昂贵的墨水(参见清单 5–6)。
清单 5–6。 媒体查询示例 1:基于媒体类型隐藏或显示块
`
` 图 5–10。 在普通桌面网络浏览器中查看的 Android 思想主页
图 5–11。 打印时查看同样的安卓思想网页
开发人员还可以为“投影”媒体类型分配样式或样式表,以解决在会议室的投影仪上观看项目时网站标题中褪色的颜色(参见清单 5–7)。
清单 5–7。 媒体查询示例 2:根据媒体类型更改背景颜色
`
`然而,随着 CSS3 的出现,媒体类型的概念已经演变成更加漂亮和灵活的东西,这使得级联样式表能够不仅仅是一种为 web 文档和标记添加样式的“愚蠢”机制。当使用媒体查询时,感觉它们从一开始就应该是 CSS 的一部分。我听到我的一些朋友把媒体查询称为“自由”,甚至听到我认识的一个开发人员——当然,在一个漫长的编码夜之后——把媒体查询简单地称为数字时代的新“黑魔法”。事实上,它们都不是。媒体查询作为一种工具,当掌握时,将使您作为开发人员的工作更容易,并且在一天结束时,将为您提供一个漂亮灵活的布局,立即转换为用户的查看媒体,就在您的眼前!
如果您过去使用过 JavaScript 或任何其他脚本或编程语言,那么媒体查询对您来说可能很容易学会。基本上,我们所做的是为样式表分配逻辑,这将让客户端的浏览器在将正确的模板样式分配给正确的查看设备时完成所有繁重的工作。以下是使用 CCS 媒体查询的一个非常基本的示例:
<linkrel="stylesheet" media="screen and (orientation:portrait)" href="portrait.css" />
在本例中,当浏览器从垂直位置的移动设备查看应用或网站时,我们告诉客户端的浏览器加载“portrait.css”样式表。 2 不限于向链接标签分配媒体查询。媒体查询也可以直接添加到你的设备的 CSS 文件中,就像我们在前面的例子“The Daily Droid”中看到的那样。事实上,当使用@import 规则调用 CSS 文件以获得正确显示时,甚至可以调用媒体查询,如下例所示:
<style type="text//css"> @importurl(landscape.css) screen and (orientation:landscape); </style>
在下面的例子中,我们将有几个@import 规则导入到许多不同的 CSS 文件中,用于不同的布局类型(参见清单 5–8)。
清单 5–8:媒体查询示例 3
``
2 如果手机从横向模式切换到纵向模式,带有传感器来检测移动的设备通常会告诉软件,允许您的网页根据您定义的风格自动重新格式化。
我完全知道你在想什么,“如果我必须编写我的 web 应用来匹配连接到 16×9 显示屏的电视或设备的 Google TV Android 设备,会怎么样?!"您不必担心,因为 CSS3 媒体查询的灵活性已经涵盖了您的需求,如下例所示:
<link rel="stylesheet" media="all and (device-aspect-ratio: 16/9)" href="television.css" />
从前面的例子中可以看出,在一个由 HTML5 和 CSS3 支持的设备和应用迅速主导的世界中,媒体查询是任何 web 开发人员的武器库中一个不可思议的工具。要了解更多关于媒体提问的力量,请点击这里查看 W3.org 网站— [www.w3.org/TR/css3-mediaqueries/#media0](http://www.w3.org/TR/css3-mediaqueries/#media0).
总结
在这一章中,我们已经讨论了很多关于你的网页的大小,以及这个大小是如何根据用来浏览内容的屏幕而变化的。现在我们已经解决了屏幕分辨率的问题,我们将放大一点,解决处理不同浏览器的最佳方式!
六、处理不同的浏览器平台
到目前为止,我们已经讨论了检测访问您的 web 页面或应用的设备类型以及适当处理它的方法。这涉及到特殊的 CSS 文件、格式问题等等。然而,正如任何优秀的面包师会告诉你的那样,把所有鸡蛋放在一个篮子里并不总是最明智的想法。这就是为什么,在这一章中,我们将后退一步,讨论您可以用来了解什么设备正在访问您的服务并采取适当行动的方法。
原因很简单:没有一种解决方案能解决所有问题。例如,您可能需要允许移动用户访问 web 应用,并且您可能会发现可以格式化页面,使其在桌面和手机上看起来都很棒。对于极其简单的布局,这通常是开发人员所做的。然而,对于更复杂的系统,可能需要创建一个完全独立的网站。中庸之道是构建一个在桌面、平板电脑或手机上都很好看的 HTML 文档。在这一章中,我们会给你一些你需要的工具来判断是什么设备在看你的页面,并且我们会提供一些关于你的页面如何对每个设备做出反应的想法。毕竟,就像没有两个网站是完全一样的——尽管很相似——也没有两个设备的行为是完全一样的(但谢天谢地,就像网站一样,有相似之处)。
元标签和视口
我们已经讨论了许多对于普通用户来说是隐藏的关于 HTML 的事情——使用的不同标签,不同的部分,比如head
和body
,甚至像 CSS 和 JavaScript 这样的东西如何嵌入到 HTML 文档中。然而,元标签或元素是特殊的,虽然它们不会显示给用户,但它们通常会影响用户对页面显示或格式的体验。
META 标签位于 HTML 页面的head
部分,通常有两个元素,元素名和值。例如,以下值指定了与特定网页相关联的关键字:
<**meta** name="keywords" content="android, howto, information" >
一点元历史
当搜索引擎首次开始索引互联网时,他们使用关键字和描述元素来理解给定网页的内容。在 20 世纪 90 年代末,全文搜索引擎(如谷歌)开始索引并使用给定网页的所有文本来确定页面包含的内容,这些META
元素变得不那么重要了。事实上,一个完整的行业已经兴起,声称通过“优化”他们的网页来帮助网页所有者获得更多的流量,这通常包括更新搜索引擎使用的META
元素。这个行业的“搜索引擎优化”,或搜索引擎优化,今天仍然存在,事实上是一个非常残酷的业务类型,虽然许多搜索引擎寻找旧的“技巧”使用,并可能惩罚网页使用它们。例如,使用的一个技巧被非正式地称为“关键字填充”,这听起来就像是——用许多不同的术语填充META
关键字元素,其中一些可能在搜索引擎中得分很高,但在页面的实际内容方面并不准确。现代 SEO 公司必须依靠许多其他技术来提高页面的搜索排名,包括许多我们通常不认为是设计师的东西(例如,页面上有什么链接、页面标题、布局、措辞等等)。
关于META
标签的一件有趣的事情是,“官方的”META
标签比网络上存在的标签总数要少得多。各种生产网络浏览器的软件公司,如微软,可能决定构建他们的浏览器来检测“非官方的”META
标签并对其做出响应。
例如,微软率先使用了 MobileOptimized META
标签,该标签告诉其移动互联网浏览器(Pocket Internet Explorer 或 Internet Explorer Mobile)如何专门渲染使用该标签的页面。一些非微软生产的浏览器支持这种标签,但不能保证这种标签对竞争对手浏览器的用户会像对 Internet Explorer 用户一样有帮助。类似地,我们的老朋友 AvantGo(前面提到过)也使用了一个名为 HandheldFriendly 的标签来实现相同的目的,但也存在相同的问题。
视口元素
所有这些困惑、竞争和不一致导致了被称为 Viewport 的新META
元素的引入和相当广泛的接受,尽管仍然是非官方的,但它是移动开发人员感兴趣的最新“特殊”META
标签。视口可以被认为是网页顶部的窗口。有时候,整个网页适合窗口,而其他时候,我们希望窗口大小不同,这取决于我们如何构建页面。与以前的尝试相比,Viewport 标签更成功的一个原因是它能容纳的信息量。例如,一个简单的视口标记可能如下所示:
<meta name="viewport" content="width=320,height=device-height" />
这段代码告诉移动浏览器,这个页面的内容应该是 320 像素宽,和设备的屏幕一样高。(如果你是为平板电脑设计的,你可能会把它调整到 800 像素宽。)为了更好地了解这将如何显示页面,请看一下 Figure 6–1。它显示“那条推特是谁?”我们在第二章中使用前面的视口设置制作的游戏。
图 6–1。 这里我们有“那条推特是谁?”设置了视口的游戏
注意页面的布局是如何大于手机的可用查看空间的?这是因为当我们将高度设置为设备的高度时,我们将宽度设置得太宽了——320 px——浏览器不知道任何关于初始比例的信息,正如页面底部的缩放按钮所证明的那样。在这种情况下,浏览器放大以显示页面的左上角,并提供缩放按钮,以便用户可以放大和缩小页面,根据自己的选择查看尽可能多或尽可能少的页面内容(虽然有时能够缩放页面上的内容很好,但如果我们可以在该页面上禁用该功能,则更好,这样我们可以保证我们的布局将按照我们预期的方式显示)。换句话说,浏览器对它应该如何显示这个页面做了一个有根据的猜测,但是由于把它拉到了左上方,它悲惨地失败了。它还可以显示所有缩小的内容,在这种情况下,所有 320 像素都适合,但文本会非常小。我们要给它更多的信息,这样它第一次就能正确显示。
幸运的是,Viewport 标签还可以包含关于页面缩放的信息——包括用户放大和缩小给定页面的能力。以下稍微复杂一些的 Viewport 标记指定了所有这些内容:
<meta name="viewport" content="width=320, height=device-height, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
这段代码告诉浏览器两件事。首先,它提供了比例值,允许浏览器知道您认为最大和最小的比例,以及最初设置比例的位置。在这个例子中,我们告诉浏览器,我们对页面进行了编码,因此它被设计为 100%可见,它不应该再变大或变小。这段代码添加的第二项是关闭用户放大和缩小选项的能力,如下面的图 6–2 所示。现在,我们不必担心我们的设计不会以我们希望的方式展示给用户。就我个人而言,我喜欢在构建移动网站或应用时总是剥夺用户缩放页面的能力。我知道有些人真的喜欢通过缩放天堂的能力,但在现实中,移动设备的屏幕空间非常有限,作为一名设计师或开发人员,我觉得你的工作是利用有限的屏幕空间,而不是让用户通过缩放来浏览你的成品。
图 6–2。 第二章的“那是谁的推特?”关闭用户可扩展的游戏
但是,如果你想要一个快乐的媒介,即你的应用应该自动放大,但也应该允许用户根据自己的意愿修改缩放比例,那该怎么办呢?那么像素更高的设备呢(比如谷歌 Nexus One,480 像素宽)?我们可以用几种方法来解决这个问题。第一种方法是删除META
标签中的user-scalable=no
行,并针对某个屏幕密度调整比例值。这让META
标签给出初始值,但不约束用户。为了确定这是否是正确的举措,我们可以进行一些用户测试(即,调查人们对我们的移动界面的看法),或者如果我们真的很好奇,我们可以实现一些 javascript 代码来查看用户在做什么,然后将其写入日志(这超出了本书的范围,尽管资源确实存在,如本页的讨论:[
htmldoodads.appspot.com/zoom-level.html](http://htmldoodads.appspot.com/zoom-level.html)
)。我们还可以构建一个页面,根据设备像素密度以不同方式显示,这是我们必须在底层 CSS 中指定的,使用低密度(ldpi)、中密度(mdpi)和高密度(hdpi)标签,然后为每种情况指定不同的 CSS 格式。
最后,如果我们有幸只为一个平台开发(例如,只有 Android 手机),我们可以研究浏览器的具体特性。在这种情况下,Android Webkit 浏览器会自动缩小文本元素的大小,以便它们适合屏幕(去查看在线报纸上的一篇文章,并放大一大块文本来查看这一过程)。如果这是我们的使用场景,那么我们就没有什么可担心的了。虽然这很容易让人相信,但你应该在 Android 设备上彻底测试你的页面。使用META
标签作为额外的安全层来帮助内置浏览器也没有坏处。
用户代理
有时,指定浏览器应该如何处理您的页面并不能满足您的需求。例如,也许你知道某些信息,不管它在移动设备上如何缩放或格式化,对于普通用户来说在小屏幕上处理太多了。也许你的页面使用了一堆 Flash 小程序,这些程序不能在 iPhone 或 iPad 等 iOS 设备上运行,你想把这些用户重定向到其他地方。也许你的网站上有一些特性和功能并不是在所有的应用中都可用,对于不太先进的浏览器来说需要禁用。最后,也许你只是想给出一个网址[
mysite.com](http://mysite.com)
,让它自动将手机和平板电脑导向一个特殊的优化版本,同时向其他人显示整个页面。大多数网站实现这些目标的方式是通过读取访问者的 web 浏览器提供的用户代理字符串。让我们首先来看一个示例用户代理,然后看看我们如何在我们的应用中使用它。
Nexus One 用户代理
用户代理字符串的格式是由 RFC 1945(HTTP 规范)制定的,其中包括该字符串的定义。该字符串由产品名称和版本以及可选注释组成。虽然这些是 RFC 1945 要求的字符串的一部分,但是许多供应商添加了额外的信息,例如操作系统、加载的扩展和平台细节。运行 Android 2.2 (Froyo)的 Google Nexus One 上的内置浏览器发送以下用户代理:
Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
这个字符串,分解后,翻译成:
- Mozilla 5.0 浏览器
- 运行在 Linux 操作系统上,特别是 Android 2.2
- 使用美国英语
- 在 Nexus One 手机上,运行操作系统的内部版本 FRF91。
- 它(注释指示)类似于 AppleWebKit 浏览器(版本 533.1)。
我们的 web 页面或应用可以获得很多信息(如果您查看 RFC,甚至可以获得关于引擎或当前浏览器提供的安全级别的更多信息)。利用这一点,我们可以将特定手机、浏览器、操作系统和语言的用户引导到我们为他们创建的特殊格式的页面,或者显示我们针对的浏览器的特定功能。
然而,对于用户代理检测有一个需要考虑的警告:用户代理欺骗。许多第三方浏览器,如 Android 上流行的 Dolphin HD 浏览器,允许用户设置自己的用户代理。这有多种原因,包括:
- 用户希望看到页面的桌面版本,不管它的格式看起来有多糟糕。
- 用户想要访问专门为另一个操作系统或浏览器构建的页面(即,可能 web 开发者还没有构建优化的 Android 版本,但是已经有了优化的 iOS 版本。)
- 出于这样或那样的原因,用户只是想在网站的日志中显示为桌面系统!想到的一个是开发者设置的移动用户希望查看的限制(即,仅向桌面用户显示特殊报价)。
对于用户代理欺骗,我们无能为力。毕竟,整个系统的设置围绕着被准确报告的用户代理。如果这是您所关心的问题,您可能需要使用除用户代理检测之外的替代方法来为您的用户提供正确的页面,而不管他们想要什么。这通常采用脚本的形式来测试浏览器的功能——然而,人们需要知道移动浏览器具有哪些细微的功能,哪些只有桌面浏览器才具有——这远远超出了我们在这里讨论的范围。
现在,您已经对用户代理字符串的用途有了基本的了解,让我们看几个例子,在这些例子中,我们检测最终用户的用户代理字符串。
PHP 用户代理检测
清单 6–1 展示了一个 PHP 示例,在这个示例中,我们将检测用户代理字符串并将其显示给用户。如果用户使用的是 Android 网络浏览器,那么他们将会看到总是令人愉快的笑脸(见图 6–3),但是如果他们试图在非 Android 驱动的设备上查看脚本,那么他们将会看到一张永远不受欢迎的皱眉脸(见图 6–4)。
清单 6–1。 PHP 用户检测代码
<?php $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']); if(stripos($userAgent,'android') !== false) { echo 'You are using an Android web browser! :)'; } else { echo 'You are not using an Android web browser! :('; } ?>
为了理解这段代码是如何工作的,我们将一行一行地把它拆开。首先,我们定义变量$userAgent,
,它获取请求页面的浏览器的用户代理字符串。然后我们使用 PHP stripos
函数——它在字符串中搜索一个字符串的出现——来解析用户代理以获得我们提供的值。如果在字符串中找不到这个值,在本例中是android,
,那么向用户显示一条消息。如果找到了,将显示另一条消息。
这只是一个小例子,展示了如何使用 PHP 来检测用户代理字符串,但是在很多情况下,这种技术可能会让您省心。例如,在 PHP 中使用检测可能允许您在内容发送之前阻止它(通常 JavaScript 检测不会这样做)。此外,如果您的应用大部分是用 PHP 编写的,那么您可能更愿意用这种方式记录用户代理,而不是通过 JavaScript。
图 6–3。 使用 PHP,我们获取用户代理字符串,并使用它来决定向最终用户显示哪些内容
图 6–4:Drat!看起来此人没有使用 Android 浏览器!
JavaScript 用户代理检测
虽然 PHP 非常适合检测服务器端的用户代理字符串,但有时开发人员可能需要检测客户端的用户字符串。例如,假设您正在使用一个站点或应用,该站点或应用允许您将信息导出到用户计算机上的文本文件或二进制格式文件中,以便以后查看。在 Android 设备上,文本是没有问题的,但如果你的用户选择二进制格式,当它无法打开时,他们可能会感到困惑。因此,我们可能只想隐藏该选项,而让其他选项可用。幸运的是,使用 JavaScript 完成这项任务就像使用 PHP 一样简单。
介绍 JavaScript 代理检测代码
清单 6–2 中显示的 JavaScript 代码让我们快速体验了 JavaScript 中的检测是多么简单;它类似于 PHP。与上一段代码相比,这段代码唯一的主要区别是,我们没有像使用 PHP 那样将消息写入页面,而是选择向用户显示一个弹出对话框(参见图 6–5 和图 6–6)。
清单 6–2。 Javascript 用户检测代码
<script type="text/javascript"> var ua = navigator.userAgent.toLowerCase(); window.onload = function() { if(navigator.userAgent.match(/android/i)) { alert('Android Rocks!'); } else { alert('Yawn, I guess your boring Desktop browser is okay'); } } </script>
图 6–5。Android 2.3 姜饼设备上的警告对话框
图 6–6。 谷歌 Chrome 浏览器上无聊的警告对话框
使用 JavaScript 代理检测向不同平台展示不同内容
单一平台开发人员(例如,为 Android、iOS 或一个操作系统编写应用的人)的一个自然发展是跳到不同的平台。Web 应用非常适合这一点,因为它们很大程度上可以一次编码并轻松地“移植”到其他类似的架构上。然而,为了避免衍生版本成为一场噩梦(例如,Android 和 iOS 版本,需要您分别对两个版本进行添加),可能更容易的是只拥有一段可以根据其运行位置进行更改的代码。在本例中,我们将向您展示如何将上一节中的清单 6–2 中的 JavaScript 代码实现到一个 Web 应用中,该应用将根据其运行的位置显示不同的内容。
我们还将通过把 jQuery Mobile 用作这个应用的“框架”,让您体验一下在下一章中会经常看到的东西。如果你喜欢接下来几个屏幕的样子,那么你会喜欢阅读第七章。
让我们首先讨论我们的总体目标:显示下载应用的适当链接。在 Android 中,我们希望链接指向 Android Market(或者可能是已经开放的新的 Android 应用商店之一,比如亚马逊的应用商店)。在 iOS 中,我们希望链接指向苹果应用商店。我们还想展示适当的图形。让我们从构建一个显示 Android 链接的简单页面开始。我们首先需要获取图形(可以在 Android 品牌页面上找到,([www.android.com/branding.html](http://www.android.com/branding.html)
)以及苹果应用商店图形([www.apple.com/itunes/affiliates/resources/documentation/identity-guidelines.html](http://www.apple.com/itunes/affiliates/resources/documentation/identity-guidelines.html)
),并将它们放在与清单 6–3 相同的目录中。
清单 6–3。 下载我们的 App HTML 页面
`
<head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Download our App</title> <link rel="stylesheet" href="//code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" /> <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script> <script src="//code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script> </head> <body>Our App!
bugjr.com
您会注意到这个页面相当简单。如果您查看页面的内容,您会看到我们有两个内容块:第一个名为androiddiv
,显示 Android Marketplace 的文本和图像。第二个是applediv
,显示苹果应用商店的文本和图片。这两个块在它们的 div 标签中都被设置为"display: none"
,这意味着默认情况下,两者都不显示。页面顶部是一个非常简单的 JavaScript 函数,它测试检测到哪个用户代理,并切换到适当的文本块(通过将显示设置为"block"
)。如果我们在 Android 网络浏览器中加载这个页面,我们会看到图 6–7:
图 6–7。 在安卓浏览器中加载下载 App HTML 页面
然而,如果我们在 iPhone 上的 Safari 中加载这个页面,我们将会看到 Figure 6–8:
图 6–8。Safari 中下载 App HTML 页面
现在我们有了一个简单的方法来引导我们的用户找到正确的下载,而不需要他们点击某个图标!正如你所想象的,我们可以将同样的检测用于其他目的——向不同的用户显示不同的文本,显示不同的广告,等等……最好的部分是它都是用 HTML 和 JavaScript 编写的,这意味着如果你选择使用一个产品将你的 Web 应用转换成一个本地应用(我们将在第十三章中讨论,使用 PhoneGap 或 Titanium Mobile 等工具),你可以使用这个技巧为用户提供更高级别的定制!
既然我们已经讨论了在 PHP 和 JavaScript 中检测用户代理,我们将讨论如何使用 Apache Web 服务器来完成检测。
。htaccess 用户代理检测
这些类型的文件是通常与 Apache web 服务器相关联的配置文件。也称为分布式配置文件,.htaccess
文件可以基于每个目录为 web 应用设置一系列规则和配置。如果服务器支持的话,通过使用 mod_rewrite 模块,.htaccess
文件可以用来控制将 URL 重写为更干净、更容易被人阅读的内容。的另一个有趣的用法。htaccess 文件将使用它来检测用户代理字符串,并在需要时将用户定向到不同的站点或子域。参见清单 6–4。
清单 6–4。 举个例子。htaccess 文件
<IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteCond %{HTTP_USER_AGENT} ^.*Android.*$ RewriteRule ^(.*)$ http://android.yoursitesurl.com [R=301] </IfModule>
前面的代码首先检查 Apache 服务器是否支持重写代码,然后打开重写引擎并设置条件和规则。该条件在用户代理中检查 Android,如果找到,规则告诉 web 浏览器将请求重定向到[
android.yoursiteurl.com](http://android.yoursiteurl.com)
而不是通常的[
yoursiteurl.com](http://yoursiteurl.com)
(或者用户输入的任何内容)。
这种类型的检测是非常强大的,并且可能会惹恼一些用户。虽然用户可以通过关闭 JavaScript 来禁用 JavaScript 检测。htaccess 和 PHP 用户代理检测只能通过改变用户代理来“欺骗”,这不是所有浏览器都支持的。(值得注意的是,免费的 Dolphin HD 浏览器允许用户在 Android、iOS 和桌面之间轻松切换用户代理,这对于测试和欺骗非常有用!)此外,用户可能希望在他们的 Android 浏览器中查看完整的网页,虽然 PHP 方法允许您提供某种代码切换(即,用户可以单击的链接将设置一个 PHP 会话变量,告诉您的代码不要显示移动网站)。htaccess 用户代理检测不允许这样做。然而,在简单的情况下。htaccess 解决方案是最容易配置的。明智地选择您的检测方法!
总结
恭喜你!您已经完成了另一个章节,使用新发现的知识视窗和用户代理字符串,您将能够更好地处理可能不时出现的某些讨厌的用户界面问题。尽管这本书主要关注的是为 Android 设备构建 web 应用,但是认为你永远不会遇到需要开发跨所有平台的应用的情况是愚蠢的。使用我们在那里讨论的内容,您可以为您的应用定制一个高效、优雅的解决方案!
七、使用 jQuery Mobile 构建令人印象深刻的用户体验
信不信由你,对于一个母亲获得艺术学校奖学金、父亲从事广告工作多年的人来说,我并没有太多的艺术天赋。我大部分的编程努力都集中在功能上,而不是风格上…结果好坏参半。一方面,许多人称赞这种极简设计——例如,我的一个名为快餐卡路里查询的应用只不过是两个下拉框和几个按钮。它有超过 1000 条评论,在安卓市场上的评分为 4.3 分。然而,对于每一个欢呼“如此容易导航,方便的信息!”(实际回顾),我得到的是不理解为什么看起来不好看的人。
值得庆幸的是,在 web 应用领域,有一些选项可以让像我这样的设计“懒汉”看起来像是我们支付了一些非常高的价格让事情看起来华丽的设计师。事实上,有很多工具可以免费实现这个目标,在这一章中,我们将深入了解其中一个工具:jQuery Mobile(jquerymobile.com
)。这绝不是你唯一的选择。其他想到的还有:Wink 工具包([www.winktoolkit.org/](http://www.winktoolkit.org/)
)、Sencha Touch ( [www.sencha.com/products/touch/](http://www.sencha.com/products/touch/)
)、Zepto.js ( [
zeptojs.com](http://zeptojs.com)
)、Jo ( [
joapp.com/](http://joapp.com/)
)、xui.js ( [
xuijs.com/](http://xuijs.com/)
)和 JQTouch ( [www.jqtouch.com/](http://www.jqtouch.com/)
)。然而,就我们的目的而言,jQuery Mobile 在特性和易于配置方面提供了最好的性价比。所以——让我们开始探索吧!
基础知识
如果您熟悉用于桌面浏览器的 jQuery UI 工具包,那么您应该很快学会 jQuery Mobilepretty!jQuery Mobile 是这样一种框架,它试图将开发移动应用的大量繁琐任务从开发人员手中拿走,并将所有繁重的工作交给框架,这样您就可以将更多的时间放在对您的应用更重要的其他项目和功能上。jQuery Mobile 创建了一系列触摸优化的小部件和用户界面元素,正在迅速成长为移动 javascript 框架领域的顶级竞争者之一。
然而,jQuery Mobile 最好的特性之一是它非常简单且易于修改的模板结构。有了一点 CSS 知识,开发人员可以很容易地修改他们的 jQuery Mobile 主题的样式,使其看起来像他们想要的那样。这对于像我这样喜欢弄脏自己的手,在他们接触到的任何一段代码的内部进行修改的人来说是非常棒的。另一方面,正如我们将很快看到的,创建一个自定义主题一点也不困难——事实上,如果我们不想的话,我们根本不需要接触 CSS!
最重要的是,jQuery Mobile 是一个非常容易安装和运行的框架。开始时,只需在文档头中包含 jQuery Mobile 源文件,然后在 HTML 标记中使用特殊的数据属性。数据属性是 HTML5 的有趣补充。在 XML 和以前版本的 HTML 中,正式定义的属性被用在标签中来描述数据或数据的格式。虽然在以前的 HTML 版本中,总是可以向 HTML 代码中添加自定义属性,但这总是不被接受。而且,通常情况下,如果你的代码通过一个验证程序运行,它最终会到处出错。清单 7–1 显示了一个无效的自定义“情绪”属性,而清单 7–2 显示了如何在 HTML5 中使用一个有效的“数据-情绪”属性。
清单 7–1。HTML 中一个无效自定义属性的例子
`
Today was an awesome day!
Suspendisse consectetur consequat risus non viverra. Phasellus ligula urna, egestas porttitor facilisis vel, euismod sit...
清单 7–2。html 5中一个有效自定义属性的例子
`
Today was an awesome day!
Suspendisse consectetur consequat risus non viverra. Phasellus ligula urna, egestas porttitor facilisis vel, euismod sit...
让我们看一下前面的代码示例。在编写代码的第一次尝试中,我们创建了一个附加了无效自定义属性的 div 容器,以指示创建该博客条目的个人的心情。虽然没有什么可以阻止我们使用这个异想天开地创建的 mood 属性,但是代码不能用流行的验证引擎进行验证,比如 W3c 标记验证服务([
validator.w3.org/](http://validator.w3.org/)
)。这是因为 mood 不是 HTML 规范认可的属性。经过多年来自各地开发人员的抱怨,权力机构决定对我们开发人员有点怜悯,并给了我们最终创建自定义属性以附加到我们代码上的能力。开发人员可以使用这些新的数据属性向他们的标记代码中添加任何类型的数据,这些数据可以用于我们可以想象的任何目的。在我们的第二个例子(清单 7–2)中,我们使用了data-mood
属性来记录我们的心情,以一种取悦验证者的方式,同时仍然允许我们根据需要在脚本中使用这些数据!只要我们将前缀“data-”放在想要存储的数据字符串前面,它就是有效的 HTML5 代码。我们可以将它们用作小型数据容器,如下例所示。"
现在,您已经了解了数据属性是如何工作的,以及如何使用它们来丰富我们的移动应用,让我们来看看一些 jQuery 移动代码,并剖析它是如何工作的。
在清单 7–3 中,我们将使用 jQuery Mobile 框架显示一个非常简单的页面。您会注意到它使用了许多数据角色属性。
清单 7–3。 来自默认 jQuery 移动样板文件的 HTML 标记示例
`
Well Then, Soldier...
How goes the day?
© 2011 Dodo Men Ply, Inc.
只需前面的几行代码,就可以使用普通的老式 web 浏览器创建非常原始的移动应用外观。这个页面非常简单。首先,我们有一个包含数据属性值“page”的主容器,它是内容的包装器。正如所料,在那之后我们有了我们的“header”,它通常包含页面的标题和人们决定放在那里的任何导航元素。在我们传递了页眉之后,我们开始向页面的主体前进,也称为“内容”数据属性,紧接着是我们的“页脚”
正如我之前所说的,这个标记并没有太多的东西。大多数神奇的事情都发生在幕后,在 jQuery 移动应用的内部工作中。在接下来的几个例子中,我们将探索如何使用 jQuery Mobile,从简单到复杂,然后是自定义主题化!让我们打开可信的网络浏览器,看看这个页面在我们的移动设备上是什么样子的(见图 7–1)!
图 7–1。 由 jQuery Mobile 创建并显示在 Android 2.3.4 手机上的一个看起来非常本地的用户界面
你看到那有多容易了吗?最好的一点是,我们仅仅触及了 jQuery Mobile 的皮毛。只需一点时间,人们就可以轻松地构建出一个功能齐全的移动 web 应用,其中包括漂亮的用户界面元素、模态窗口、对话框、页面转换、导航工具栏以及其他许多东西!让我们再深入一点。
添加多个页面
通常,应用包含不止一个页面!在清单 7–4 中,我们将创建一个更高级的 jQuery Mobile 文档,它包含三个页面:主页、关于页面和联系页面。在这个过程中,我们将会比平常多加一些注释,这样您就可以看到代码的哪个部分创建了哪个元素(显示在图 7–2、7–3 和 7–4 中)。这样,如果您想要定制某些元素,您将确切地看到需要修改什么。
清单 7–4。 页面上的所有 UI 元素都是由 jQuery 移动应用创建的
`<!DOCTYPEHTML>
Hello World
`在标题中,我们将在页面顶部的黑条中指定我们想要查看的测试。如果我愿意,我可以包含不止一行文本,黑条会继续增加。然而,这可能会让用户有点分心。
清单 7–4 续。 主页面的内容
`
如果你在家编码,你会注意到每一项都出现在一个按钮式的列表中。你可能想知道我们是否可以让这些列表更有趣一点。事实上我们可以——jQuery Mobile 支持几种类型的列表([
jquerymobile.com/demos/1.0.1/docs/lists/index.html](http://jquerymobile.com/demos/1.0.1/docs/lists/index.html)
)有完整的列表和示例)。我们在这里使用只读列表,因为我们没有将列表项链接到任何特定的页面或动作。但是,也可以使用其他链接选项,例如:
- 一个嵌套列表(参见 Figure 7–2),其中一个列表项被点击后,会滑开以显示多个列表项。
图 7–2。 一个嵌套列表
- 计数气泡式列表(参见图 7–3),其中每个列表项目的末端都可以显示一个数字,嵌入到项目中。这对于显示新项目、通知、新闻故事等的数量非常有用
图 7–3。 一个盘点泡泡榜
- 缩略图(图 7–4)和图标(图 7–5)列表,在列表文本左侧显示缩略图或图标。这对于显示小照片和项目所用于操作的描述性图标很有用,或者只是为了装饰。
图 7–4:缩略图列表
图 7–5。 一个图标列表。
- 创建插入搜索字段的几个选项(图 7–6),通常是顶部的一个列表项,让用户稍后过滤列表项。
图 7–6。 列表顶端的一个搜索过滤框
- 最后,可以创建一个带有表单的列表(Figure 7–7),允许您在列表视图中创建一个有序的表单页面。对于结帐页面、设置页面和类似的情况很有用。
图 7–7。 表单列表内的元素
就列表的用途而言,天空是无限的!现在,回到代码…
清单 7–4 续。:主页面的页脚。
`
您会注意到,在主页的底部,我们创建了一个页脚部分(看起来像另一个黑条),并添加了两个按钮。在 jQuery Mobile 中创建按钮非常简单——基本上,它只是将data-role="button"
添加到任何常规的 HTML 链接中。
后两个属性—过渡和主题—指的是页面过渡应该如何发生,以及按钮的颜色和样式。我们将在接下来的几章中详细讨论这两个问题。我们也可以通过指定一个图标来手动地为按钮添加更多的样式。在有意义的地方,jQuery Mobile 会自动向按钮添加图标(例如,通过向对话框关闭按钮添加 X 图标)。但是,如果我们愿意,我们可以手动执行此操作。例如,将前面的“联系人”链接改为
<a href="#contact" data-transition="pop" data-role="button" data-theme="b" data-icon="plus">Contact</a>
会在按钮上添加一个漂亮的+图标。完整的图标列表可以在 jQuery Mobile 的网站([
jquerymobile.com/demos/1.0.1/docs/buttons/buttons-icons.html](http://jquerymobile.com/demos/1.0.1/docs/buttons/buttons-icons.html)
)上找到。默认情况下,图标出现在按钮的左侧;然而,我们可以使用data-iconpos="right"
或"top"
或"bottom"
来移动它。我们也可以使用data-iconpos="notext"
来显示图标而不显示其他任何东西!
如果你想使用自己的图标呢?嗯,那也是可能的。为此,您需要添加一些自定义 CSS,并创建一个格式正确的图标文件(这两者在前面提供的链接中都有解释)。
最后,通过用<div data-role="controlgroup">
和</div>
标签包围一系列按钮,可以将按钮格式化为同一组的一部分。这将使您可以直观地聚集具有相似含义的按钮(例如,“是”、“否”或“取消”组)。这通常意味着只有顶部和底部按钮的角是圆形的(如果使用默认主题),按钮之间几乎没有空间。这对于给用户一个关于 groupedbuttons 相关性质的微妙提示非常有用。现在第一页已经完成,从页眉到页脚,我们将在同一个 HTML 文档中定义另外两个页面!
清单 7–4 续。:关于和联系页面。
`
About Us
Vivamus sit amet nulla vitae odio ultricies fringilla quis at felis. Integer sagittis eleifend leo, et tempor elit adipiscing in. Pellentesque commodo condimentum pulvinar. Integer vitae tellus ac sapien molestie euismod. Sed a enim ut leo fermentum lobortis ac eget velit. Mauris commodo porta felis id fermentum. Aenean eleifend justo eu sem consectetur auctor. Quisque convallis ullamcorper elementum. Integer hendrerit vehicula nisi eu congue. Integer aliquet quam a arcu cursus ac consequat est pretium. Nam nec pharetra lorem. Maecenas lacinia facilisis eros quis tempor.
Send Us Mail!
现在我们已经讨论了代码是如何工作的,让我们看看主页面在 Figure 7–8 中的样子。
图 7–8。 在 Android 2.3.4 移动设备上查看我们的 jQuery 移动主页
我们将快速跳过“关于”页面,因为这里没有太多的操作。不过,有一点我们将要介绍的美味佳肴。我敢肯定,看到这一页的你可能会问自己,“嘿,他为什么不创造某种视觉线索,可以用来导航回我们所在的上一页?!"(即使你没有想到这一点,你现在已经想到了)好吧,你不用担心!如果您在您的 web 浏览器中加载我们刚刚创建的页面,并查看“关于”页面,您会注意到 jQuery Mobile 非常好地在您的页面中为您包含了一个后退按钮(参见图 7–9),甚至包含了我提到的正确图标!
图 7–9。 我们的 jQuery Mobile 关于在 Android 2.3.4 移动设备上查看的页面。注意到 jQuery Mobile 自动包含在用户界面中的 back 按钮了吗?
最后,在我们的联系人页面上,我们真正开始享受 jQuery Mobile 提供的一些特性(参见图 7–10)。查看我们的代码,您会看到该页面上有两个数据角色为“fieldcontain”的 div 容器。我相信您现在已经猜到了,这些容器是用来存放和分隔表单输入字段的,这些字段不一定要放在一起。基本上,它是用于我们这些平面设计受损的人的审美。在分离了表单字段之后,我们又有了另一个 div 容器,它保存了 Cancel 和 Submit 按钮。通过将 fieldset 元素分配给 ui-grid-a 的 CSS 类(本例中的“a”代表将用于样式化网格容器的主题),这些按钮以类似网格的格式放置在页面上。最后,值得注意的是,我们这里的代码将电子邮件表单发送到 nothing (#),就像它现在所写的那样。在本章的后面,我们将看到如何使用 JavaScript 来处理表单,或者编写一个简单的 PHP 脚本来接收表单数据并进行处理。
如你所见,jQuery Mobile 允许你在很短的时间内创建一个非常漂亮的用户界面!
图 7–10。 我们的 jQuery 移动联系人页面格式整齐,可以在 Android 2.3.4 移动设备上查看
那么,关于这些转变
如果您一直在编写代码,您会注意到上一个示例中的按钮根据我们想要做的事情有不同的转换。动画是移动设计中的一个关键元素,因为它让小屏幕给用户一种三维的感觉,这可以帮助他们理解他们在做什么。默认情况下,jQuery mobile 框架将实现从右到左的滑动过渡,这给用户翻页的感觉。这个框架也很好,当“后退”按钮被按下时,它将反转最初使用的过渡,所以在这种情况下,转向上一页。在清单 7–5 中,我创建了一个简单的页面,展示了从第一页到第二页的所有过渡类型。
清单 7–5。 过渡演示 HTML
`
The Title
This is where we put what we want to talk about on this page, it's going to be great
Pop Page 2Slide Page 2
Slideup Page 2
Slidedown Page 2
Fade Page 2
Flip Page 2
The Title of Page 2
This is where we put what we want to talk about on this page, it's going to be great
Back to page 1呈现时,页面看起来像 Figure 7–11,带有几个文本链接,将根据需要显示适当的过渡。
图 7–11。 过渡演示页面
点击每个链接查看相应的过渡,然后使用第 2 页上的后退按钮查看相反的过渡。我们在清单 7–4 的 HTML HREF 链接中指定了我们想要的过渡,你可以随心所欲地使用它们。然而,请记住,当处理转换时,应该事先决定每个转换的含义,以免用户感到困惑。如果在数据库中输入内容会导致渐变,而向上滑动会显示消息或错误,自由切换这些内容可能会让用户怀疑他们采取的行动。另请注意,翻转和弹出过渡可能无法在某些版本的 Android 股票浏览器上正确呈现,因此可能需要避免它们。说到向用户显示消息…
让我们来对话吧
就计算机而言,对话框已经存在很长时间了。我们已经在本书前面展示了一些非常简单的例子。然而,jQuery Mobile 有一个非常好的内置方式来显示这些对话框,我们将使用下面的清单 7–6 来展示它。
清单 7–6。 对话框示例
`
The Title
This is where we put what we want to talk about on this page, it's going to be great
Open DialogSlide Down Dialog
Flip Dialog
Pop Dialog
The Title of Page 2
This is a dialog box
Back to page 1当我们加载清单 7–6 时,我们得到一个包含一系列 3 个按钮的页面(图 7–12),我们可以按下这些按钮来查看弹出的第二页(与我们的过渡演示中的页面相同)。在对话框中,一旦打开,我们还会在右上角看到一个小黑 X 按钮来关闭消息,不需要任何额外的编码。
图 7–12。【dialogs.html】对话框演示页面,
当你需要引起用户的注意,并且不想给人一种他们正在进入新页面的印象时,对话框是很有用的。如果使用得当,它们会给你的用户一个很好的,完美的体验。
现在你已经添加了一些个人风格的过渡和对话框,让我们回到我之前提到的主题:自定义主题。
使用主题滚轮滚动您自己的主题
让我们面对现实:创造一个主题并不是一件容易的事情。有很多事情需要考虑,虽然苹果已经做了很多工作来使移动页面看起来有点标准,但你不希望我们的 Android 应用看起来完全像 iPhone 的克隆版,对吗?(事实上,也许你有,但是嘿——有选择总是好的!幸运的是,jQuery Mobile 团队已经创建了一个他们自己的名为 theme roller(Figure 7–13)的令人敬畏的 web 应用,它可以让你快速而轻松地创建一个主题。
图 7–13。?? 主启动屏幕
ThemeRoller 允许您调整普通 jQuery 移动主题的每一个元素。在屏幕的右侧,您将看到三个主题(A、B 和 C),每个主题都可以定制,如果需要,您甚至可以添加更多主题。创建之后,我们可以使用data-theme
属性指定我们想要在给定页面上显示的主题(在前面的清单中可以看到这样的例子,比如清单 7–4)。
一旦你关闭了“开始滚动”屏幕,你可以开始扩展屏幕左侧的选项,就像我在图 7–14 中所做的那样。
图 7–14。 自定义滚轮屏幕左侧的选项
我可以对我的所有主题进行全局更改,或者我可以专门对主题 A、B 或 c 进行更改。在这种情况下,所有的默认设置都提供了非常 iPhone-y 的外观。我将改变它们,以创建如图图 7–15 所示的配置。
图 7–15。 改变选项更新右边的预览
你可以看到我已经改变了相当多的设置,这使得界面非常不同。我还没有改变颜色,虽然我可以很容易地做到这一点。事实上,ThemeRoller 甚至会给我一个 Adobe kuler 样本集(图 7–16;互相搭配的颜色集合——([www.adobe.com/products/kuler/](http://www.adobe.com/products/kuler/)
)帮助我挑选一组让我的用户满意的颜色。
图 7–16。 Adobe kuler 样本帮助我找到了我正在寻找的那种配色方案
经过几分钟(或几小时)的定制,我可以通过输入几个选项来下载我的主题,如图图 7–17 所示。这为我提供了一个包含主题目录的 ZIP 文件,一个正确嵌入了我的主题的示例 index.html,所需的图片,以及两个用于我的主题的 CSS 文件,如果需要的话,我可以手工编辑它们。
图 7–17。 带说明的下载主题窗口
如果我需要修改我的主题,我可以使用 ThemeRoller 中的“导入”选项将它重新导入到 web 应用中,并再次播放。然后,我可以像导出第一个主题一样导出新主题。想象一下:你可以创建自定义主题(就像我之前创建的那个,显示在图 7–18 中的示例索引文件中),然后为“姐妹”应用修改它们——无需直接接触 CSS!
图 7–18。 我的自定义主题应用到 ThemeRoller 提供的示例索引文件
现在我已经让我的应用看起来像我想要的那样,并且我知道如何构建额外的页面、过渡和对话框。我正在创建一个不仅功能强大,而且外观漂亮的应用!
把所有的都放在一起:简单的计算
既然我们已经在 jQuery Mobile 中构建了一些示例页面,那么让我们构建一些有用的东西:一个简单的计算器。我们将构建一个非常简单的计算器,它接受两个数字并对它们执行一些操作,然后返回结果。我还将创建一个自定义主题来使用。最终结果看起来像图 7–19:
图 7–19。 简单的 Calc jQuery 手机 App
Simple Calc 是一个极其简单的计算器。你在第一个盒子里放一个数字,在按钮下面的第二个盒子里放一个数字,然后按+或-。会弹出一个小的 JavaScript 警告,告诉你答案是什么。这当然很简单,但是对于展示如何使用 JavaScript 向 jQuery 移动页面添加一些交互性很有用。让我们看看清单 7–7 中的代码。
清单 7–7。 简单计算的代码
`
Simple Calc
Neat!
让我们浏览一下这段代码。首先,我们开始添加通常的 HTML 标题和 jQuery Mobile 的链接。您会注意到我的自定义主题也包含在第 7 行(simplecalc.css)。我使用我们在上一节中讨论的 ThemeRoller 构建了这个!
接下来是一组定制的 JavaScript 函数,addnums()
和subnums().
这些函数访问两个数字框(分别声明为 num1 和 num2)并执行所需的操作。然后他们弹出一个带有结果的标准 JavaScript 警告框。如果您想发挥您的 JavaScript 能力,您可以将这两个功能合二为一!
再往下一点,我们可以看到实际内容的布局。我们放入了两个输入,数字 1 和数字 2,以及它们之间的两个按钮。我们使用了一个控件组,这样按钮组合在一起,而不是分开。您会注意到每个按钮都有一个对其对应的 JavaScript 函数的onclick=
引用。这就是神奇的地方——也是为什么我们实际上不需要构建表单操作页面——JavaScript 会处理所有的计算。
这就是了——一个简单的计算器!
总结
使用 jQuery Mobile,人们可以快速获得一个基本的应用,并使它看起来像是专业设计的。您还可以制作快速而漂亮的“信息”应用(例如,作为会议程序或建筑物或事件的导游的应用)。我们已经介绍了 jQuery Mobile 的基础知识,但是如果您需要的话,仍然有一些高级的主题和细微差别留给您自己去探索——您的想象力是唯一的限制!
八、构建视觉丰富的互联网应用
既然我们已经更好地掌握了 JavaScript 和各种面向移动的 JavaScript 框架可以做些什么来增强我们的用户体验,那么是时候深入其中,用一点数字手指画来弄脏我们的手了。构建优秀的 web 应用不仅仅是编程和逻辑。相当多的网页开发从设计领域获得了很多线索。以 CSS 为例。它让开发人员疯狂的一个主要原因是因为 CSS 是一个设计工具,而不是真正的开发工具。当然,我们必须编写代码使一切看起来漂亮,但实际上我们正在做的是精心设置边距、填充、颜色、字体等。
记住这一点,在这一章中,我们将探索开发中更多涉及设计的有趣方面。我们将看看 Creative Commons 图标和图形的资源,web 字体的资源,CSS 框架,甚至是一个面向 web 开发人员和 web 设计人员的漂亮的位图和矢量图形编辑器,叫做 Adobe Fireworks ([www.adobe.com/products/fireworks.html](http://www.adobe.com/products/fireworks.html)).
查找和使用图标和库存照片
让网站看起来更有吸引力的最简单的方法之一是在你的项目中添加一些图标或吸引人的图片。有大量的资源,艺术家们乐意提供他们创作的图标集或他们拍摄的宏伟照片,免费使用,不问任何问题。
在这一节中,我们将看看一些更受欢迎的网站,这些网站迎合了设计师、开发人员和所有其他爱好者对有吸引力和有趣的事物的需求。
图标查找器
Iconfinder ( [www.iconfinder.com](http://www.iconfinder.com)
/,见 Figure 8–1)是我个人最喜欢的一个,可以在开源图标的知识共享空间中找到。这个搜索引擎可以让你从 150,000 多个不同的图标中挑选出你的下一个项目。
从 12 像素到高达 512 像素的图标和图形,这个搜索引擎几乎可以保证有一些东西可以用来直观地提升你正在进行的任何项目。
虽然此搜索引擎中的图标可以免费使用,但并非所有商业网站(即,为营利团体而非非营利团体建立的网站)上的图标都可以免费使用,因此请确保阅读您可能想要使用的任何图标的许可协议。到目前为止,我们已经提到过几次 Creative Commons 许可,所以花点时间考虑一下它和其他许可是有意义的。首先,你必须记住,无论是谁最初设计了一件艺术品(无论是图标、照片等等),都保留着该艺术品的版权。很容易想到“嘿,这是在互联网上;必须是免费的。”然而,如果主人发现了,这种想法会让你很快陷入麻烦。许多艺术家和设计师不希望为他们的内容创建一个独特的许可证,他们决定使用知识共享许可证([
creativecommons.org](http://creativecommons.org)
。这些许可证允许艺术家自由使用他们的作品,有或没有某些限制。你不能仅仅假设在知识共享下许可的东西是免费使用的——它可能对商业使用有限制(NC 是知识共享团队使用的简写),或者它可能要求在你的应用中归属(这里使用的简写是将图标或图像归功于所有者)。此外,所有者可能会指定应该知道的其他选项。
总而言之,如果你不确定是否可以使用一个图标,不要害怕接触作者并询问。最坏的情况是他们会说不!
图 8–1。 Iconfinder 搜索引擎首页。超过 158,184 个图标,并且还在增长!
查找图标
find Icons(Figure 8–2)是另一个搜索引擎,它迎合了开发人员和设计人员对网站的需求。功能和布局类似于图标查找,所以如果你熟悉一个搜索引擎,你可以很容易地适应使用另一个搜索引擎。
我发现当搜索一个图标时,同时使用两个站点总是好的,因为每个站点都有一系列在另一个站点上没有的图标。您可以在[
findicons.com/](http://findicons.com/)
浏览查找图标搜索引擎
图 8–2。 在查找图标网站上搜索“暂停”图标
使用图标
假设你已经找到了一个你喜欢的图标,或者你已经使用 favicon.ico Generator ([www.favicon.cc/](http://www.favicon.cc/)),
这样的服务创建了一个图标,你可以通过在 HTML Header 部分放置以下 HTML 代码来轻松地为你的 web 应用指定这个图标:
<link rel="shortcut icon" href="http://yourdomain.com/app.ico" />
这一行告诉所有现代浏览器你的应用的图标位于[
yourdomain.com/app.ico](http://yourdomain.com/app.ico)
(你可以把图标放在你选择的任何地方)。这将导致浏览器显示你的图标(而不是常用的“书签”图标),只要用户将你的页面加入书签或者(在某些浏览器中)浏览到它。图 8–3 和 8–4 分别显示了查看有图标和无图标的网站时 Mozilla Firefox 浏览器的地址栏。
图 8–3。 火狐显示一个带有指定图标的网址
图 8–4。 火狐显示一个没有图标的网址
您可能还想添加一行:
<link rel="apple-touch-icon" href="/image.png" />
这一行明确告诉移动浏览器(如苹果的 Safari 和安卓的浏览器)应该在锁定主屏幕快捷方式的功能中使用参考图像文件(通常是 57×57 像素图像或 114×114 图像文件(对于较新的系统))。对于 web 应用来说,这可以给用户一个完美的外观,让他们更接近原生应用的体验。
既然我们已经选择并使用了一个图标,让我们来讨论一下如何通过查找和使用库存照片来使我们的应用更加图像友好。我们将从最古老的图像搜索工具之一 deviantART 开始。
猜猜
似乎 deviantART ([www.deviantart.com/](http://www.deviantart.com/)
,见图 8–5 已经存在了很长时间,从技术角度来说,的确如此。这个 2000 年的古老遗迹经受住了互联网时代的考验,是业余和专业艺术家以及平面设计师创造力的一个闪亮支柱。
除此之外,一些社交媒体功能允许用户评论或与其他用户分享作品或艺术,你会有一个令人上瘾的用户体验,提供了大量的资源...有时并不是很好的资源。
一定要去看看他们的摄影区,那里有很多免费的库存摄影作品,可以在你即将到来的项目中使用。另外,一定要查看提供的库存照片的许可协议。有些艺术家可能有特殊的要求,在你利用他们的作品之前,必须先满足这些要求。这些要求可能相当普通(即无商业用途)或非常具体(即必须用于公共服务公告或慈善事业)。
图 8–5。 查看 deviantART 的股票摄影版块
组合拳
另一个更专业也更昂贵的解决方案是 iStockphoto ([www.istockphoto.com/](http://www.istockphoto.com/))
,它可以为你的 web 应用提供出色的库存照片。这是一项我已经使用多年的服务,为客户项目购买免版税的图像。
虽然与 deviantART 不同,这种资源不是免费使用的,但您将购买的图像来自专业人士,并且往往比您在其他地方找到的业余人士的图像质量更好。
除此之外,还有一个很大的图像存档,可以很容易地搜索到你想要的东西,你有一个应该总是放在手边的资源!你永远不知道什么时候,有一天,你的一个客户可能会打电话给你说“嘿,我已经决定我们真的要在我们的网站上添加一个巨大的粉红色电话”,尽管这听起来很疯狂和可笑,但你很可能会在 iStockphoto 上找到一个巨大的粉红色电话的图像。
除了 iStockphoto 和 deviantART,你还可以在 Shutterstock ( [www.shutterstock.com](http://www.shutterstock.com))
和 Flickr ( [www.flickr.com](http://www.flickr.com)).
)找到图片,特别是 Flickr,它有一个非常好的高级搜索功能([www.flickr.com/search/advanced/?](http://www.flickr.com/search/advanced/?)
),允许你指定你只想要 Creative Commons 许可的内容(而且,你只想要 CC 许可的内容,这些内容可以免费用于商业用途,并可以进行改编)。这是为您的应用获得非常好的用户提交照片的简单方法。
在我们结束讨论照片之前,讨论一下什么时候应该和不应该选择使用照片可能是有用的。在许多不同的空间中使用图片来推广你的应用和/或让它大放异彩是很有诱惑力的;但是,有一条必须要走的细线。
在 Web 应用中使用照片的指南
在找到我们在前面几节中讨论的服务后,很容易就能找到应用每个部分的照片。例如,你可能在应用的主屏幕上有很大的导航按钮,这些大的正方形在里面放照片会不会比文字或简单的图标看起来更好?也许……但是,我们建议您首先考虑以下几点:
- 我给每张照片增加了多少加载时间?回想一下,当我们测试时,我们的应用通常驻留在我们的计算机上。这使得加载时间的争论很大程度上没有实际意义——我们只是不知道在特定情况下我们的应用会如何加载。考虑到这一点,漂亮的照片有时会导致应用加载缓慢。我们建议使用适当大小的照片(例如,必要时调整大小;不要简单地指定高度和宽度,这将使它们呈现得更小,但实际上文件大小不会更小(如果可能的话,小于 10 千字节),并在各种条件下测试页面的照片和非照片版本(例如,从生产 web 服务器提供给 WiFi 和蜂窝数据连接上的设备)。
- 我的图片会分散用户的注意力吗?是的,虽然一张图片抵得上 1000 字,但有些图片在用户中引起的争论更有价值。在某些情况下,停止标志的照片对用户来说可能很容易理解,但在其他情况下可能会令人困惑(例如,在游戏中,停止标志是否意味着一轮结束?退出游戏?阻止我的小男人走路?).图像应该只是帮助用户更快地找到东西,而不是通过分散注意力、混淆视听或质量低劣来迷惑用户。
- 使用图片会引起用户刻板印象吗?图像可能很强大,但在某些情况下,它们可能与我们熟知的传统刻板印象有关,也可能与一些你可能没有想到的刻板印象有关。例如,使用可靠的“穿西装的男人”和“使用笔记本电脑的女人”可能看起来适合你的应用;然而,用户通常已经看过这些股票图片,可能会认为你的 web 应用和其他使用过相同图片的人一样没有创意。这就是为什么现在许多网站倾向于极简设计趋势——这比“努力工作的主管”或“足球妈妈”更难预测
假设你已经考虑了前面列表中讨论的问题,使用照片可能是增强你的应用吸引力的好方法。注意你的用途!现在我们来看一个更微妙的图像操作:在我们的 web 应用中使用自定义字体。
网络资源
你可能还记得,在第二章中,我们在构建“那条推文是谁?”时,简要地提到了使用网络字体的基础申请。在过去的一年半时间里,所有最流行的桌面浏览器都推出了更新,现在可以在标记中使用网络字体。几乎所有 Android 的移动浏览器都可以使用它们。
将 web 字体添加到您的网站或应用非常简单,如清单 8–1 所示。
清单 8–1。 用一点 CSS 魔法将 Droid Sans 字体加载到我们的网络浏览器中。
``
让我们快速看一下清单 8–1 中的代码,这样你就能理解我们在这里做什么了。我们将在@font-face
代码中定义的第一个属性是font-family
属性。在这里,我们可以随意命名字体。我们在这里使用字体的真名,Droid Sans,但是如果我真的想的话,我可以称它为“龟蜡”。如果您希望使用内部命名结构,使用自定义名称可能会有所帮助,尽管如果您希望对两种不同的产品使用相同的字体,这可能会造成混淆。
接下来,我们将定义新字体的font-style
。这很重要,因为根据font-style
是什么,我们可以有不同的 Droid Sans 字体加载变体。作为一名开发人员,这是你需要小心的地方,确保你不会在不需要的时候加载 10 种不同的字体。这不仅会弄乱你的代码,还会加载不必要的资源,从而降低用户体验。在这种情况下,我们只想使用这种字体的默认或“正常”外观;也就是说,我们不想要斜体。
在我们的font-style
之后,我们有我们的font-weight
。这就像它前面的属性一样,可以用来加载不同版本的字体,比如 Droid Sans Bold。
最后,但并非最不重要的,我们有我们的src,
,它用来告诉你的计算机在服务器上哪里可以找到我们的字体,以及在用户的计算机上该字体可能被称为什么。如果字体在用户的机器上,这可以为我们节省一些带宽,因为不必下载我们已经拥有的文件。虽然在大多数情况下本地加载更可取,但是您可能会遇到希望从另一个位置加载字体的情况,例如当您测试各种字体样式以确定最合适的字体时。
谷歌网页字体
你可能还记得,在我们之前的一章中,我们从谷歌网络字体中抓取了 Droid Sans 字体。当时,我们并没有详细介绍谷歌网页字体,也没有说明它对设计者和开发者来说是多么不可思议的资源。
在你的下一个项目中使用谷歌网络字体(如图 8–6 所示)最有趣的一点是,所有的字体都是开源的,任何人都可以使用,不用担心违反许可和欠别人钱。
图 8–6。 新设计的谷歌网页字体网站的登陆页面
我听到的关于反对使用 web 字体的开发人员的最大抱怨之一是,字体往往有点笨重,这确实增加了加载时间。虽然由于有限的带宽,这在移动设备上可能很烦人,但谷歌已经考虑到这一点,并创建了一种简单的方法来缩小加载到用户浏览器中的字体文件的大小。
快速浏览一下清单 8–2。这里我们有了将 Chivo Google Web 字体添加到我们的 Web 应用所需的基本 HTML 标记。
清单 8–2。 将非优化版本的 Chivo 字体加载到我们用户的浏览器中。
<linkhref='http://fonts.googleapis.com/css?family=Chivo' rel='stylesheet' type='text/css'>
现在,如果你看一下清单 8–3,你可以看到我们是如何优化字体大小的,只加载我们需要组成单词“Hello World”的字母如果你需要在你的网站上加载一个只使用一次的字体,比如 logo,并且只使用几个字母,这是非常完美的。
清单 8–3。 加载 Chivo 字体的优化版本,只从字体中加载我们需要的字母
<link href='http://fonts.googleapis.com/css?family=Chivo&text=Hello%20World' rel='stylesheet' type='text/css'>
除了允许你优化你的字体,谷歌还会定期向谷歌网络字体目录添加新的字体。当我无聊的时候,它无疑是我最喜欢浏览的地方之一,因为你永远不知道什么时候你会发现一种神奇的字体,它会激励你坐下来开始编写新的代码。
要考虑的字体问题
就像使用照片一样,使用不同的字体和文本样式时,需要考虑和计划一些注意事项:
- 不同的风格是什么意思?我们之前提到过,我们可以指定字体的风格变化,比如将文本变为斜体或粗体,甚至使用不同的字体变体,比如使用印刷体。虽然这些变体提供了很大的灵活性,但是如果没有一致地应用,它们也会使用户困惑。您应该考虑是否希望块文本引用动作(例如,视频或音频剪辑的播放按钮),或者是否希望块文本以斜体显示(尽管如果文本不够大,在移动屏幕上很难阅读斜体)。但是,如果动作是斜体,照片下面的标题应该如何显示?粗体可能看起来太强了,或者可能意味着文本是一个链接。看到这是如何变得有点复杂了吗?为你的应用创建一个非正式的“风格指南”可以创造奇迹,产生一致和漂亮的输出,并有效地使用字体。
- 风格是否值得用流畅来交换?认知心理学——或大脑如何解读信息的心理学——的研究人员使用术语“流畅性”来指处理信息的难易程度。一个字体的粗细足以保持其行的分离,并且没有衬线,这是非常流畅的,这意味着读者可以很容易地快速解析它并理解你在说什么。另一方面,“优雅”笔迹的波浪形字体或非常方方正正的块状字体(字母开始相互碰撞)非常不流畅。这意味着用户将需要更长的时间来阅读这些部分。在某些情况下,这是一件好事——我们可能希望将注意力吸引到应用的某个部分,并确保用户花足够的时间来考虑它(例如,所有数据都将被删除的通知可能是一个很好的块字体候选,假设它不会如此不流畅,以至于导致人们出于沮丧而绕过它!).然而,在其他情况下,我们可能希望关注速度而不是风格。
- 颜色不仅仅意味着字体。虽然在应用中使用不同的字体来创建不同的外观很有诱惑力,但是人们应该考虑简单地改变文本的颜色是否足够了。在一个方案中使用颜色搭配得很好(也许使用颜色组合(
[www.colorcombos.com/](http://www.colorcombos.com/)
)比改变字体更能创造丰富的外观。前面讨论的规则同样适用于颜色(即,颜色应该一致地向读者传达相同的信息——这是警告、行动还是描述?);但是从加载和配置上来说,成本比使用字体的成本要小。
现在,我们已经讨论了如何使用字体、图像和图标来创建您想要的外观,我们将进入一个更广泛的环境——CSS 框架,在这个环境中,我们可以获得一个完整的 CSS“皮肤”来包裹我们的应用,具有深远的影响和后果!
CSS 框架
不管你喜欢还是讨厌它们,在过去的几年里,CSS 网格框架越来越受 web 设计新手和开发人员的欢迎。我把这一切都归咎于 Blueprint,一个在 2008 年左右变得非常流行的 CSS 框架。Blueprint 的创建是为了减少开发时间,并防止开发人员在忘记边距和填充之间的区别时沮丧地拔掉自己的头发 1 。最后一部分对我来说可能是一个小笑话,但它离真相不远。
正如我们在本章开始时所讨论的,像级联样式表这样的工具被开发人员广泛使用,但实际上是设计人员使用的工具。开发人员可能不会对 CSS 感到舒服——有点类似于让一个木匠来管理家里的电线。他可能知道足够去做,但话说回来,他可能会把事情搞得比他从未开始做时更糟。
虽然这听起来可能有点荒谬,但我已经多次为客户承担了一个项目,处理已经由开发人员或工程师编写的样式,很明显,他们已经用编写代码逻辑的相同方式处理了编写 CSS 的整个方法。将 CSS 视为设计标准,而不是代码中的规则。
如果你听起来像是那些似乎不“理解”CSS 的人之一,那么你可能想看看已经被创建来使你的生活更容易的一些框架。从长远来看,它们将帮助您快速解决开发人员在尝试将他们的内容样式化到网格中时遇到的许多常见问题,从而节省您的时间和金钱。它们也是很好的工具,可以用来如饥似渴地了解它们是如何工作的。
对罗科来说,这不是什么大问题。对乔恩来说,他需要所有他能得到的大块头发!
谁知道呢,在玩了一会儿 CSS 框架并学习了它们是如何操作的之后,你可能最终会对样式表语言产生真正的热情,并且最终会比你实现一个已经做好的解决方案更快地手工编写你自己的 CSS!
在下文中,我们强调了三个框架,从最简单的开始,它为你解决了许多桌面/移动 CSS 难题——1140 像素网格。其次是更少的框架 4 ,它提供了一些支持,但有更多的修改空间。最后,我们讨论一下 320 和更高版本,Less Framework 4 的一个版本,它允许您快速启动并运行,而无需大量的调整!
1140 像素网格
1140px 网格是另一个 CSS 框架,目前在互联网上运行,拥有相当大的追随者。该框架本身是在 Creative Commons 许可下发布的,可以随意使用、复制、分发和操作。
创建该框架是为了适应屏幕分辨率的最新变化,特别是当前 1280 × 800 计算机屏幕的突出地位,同时也是为了适应更小的显示器。直到最近,大多数网站都是围绕这样一个理念设计的,即普通用户的浏览器应该有大约 1024 像素宽和 800 像素高的分辨率。然而,在过去的几年中,许多开发者已经注意到,平均用户屏幕分辨率一直在缓慢地提高。
记住这一点,1140 像素网格的设计是为了利用现在更常见的屏幕分辨率,并使用响应式 web 设计以流畅的方式缩小,正如本书第五章所讨论的。这对开发人员来说意味着,如果您正在为桌面用户体验设计和构建一个 web 应用,那么您只需要为一个更大的显示器设计一次应用。然后,对于任何分辨率较低的浏览器,框架会智能地缩小应用。
然而,这并不总是一劳永逸的过程,有时它确实需要你进去对 CSS 做一些修改,让它看起来完全像你想要的样子。例如,网格将允许你使用许多列;但是,如果您希望您的应用在桌面和小浏览器上都有吸引力(类似于我们在本书前面讨论的,当讨论专用移动版本而不是一个页面同时提供两者时),您可能希望将自己限制在一个网格中。总而言之,如果你必须先做大再做小,你可以在[
cssgrid.net/](http://cssgrid.net/)
查看 1140px 网格框架,让你的生活变得轻松一点
少框架 4
Less 框架([
lessframework.com/](http://lessframework.com/)
,图 8–7)是 CSS 网格系统中的另一个例子。Less Framework 包含三种不同的排版预设和四种不同类型的布局,比一些竞争对手更强大。与 1140px 网格框架不同,该框架是根据平板电脑和智能手机的想法构建的。如果您正在构建一个只需要在手机或平板电脑上显示的应用或页面,那么 Less Framework 4 是比 1140px Grid 更好的选择。虽然这个框架是为 iPad 和 iPhone 用户开发的,但它仍然可以在基于 Android Webkit 的浏览器上很好地工作。
然而,Less 框架并不像其他一些选择那样是一个为你做任何事情的框架。为了更好地利用这个框架,建议您熟悉 HTML 和 CSS。你们三个不一定要成为最好的朋友,也不一定要邀请对方来吃圣诞晚餐,但是偶尔在饮水机旁聊聊天,看看孩子们过得怎么样,也不会有什么坏处。
作为我们意思的一个例子:考虑 Less Framework 4 有两种移动布局:普通和宽。宽布局适用于横向移动电话。考虑到用户可能会因为各种原因在不同时间切换手机的方向,您可能希望在 JavaScript 或 HTML 中采取措施,根据方向重新呈现或重新组织材料。一个在 wide 上看起来不错的应用在正常情况下可能看起来很糟糕——这是你应该确定、测试和调试的事情。
图 8–7。 少了谷歌 Chrome 桌面浏览器中看到的框架 4 主页
320 及以上
另一个知识共享许可框架 320 和更高版本([
stuffandnonsense.co.uk/projects/320andup/](http://stuffandnonsense.co.uk/projects/320andup/))
从一开始就考虑到了更小的观看门户。正如 320 和 Up 网站所说,这个框架是“小屏幕优先”。由安迪·克拉克和基思·克拉克创建,基于 Less Framework 4,这个 CSS 样式集合带有预设的排版样式和一些额外的 JavaScript 代码,用于调整图像大小和提高网站在旧浏览器中的综合性能。正如您在图 8–8、8–9 和 8–10 中所看到的,它可以很好地适应当今 Android 设备上使用的不同常见方向和屏幕。
当您希望避免桌面和移动版本的应用之间的任何重叠,并且当响应是您的主要目标时,320 和更高版本是一个不错的选择。320 和更高版本还包括比 Less Framework 4 更多的定制,例如用于调整照片大小的内置 JavaScript 库,以及应用适当优化的 CSS。如果 Less Framework 4 的定制让您望而生畏,那么您可能希望从 320 开始,看看您是否真的需要深入了解!
图 8–8。 在安卓 2.3.4 姜饼手机上以纵向模式查看的 360 及以上网站
图 8–9。 在 Android 2.3.4 姜饼手机上以横向模式浏览 360 及以上网站
图 8-10。 在桌面浏览器中用谷歌 Chrome 查看 360 及以上网站。请注意,该图和之前的图都显示了相同的标记代码,但页面看起来却因用户浏览器分辨率的不同而有所不同。
对比框架:关于乔恩!!
选择正确的框架可能很难,所以在这个例子中,我们将采用相同的内容并在所有三个框架中呈现它。材料是我(乔恩的)个人网站上的简历。据推测,这将是我的移动 Jon 应用中的一个页面。首先,让我们看看在桌面网络浏览器中呈现的原始页面(参见 Figure 8–11)——这是我们试图在某种程度上在移动版本中模仿的。
图 8–11。 乔恩的 Biosketch 渲染在桌面上。
首先,我们将在 1140 像素的网格中渲染它。回想一下,这个框架的优势是我们可以从桌面到移动设备获得类似的东西。首先,移动视图(图 8–12):
图 8–12。 Jon 的 Biosketch 使用 1140px 网格在手机上渲染
现在,让我们看看桌面浏览器中的视图(Figure 8–13):
图 8–13。 乔恩的 Biosketch 使用 1140px 网格渲染在桌面上
这并不完全理想——两个单栏布局在 Mobile 中堆叠在一起。然而,在桌面视图中,它们是并排显示的,非常“拥挤”。通过调整 HTML 代码,我们可以缓解这种情况,并使桌面版本看起来更好(见图 8–14),同时也保持移动版本。在这种情况下,问题很简单,我们使用了两个“onecol
”框,而不是左边的onecol
和右边的elevencol
(填满屏幕的其余部分)。1140 像素的网格要求从一开始就知道要填充多少列,并使用适当的 div 语句。有 12 列,所以无论您使用什么组合,总共应该是 12 列(即,在本例中,1 个 onecol 和 1 个 elevencol)。您还会注意到,最后一列的 div 语句中有单词“last ”(参见清单 8–2),它让 1140px Grid 知道它是行中的最后一列,因此应该填充页面的其余部分。
图 8–14。 乔恩的 Biosketch 的桌面视图片段,修复
1140px 示例的代码可以在清单 8–2 中看到:
清单 8–2。 乔恩的 biosketch 使用了 1140px 的网格。
`
<head> <meta charset="utf-8" /> <title>The 1140px Grid · Fluid down to mobile</title> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="css/9781430239574.css" type="text/css" media="screen" /> <script type="text/javascript" src="js/css3-mediaqueries.js"></script> <style type="text/css">body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.container p {
color: #000;
line-height: 16px;
background: #FFF;
text-align: left;
margin: 20px 0 0 0;
}
Jonathan E. Westfall, PhD, born 1982, is a researcher and technologist working in New York City at Columbia Business School. His current research focuses on` `the variables individuals use when making economic and consumer finance decisions, specifically strength of handedness, a variable correlated with a number of decision- making and cognitive psychology tasks and measures. Dr. Westfall also conducts research on consumer financial decision making, and applications to both marketing and public policy. His current appointment is as the Associate Director for Research and Technology at the Center for Decision Sciences, a center within Columbia Business School at Columbia University in New York City. Additionally, he holds an appointment as a Lecturer in the Columbia University Psychology department, periodically teaching a course in Judgment and Decision Making
现在,让我们在 Less Framework 4 中看看这个。首先,你会注意到 Less Framework 擅自应用了一些基本的字体格式,这是 1140px 允许我们手动完成的,或者如果我们愿意的话,根本不用。如果你想要那种“移动”的感觉,这可能会帮助你更快地起床和跑步,如图图 8–15 所示。
图 8–15。 乔恩的 biosketch 使用了少框架 4 的框架
代码实现起来也相当简单,主要是因为我们不用担心桌面版本。清单 8–3 给出了完整的代码,引用了 Less Framework 4 包中提供的 CSS。
清单 8–3。 乔恩的 biosketch 使用了少框架 4 的框架
`
Jonathan E. Westfall, PhD, born 1982, is a researcher and technologist
working in New York City at Columbia Business School. His current research focuses on
the variables individuals use when making economic and consumer finance decisions,
specifically strength of handedness, a variable correlated with a number of decision-
making and cognitive psychology tasks and measures. Dr. Westfall also conducts research
on consumer financial decision making, and applications to both marketing and public
policy. His current appointment is as the Associate Director for Research and Technology
at the Center for Decision Sciences, a center within Columbia Business School at
Columbia University in New York City. Additionally, he holds an appointment as a
Lecturer in the Columbia University Psychology department, periodically teaching a
course in Judgment and Decision Making
`
最后,我们来看看我的 320 以上的 biosketch。它使用背景图像和自定义字体进一步应用格式,如您在 Figure 8–16 中所见。
图 8–16。 乔恩的 biosketch 采用 320 及以上框架
与 1140 像素的网格相比,代码更简单,因为我们不关心桌面渲染。参见清单 8–4。
清单 8–4。 乔恩的 biosketch 使用了 320 和 Up 框架
`
<head> <meta charset="utf-8"> <title>320 and Up Version</title> <!-- http://t.co/dKP3o1e --> <meta name="HandheldFriendly" content="True"> <meta name="MobileOptimized" content="320"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial- scale=1.0"> </head>Jonathan E. Westfall, PhD, born 1982, is a researcher and technologist working in New York City at Columbia Business School. His current research focuses on the variables individuals use when making economic and consumer finance decisions, specifically strength of handedness, a variable correlated with a number of decision- making and cognitive psychology tasks and measures. Dr. Westfall also conducts research on consumer financial decision making, and applications to both marketing and public policy. His current appointment is as the Associate Director for Research and Technology at the Center for Decision Sciences, a center within Columbia Business School at Columbia University in New York City. Additionally, he holds an appointment as a Lecturer in the Columbia University Psychology department, periodically teaching a course in Judgment and Decision Making
如您所见,每个框架的格式略有不同,允许您选择自己喜欢的格式。虽然 Less Framework 4 和 320 及更高版本在外观上看起来非常“移动”,但它们缺乏与桌面版本的任何交叉功能。另一方面,1140 像素的网格在风格上不是很“移动”,这意味着你需要手动添加。然而,作为一个加号,你在一个文件中得到一个桌面和移动版本。
为自己选择合适的 CSS 框架可能会很困难;然而,考虑到我们在这里讨论的框架,它应该给你一个为你的应用选择正确的框架的起点!
土坯烟花
Adobe Fireworks,如 Figure 8–17 所示,可能是我的 Web 开发实用工具带中第二常用的工具,这是有充分理由的。这个非常容易使用的矢量和位图编辑程序非常适合润色或压缩图形,以及模拟设计和新项目的原型。
最初由 Macromedia 创建并拥有,后来于 2009 年被 Adobe 收购,这款面向各地网页设计师的神奇应用没有像 Adobe 的旗舰照片编辑程序 Photoshop 那样受到如此多的关注,但这没关系,因为它有许多精彩的功能,很容易使它成为一个应该包含在您的军火库中的程序。事实上,对于 web 开发人员和设计人员处理的许多任务来说,Fireworks 是更好的产品。
就像 Adobe Photoshop 一样,Fireworks 也布满了键盘快捷键,以满足高级用户的需求,同时保留了一个稀疏的 UI,其中包含易于查找的工具、选项和属性,使 Adobe Fireworks 的任何新用户都可以轻松地找到应用。
图 8–17。 运行在 Windows 7 电脑上的 Adobe Fireworks CS5
Adobe Fireworks 最酷的功能之一是能够在其特殊格式的 PNG 文件类型中创建页面,这将允许设计人员非常整洁地创建漂亮的布局,并传递给他们的本地开发人员。这些页面的工作原理是,每当您需要一个新页面时,它都会为您提供一个新的画布。您可以在 Fireworks 中按 F5 键轻松访问您的页面。
查看页面时,如图 8–18 所示,只需点击鼠标,即可选择您正在查看和处理的页面。一旦你进入了新选择的页面,你可以跳到 Layers 标签页,查看与当前选择的页面相关的所有层,而不是在一个画布中处理五个或更多页面的层。
图 8–18。Adobe Fireworks 页面面板的特写
当终于到了开始写代码的时候,保持你的设计更整洁会对你有所帮助。你必须花越少的时间在无尽的层中滚动,试图从你的模型中找到合适的资源用于你的网站,越好!
最重要的是,当要向客户展示你的原型时,给他们留下你整洁有序的印象总是一个好主意(如图 8–19 和 ?? 图 8–20),并且你将把你在模型和原型上花费的同样的关心和考虑用于构建他们的站点或应用。
图 8–19。 Adobe Fireworks CS5 在 Windows 7 机器上查看设计作品的第 1 页
图 8–20。 Adobe Fireworks CS5 在 Windows 7 机器上查看设计作品的第 1 页
总结
嘿,看那个!看起来我们又下了一章,你有了更多的知识和其他美味的珍闻在你的脑壳里浮动。现在,您已经有了一个更好的想法,知道如何让您的移动 web 应用长出爵士乐手并在房间里跳舞。
在这个世界上,我们的时装设计师甚至可以让无家可归者的服装看起来很时尚,无论你走到哪里,都有人在称赞他们的新设备有多“漂亮”,应用和网站越来越不可接受这种“新设计”的外观和气味。随着 Google+的发布和新 Google+主题在他们提供的无数产品中的推出,甚至谷歌也清理了其设计丑陋但功能强大的产品的行为!然而,正如我们到目前为止讨论的所有概念一样,权力越大,责任也就越大。通过以一致的方式使用这些工具,您可以生成一个视觉上令人惊叹且有用的应用!
九、基于位置的应用
移动开发者面临的最大机遇之一是手机和平板电脑等 Android 设备的本质所带来的丰富选择:它们是移动的!虽然传统桌面应用的用户位置可能很有趣,但很少有人会拿起一台台式机并整天带着它到处走。但是移动设备会移动,它们的位置(也就是设备用户的位置)会影响一切,从游戏到搜索产品和信息,从语言设置和设备行为等各种事物的细微差别和选项。
在这一章中,我们将探索 Android 设备的 HTML5、CSS 和 JavaScript 中的地理定位。我们将探索一个简单的支持位置的应用,并了解如何利用 HTML5 的特性和功能。所以,让我们开始行动吧!
地理定位的机制
为了让你的地理定位想法成为现实,了解所有移动部件的功能是很重要的(抱歉,我们忍不住开了这个玩笑)。在任何以 Android 为目标的地理定位 web 应用中,有两组功能非常重要。首先,给定的 Android 设备向任何基于 web 的定位应用提供了什么功能?第二,HTML5(以及在较小程度上,CSS3 和 JavaScript)的哪些特性提供了定位功能?最终,一个设备能告诉我们它的位置,一个 Web 应用能利用这些信息做什么,这两者的结合将会为你所能达到的目标设定界限。让我们来看看两者的背景。
了解设备功能
无论你有什么样的设备,它都可以依靠一系列令人惊讶的技术来提供基于位置的数据。即使对于缺乏专用硬件的设备,选项仍然存在。您使用的设备(以及我们将使用的示例)通常可以访问以下一项或多项功能:
- 全球定位系统(GPS)—设备中接收和解码 GPS 卫星信号(或类似的竞争服务,如伽利略或 Glonass)的硬件。精度可以精确到 1 米,尽管这可能会因缓存和其他影响而降低。
- WiFi 三角测量—您的设备使用 WiFi 进行网络连接,但它也可以结合使用已知的 WiFi 网络 SSID 名称以及 dBm 中的信号强度来进行三角测量。这往往比 GPS 粗糙,并且在建筑物较少的区域或有许多瞬时热点的区域受到影响。请注意,您的用户可能会因为功耗或节能原因而禁用 WiFi。
- 蜂窝塔三角测量—与 WiFi 技术类似,您的设备知道它在任何给定时间注册了哪个蜂窝,也知道其他哪些蜂窝是可检测的(以及信号强度和相关定时信息)。即使附近只有一个手机信号发射塔,该信号发射塔服务区的粗略水平仍然可以知道您设备的位置。
- IP 地址分配—虽然您的设备在连接到 WiFi 或移动网络时很可能会收到一个动态 IP 地址,但您的提供商通常拥有一组已知的 IP 地址块,以及在不同地理区域的已知分布模式。虽然这不会将你的设备定位到最近的米,但它可以很好地确定你是在堪萨斯州还是加德满都。
如您所见,有多种方法支持确定设备位置的实际机制。不管设备支持哪种方法,在编写 web 应用时,您都不必考虑选择给定的机制。您必须考虑的是地理定位应用的另外两个管理约束。
第一个这样的约束是用户对设备范围的位置特征的设置的选择。确切地说,用户是否启用或禁用了 GPS,就此而言,他们是否允许或禁止其他位置确定机制精确定位设备?图 9–1 显示了用户可以控制的正常设置
图 9–1。 安卓 4.0 中的安卓设备位置服务设置
这些设置的功能非常明显。关闭 GPS 卫星,它将无法用于任何服务,包括浏览器的位置相关功能。这就是我们第二个约束的来源。仅仅因为 HTML5 规范概述了地理定位的 API,并不意味着用户的浏览器已经实现了所有(或者任何!)的特性。
了解 HTML5 的功能
对于一个给定的设备来说,拥有一系列的定位功能当然很好,但是这些功能如何与您实际的 web 应用联系起来呢?答案来自 HTML5 对文档对象模型(DOM)所做的更改,它现在包含了一个 API 来支持一系列专门用于位置相关活动的 DOM 事件。这是一个相当重要的微妙之处。HTML5 本身并没有实现位置感知特性。相反,它包含了一个 API 规范,用于说明 web 浏览器和 web 服务器或应用如何处理位置数据。这影响到作为开发人员的您,因为您不仅需要考虑 HTML5 规范所说的地理定位活动应该发生什么,还需要考虑每个浏览器是如何实现来实际支持这些功能的。不要想得太远,一个例子是,如果一个 web 应用要求定位,那么一个给定的浏览器可能会使用什么样的第三方地理定位服务。
检测浏览器地理位置支持
浏览器中几乎所有地理定位活动的基础都是navigator.geolocation
对象。我们可以使用这个基础作为一个简洁的简写来确定浏览器是否至少实现了 HTML5 地理定位规范的基础。清单 9–1 展示了我们的第一个代码示例:一个确定给定浏览器是否理解navigator.geolocation
对象的基本 HTML 和 JavaScript 示例。
清单 9–1。 简单地理定位支持检查
`
`
您可以在文件ch09–example01.html
中找到这段代码。只需将它置于您选择的 web 服务器的控制之下,然后将您的浏览器(桌面或移动)指向它以查看结果。
注:这个例子,以及本章中的其他例子,通常应该从 web 服务器上运行,而不是直接从桌面或移动浏览器上作为文件打开。这是因为大多数当代浏览器阻止任何基于
file://
的 URL 访问地理位置 API。简而言之,任何访问一个navigator.geolocation
对象、它的方法或者数据成员的尝试都会无声无息地失败。这主要是出于安全考虑,这本身就足以构成一个章节。选择一个你觉得合适的 web 服务器,比如 Apache 或者 Nginx,你就不会看到任何问题。
图 9–2 和 9–3 分别显示了在桌面浏览器和移动浏览器中运行我们的第一个示例时应该看到的结果。
图 9–2。 我们简单的桌面浏览器地理定位支持测试结果
图 9–3。 我们简单的移动浏览器地理定位支持测试结果
关于我们测试地理定位支持的第一段代码,有两点需要注意。首先,确定给定浏览器的支持真的很简单(尽管参见“什么可能出错?”本章稍后将对支持地理定位的设备实际上何时不合作进行更细致的分析)。第二是在桌面应用中使用 JavaScript alert
函数大部分时间都很烦人,在小屏幕移动设备上就更烦人了。你们当中有经验的一般 web 开发人员会知道这一点,而对于新手来说,这是一个考虑将工作代码推送到一个功能中的好时机,这个功能可以方便地重用,而不会打扰好用户。清单 9–2 重新分解我们的检测示例,创建一个单独的geoSupport()
函数,我们可以根据需要重用它。
清单 9–2。 简单地理定位支持检查修订版
`
`
当然,您可以更进一步,将这个函数放到一个单独加载的 JavaScript 源文件中。如果你愿意,你可以这样做并修改本章后面的代码。
探索我们的示例应用
是时候介绍我们的示例应用了。除非你一直生活在岩石下,否则你可能知道随着移动设备的爆炸,流行的“签到”风格的应用的范围。无论是 Foursquare、Latitude、TripIt 还是其他类似的应用,这些都允许你向全世界宣布你的位置——大概是为了让你的粉丝和狗仔队可以跟踪你的著名动作。这些应用中有许多是作为给定设备的本机应用实现的,但是没有什么可以阻止我们开发与任何设备的浏览器中可用的 web 应用相同风格的应用。
我们的应用在ch09–example03.html
中是完全独立的,您可以从您选择的 web 浏览器中运行它。让我们先从用户的角度来看看它(显然)做了什么。将我们的浏览器指向该页面,会弹出我们新的基于位置的签到风格应用的非常简单但实用的主页,您可以在图 9–4 中看到。
图 9–4。 调用地理定位之前的示例应用
目前,这还不完全是平面设计的杰作,但我们会做到这一点。现在,让我们看看当我们点击“地理位置登记”时会发生什么按钮。Figure 9–5 显示了我们的页面动态更新,看起来像是一组纬度和经度坐标。之所以看起来是这样,是因为这正是它的本来面目——而且它们正是来自这个测试进行的地方。
图 9–5。 由于我们的示例应用,我们的位置暴露了出来。
构建我们的基本地理定位应用
此时,您可能想知道到底需要多少逻辑和代码才能让这个看似简单的应用工作。让我们看一下清单 9–3,在这里我们将看到 ch09–example03.html 文件的完整代码集。
清单 9–3。 地理位置登记示例
`
Ready to check in...
Ready to check in...
`
让我们把它分成四个部分,这样你就可以理解正在发生的事情,也可以自己修补和修改代码,这样你就可以在我们进行的过程中探索各种可能性。
地理定位支持测试,Redux
我们在本章前面介绍的supportsGeo()
函数已经被调整为返回一个布尔值,所以我们可以在其他表达式中使用它的结果:
function supportsGeo () { if (navigator.geolocation) { **return true;** } else { return false; } }
这比前一个返回字符串值的版本提供了更多的实用工具。
为动态页面更改创建实用函数
接下来,我们引入了一个简单的实用函数来控制代码中任何地方的<div>
元素的动态 HTML 行为。changeDiv()
函数接受一个<div>
元素的名称和所需的文本更改,并执行必要的更改:
function changeDiv (name, data) { var div = document.getElementById(name); if(div) { div.innerHTML = data; } }
这与地理定位并不严格相关,但是我们认为您会同意,通过从主逻辑中移除这些变化的机制,这将使我们的代码示例更加清晰。
复习基本的 HTML
在我们的例子中,我们的 HTML 代码几乎是无辜的旁观者。它提供了一个名为<div>
的元素myCheckIn
,我们的函数将在这个元素上施展它们的地理定位魔法。
`
Ready to check in...
Ready to check in...
表单和输入按钮调用我们的中心函数checkIn()
,它将执行实际的地理定位工作。
钻研坐标
好了,别在边缘跳舞了!在我们的小例子中,checkIn()
函数执行关键任务。
function checkIn () { var geoData = ""; if (supportsGeo()) { navigator.geolocation.getCurrentPosition(function(position) { geoData = position.coords.latitude + ", " + position.coords.longitude; }); <!-- alert("Confirm geolocation access before clicking OK"); --> } else { geoData = "Your browser does not support HTML5 geolocation"; } changeDiv ("myCheckIn",geoData); }
geoData
变量最终保存我们得到的纬度和经度。我们调用我们的supportsGeo()
函数来确保我们的浏览器能够支持我们的意图。然后我们通过调用navigator.geolocation.getCurrentPosition()
函数进入正题。这是 HTML 地理定位的核心功能之一,在http://
[
dev.w3.org/geo/api/spec-source.html](http://dev.w3.org/geo/api/spec-source.html)
有一页又一页的重载定义。
现在,您需要知道的是navigator.geolocation.getCurrentPosition()
是一个异步函数,在这里使用的形式中,它传递一个回调函数,一旦浏览器和底层硬件对调用者当前位置的请求作出响应,就调用这个函数。我们的position
回调为我们的geoData
变量分配了两个数据成员:position.coords.latitude
和position.coords.longitude
值,它们相当于调用者的纬度和经度。
剩下的就是我们调用实用程序changeDiv()
函数来更新我们的 HTML 页面,瞧!当然,我们也确保不支持地理定位的浏览器有适当的信息,表明不支持我们的地理定位工作。
注意:您可能会注意到我们有一个注释掉的对
alert()
的调用,要求用户在继续之前确认地理位置访问。如果你不允许网页自由询问你的位置(你没有理由这样做),那么当你的浏览器询问时,你需要确认访问。但是,即使是最快的人也无法在对navigator.geolocation.getCurrentPosition()
的调用返回之前做到这一点,即使这是异步的。此时,回调将返回一个我们的代码当前没有捕捉到的错误(但请参阅本章后面的“可能出错的内容”一节),我们的示例将会无声地失败。在测试中取消对此警告的注释,以便更好地控制navigator.geolocation.getCurrentPosition()
的异步行为。
应对安卓地理定位世界的四个角落
我们最初的示例应用介绍了几乎所有利用地理定位的 web 应用的一些关键构件。为了完善地理定位的世界,我们需要解决四个问题:我在哪里?,我是什么?,还能出什么差错?,还有我要去哪里?
我在哪里?
我们在前面的例子中使用了position.coords.latitude
和position.coords.longitude
数据成员,已经触及了确定设备位置的基本原理。您可能会想,除此之外,确定位置没有更多的事情,但是您应该考虑一些额外的数据点。
虽然纬度和经度可以告诉你是在夏威夷还是在喜马拉雅山,但你可能会同意在这两个地方你都关心另一个维度:海拔!HTML5 规范包括一个position.coords.altitude
数据成员来提供高于名义海平面的高度。注意,对它的支持是不完整的,甚至 Android 模拟器和它的浏览器在 Android 开发工具(ADT)的许多版本中都不支持它。
除了纬度、经度和高度,你可能还会关心你得到的读数有多准确。我们在本章开始时介绍了四种可能的定位机制,每一种都有不同的精确度。两个额外的数据成员是可用的,position.coords.accuracy
和position.coords.altitudeAccuracy
,为所提供的任何地理位置数据提供误差容限。
有趣的是,没有直接的方法可以确定哪个定位机制被用来提供坐标。如果您询问另一个数据值position.coords.satellites
,您可以推断它是 GPS,如果您的浏览器和设备支持,它将返回用于提供 GPS 定位的卫星数量。唯一的问题是,如果你的设备缺乏 GPS 支持,或者无法获得 GPS 定位,这两种情况都将返回 NULL。因此,对于是什么导致了卫星计数的缺失,你将处于一种模糊的状态。
我是什么?
我们已经在很大程度上讨论了回答我是什么的最佳方式。提问。HTML5 地理定位规范没有提供确定设备上存在什么特定硬件的详尽方法。处理这个问题的最好方法是使用supportsGeo()
函数,或者类似的函数,我们在上一节中已经介绍过了。这就把我们的问题变成了:我是什么?设备和浏览器组合是否支持地理定位?
有什么可能出错?
想象一下,你编写了一个很棒的签到应用,来处理 Foursquare 和 company 这样的公司。你的用户是快乐的,世界是美好的。不会出什么差错的,对吧?或许我们很快会联系上。
在任何设备上,在任何时间,您都会发现自己不得不处理一系列问题,这些问题会阻止您的地理位置代码返回准确的值,甚至根本不返回任何值。这可能是天气、太阳和行星的排列(实际上,这并不像你想象的那样是个笑话),甚至是用户干预。不管是什么原因,都有明确定义的错误状态,您应该在代码中处理它们。
在 HTML5 规范下可能出现的四种主要错误情况是:
- 超时:在允许的时间内没有定位机制响应定位数据。
- POSITION_UNAVAILABLE :您的设备有一个或多个有效的定位机制(如 GPS 或 WiFi),但没有一个能提供有效的位置数据。
- PERMISSION_DENIED :由于权限的原因,浏览器被阻止访问 HTML5 地理定位 API。用户在提示时阻止了访问,或者浏览器被明确配置为阻止访问。
- 未知 _ 错误:由于未知原因,位置数据不可用。
知道错误条件是什么当然很好,但是如何利用它们呢?你会记得我们说过getCurrentPosition()
函数有无数的重载版本。其中一个重要的子集包括一个常见的模式,接受一个用于报告位置的回调函数作为第一个参数,接受一个用于错误处理的回调函数作为第二个参数。看看我们的示例应用的下一个迭代,在文件 ch09–example03.html 中。关键的变化是我们的checkIn()
JavaScript 函数,现在看起来像这样。
function checkIn () { var geoData = ""; if (supportsGeo()) { navigator.geolocation.getCurrentPosition( function(position) { geoData = position.coords.latitude + ", " + position.coords.longitude; ** },** ** function(error) {** ** switch(error.code) {** ** case error.TIMEOUT:** ** alert ('Geolocation returned a timeout error');** ** break;** ** case error.POSITION_UNAVAILABLE:** ** alert ('Geolocation returned a position unavailable error');** ** break;**
** case error.PERMISSION_DENIED:** ** alert ('Geolocation returned permission denied (did you deny access?)');** ** break;** ** case error.UNKNOWN_ERROR:** ** alert ('Geolocation encountered an unknown error');** ** break;** ** }** ** }** ); <! -- alert("Confirm geolocation access before clicking OK"); --> } else { geoData = "Your browser does not support HTML5 geolocation"; } changeDiv ("myCheckIn",geoData); }
在粗体文本中,您将看到我们已经更改了getCurrentPosition()
函数的方法签名(重载版本),以注册我们的错误回调,并在返回任何错误条件时提醒用户。我们处于一个幸运的位置(好吧,我们在这里是在讽刺),Android 模拟器在响应地理定位请求时是出了名的慢和不稳定。出于这个原因,在真实设备上测试您的代码,以及在模拟器上测试,总是一个好的做法。在我们的第一次尝试中,作者没有真正尝试就设法触发了一个POSITION_UNAVAILABLE
错误。您可以在图 9–6 中看到结果。
图 9–6。 地理定位尝试返回 POSITION_UNAVAILABLE 错误
正如我们对supportsGeo()
函数所做的那样,您应该将这种错误检查方式转变成一种实用函数,在整个代码中使用,而不是用警告式的错误轰炸用户——尽管在这种情况下,这可能是有保证的。
我要去哪里?
为了完善我们正在探索的功能,我们可以问自己——或者更准确地说是设备——我要去哪里?有两种方法值得一提。首先,我们可信的地理位置数据成员包括两个更有用的值,position.coords.speed
和position.coords.heading
。这些指示设备的最后已知速度和最后已知方向。这些值对于像在地图上绘制运动这样的任务来说听起来很棒,理论上,这正是它们的设计目的。然而,实际上,令人不安的是,大量设备要么不报告这些值的数据,要么使用如此简单的 GPS 芯片组,以至于一开始就不支持收集数据。
这给我们带来了另一种方法。到目前为止,我们一直在展示navigator.geolocation
的getCurrentPosition()
方法。这个方法有一个对等方法,在确定随时间变化的位置以及随之发生的运动时,这个对等方法稍微复杂一些,也更有用。这就是watchPosition()
法。
在各种重载形式中,watchPosition()
方法的行为与我们的getCurrentPosition()
方法非常相似。它注册了一个回调函数用于正常定位,另一个回调函数用于错误处理,但它也接受一系列选项,包括一个定时器选项,说明它应该多长时间休眠和唤醒一次,以及继续调用您的回调。watchPosition()
将继续运行,直到被对clearWatch()
的调用停止,或者其父进程终止。清单 9–4 展示了我们正在进行的示例应用的下一次迭代,来自 ch09–example05.html。
清单 9–4。 地理位置追踪随时间(和运动!)
`
Ready to check in...
Ready to check in...
`
我们的实用函数supportsGeo()
和changeDiv()
是相同的,HTML 布局只是表面上的改变。关键是按钮现在调用我们的startWatch()
函数,它本身最终调用watchPosition()
。我们将它传递给常规回调函数和错误回调函数——onWatchSuccess()
和onWatchError()
——以及一个选项构造。在这种情况下,我们的选项如下所示
var watchOptions = { frequency: 5000 };
这有效地用于将 5 秒刷新频率传递给watchPosition()
功能。如果您自己运行代码,并选择您看到的开始监视按钮,您的浏览器应该大约每五秒钟更新一次您的位置。这在静态屏幕截图中有点难以展示,所以您必须自己尝试一下。如果你不是真的在移动,不要担心:你仍然应该看到坐标的更新,因为每个读数的精度会略有不同,这取决于用于定位的 GPS 卫星的数量,变化的本地干扰等。即使您正躺在沙发上休息,您也应该看到最低有效位的微小变化!
用地图拓展你的视野
到目前为止,您几乎肯定会同意,我们的示例应用并不完全是一件艺术品。因此,让我们添加一点活力,并介绍几乎无处不在的地理定位附件:地图!
向我们的应用添加地图
我们最新的例子来自 file ch09–example06.html,它使用谷歌地图将我们之前的签到例子提升到一个新的水平。它不只是报告数字坐标,而是将数字坐标与 Google Maps APIhosted at [
maps.googleapis.com/](http://maps.googleapis.com/)
一起使用来加载地图,并在地图上放置一个标记来突出显示您的位置。清单 9–5 是我们修改后的 JavaScript、CSS 和 HTML5。
清单 9–5。 地理定位和地图协同工作
`
您会注意到,我们的代码以一些 CSS 开始,以非常简单的 HTML 结束,它在页面加载后立即调用我们新的initialize()
方法。CSS 是 Google 的样板文件,允许 div 充当地图对象的容器。接下来,您将看到对 Google Maps API 的 JavaScript 引用(下面一行修改了格式以更好地适应此页面):
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js? key=AIzaSyBpoxnQbCGPTIcpIJ8YPO3pTNcJX3zbc4c&sensor=false"> </script>
这将调用带有特定 API 键的 Maps API。这是 Google 的技术,用于唯一地识别其 API 的用户,并限制对各种 API 的调用次数。你一定要注册自己的 API 密匙,这样你就可以控制自己的 API 访问,监控使用情况等等。
提示:在印刷的时候,免费的谷歌地图 API 被限制在每天 25000 次调用。要获得自己的地图 API 密钥,请访问
developer.google.com/
并按照说明进行操作。
我们的initialize()
函数与旧的checkIn()
函数非常相似,所以我们在这里不再赘述。关键的区别是它填充了纬度和经度两个局部变量,然后调用我们的新函数mapMe()
,来完成所有艰难的映射工作。
function mapMe(thisLat, thisLong) { var myLatlong = new google.maps.LatLng(thisLat, thisLong); var myOptions = { center: new google.maps.LatLng(thisLat, thisLong), zoom: 12, mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById("mapContainer"), myOptions); var marker = new google.maps.Marker({ position: myLatlong, map: map, title:"Hello World!" }); }
在mapMe()
中,我们完成了四个主要动作来展示我们的地图和位置标记。首先,我们使用从 HTML5 地理定位调用中收到的纬度和经度创建一个google.maps.LatLng
对象。接下来,我们构建一个名为myOptions
的选项对象,它由一个中心对象组成,用于选择浏览器中显示的地图的中心。zoom
值决定了地图中的起始缩放设置——这在移动设备上非常重要,因为默认缩放的比例太高,让用户使用不精确的捏和缩放技术放大几次。
然后,我们得到我们的映射的肉。我们创建一个新的google.maps.Map
对象,并传递将保存地图的元素(“mapContainer
是我们的 HTML 中的<div>
的 ID),以及我们构造的选项对象。这实际上在用户的浏览器中创建和显示了地图。创建之后,我们在地图上的相同坐标处创建一个google.maps.Marker
对象,并给它一个有意义的标签。图 9–7 中的结果不言自明。
图 9–7。 我们的 HTML 地理定位应用适应使用谷歌地图
其他基于位置的服务
我们已经了解了一种流行的地图服务,但是值得考虑将您的视野扩展到基本地理定位服务的其他提供商,以及地图切片(实际显示的图形“切片”)之类的提供商。
有几个原因可以让您考虑两面下注,至少确保您可以对其他服务执行相同的操作:
- 免费服务、规模和成本:许多服务,如谷歌、雅虎、必应、MapQuest、诺基亚/Ovi 等。提供某种程度的免费服务。通常的条款规定,在被限制和/或被要求付费之前,你和你的用户每天可以消费一定数量的地图展示。
- 不同的优势和劣势:一些服务,如谷歌,提供一些有用的东西,如一些地点的方向,公共交通的细节等。但在其他地方,像 Open Street Map 这样的服务提供了更详细、更新的数据,对于 OpenCycleMap 这样的服务,整个地图渲染和数据都专注于专业用途:在这种情况下,就是骑自行车的人。
- 抽象您的映射意味着您可以混合和匹配以满足您的需求。
提取地图数据和影像提供者的最重要原因可能是该领域正在快速发展和变化,良好的软件工程应该引导您尽可能将所有其他代码与这种易变的依赖关系隔离开来。这种不稳定性也可能是由提供者自己进行版本升级和更改的结果。
以下是一些有用的数据提供程序、地图切片提供程序、一体化服务,甚至是现成的制图抽象 JavaScript 库,您可能也想和通常的怀疑一起考虑:
- 开放街道地图(openstreetmap . org)-最初的开放数据提供商之一,现在也在众包基础地理位置数据的基础上提供其他服务
- cloud made(cloud made . com)-用于开放街道地图数据的地图切片提供程序。也是传单的提供者,一个开源的瓦片集合服务。
- map Nik(map Nik . org)-其他工具中常见的地图渲染器(包括此处列出的一些其他工具)。
- carto db(carto db . com)——一个基于云的地理空间数据库
- maps action(maps action . com)-一个现成的 JavaScript 库,提供地图服务和数据提供者的抽象。
- wax(map box . com/wax)——另一个现成的抽象层。来自同一家公司,TileMill 为瓦代。
这份清单可能会占满几页,但你会明白其中的意思。更令人兴奋的是,我们意识到,每周都有数十家这样的公司和服务提供商涌现出来。
游戏您的位置
即使你正在开发一个移动应用,也不要忘记你可以使用的工具来帮助测试和改进你的地理定位代码。例如,Eclipse 的 Android 开发人员工具插件中的 DDMS 为您提供了为仿真器设备设置任意坐标/位置数据的能力。当模拟器中有一个正在运行的项目时,您可以切换到 DDMS 模式,并打开模拟器控制台选项卡。您将看到设置原始坐标或直接提供 GMX 或 KML 数据的能力,如图 Figure 9–8 所示,您的仿真器浏览器可以使用这些数据。
图 9–8。 使用 ADT 和 Eclipse 在 Android 模拟器中设置坐标
如果运行 Eclipse 不是您的首选开发方式,您还可以使用 Android 的 adb 工具发出geo fix
命令来提供纬度、经度和高度。最后,如果你在没有连接到电脑的设备上调试应用,你可以使用免费的应用 Fake GPS location ( [
market.android.com/details?id=com.lexa.fakegps](https://market.android.com/details?id=com.lexa.fakegps)
)来设置你的位置。
畅游您的位置——尽情享受!
探索 web 应用地理定位功能的一个很好的方法是制作一个游戏——真的!使用上一节中的简单构建块,您可以构建游戏和活动,包括寻宝、定向活动,甚至驾驶、赛车和航海游戏。你会发现将我们的startWatch()
示例与我们的谷歌地图示例结合起来非常简单,可以创建一个“地图轨迹”风格的应用,在地图上实时绘制你的运动,并且根据游戏或比赛,还可以显示其他玩家的运动和位置,甚至整个其他游戏正在哪里进行。
在流行的“地理藏宝”运动中,可以找到地理定位功能的一个有趣的应用,比如我们在这里展示的那些。通过您喜欢的搜索引擎进行搜索以获得更多详细信息,或者继续阅读第十章并使用您自己的搜索界面进行搜索!
天空真的是无限的(事实上,你的地理定位数据也可以在你最喜欢的热气球上工作!).
总结
在这一章中,我们探索了作为移动 Web 开发者的你可以使用的基于位置的特性。我们的示例应用演示了如何使用许多核心地理定位功能,包括您在哪里,您的用户设备可能具有哪些功能,以及如何处理不断变化的位置。在第十章中,我们将探索更多关于地理定位应用开发的内容,也将涵盖其他在线服务和 API。
十、使用云服务:一个运输应用
掌握地理定位的基础知识是很好的,正如我们在第九章学到的那样,我们可以享受使用坐标的乐趣。一旦我们知道我们在哪里,以及有什么其他服务可以将我们的位置信息与其他数据混合,一个巨大的可能性就出现了。在这一章中,我们将利用我们的地理定位技术,并结合一些本地搜索功能,来构建一个个性化的公交应用。
我们的示例交通应用将找到您所在的位置,然后搜索离您最近的交通选项,无论是公共汽车站、地铁或火车站,甚至是机场。有了这些基础,您就可以将我们的 transit 应用扩展到几乎任何搜索加定位问题。
作为一名开发人员,在将基于 web 或基于云的服务与您自己的应用相结合时,您有许多选择来决定需要做多少工作。您可以选择使用公开可用的数据源构建自己的服务,使用完整的服务,或者构建混合服务。使用完整的服务在这一章中没有什么可谈的,所以我们选择了一种混合的方法,在这种方法中,我们将使用我们的地理定位技术来使用标准的搜索服务来提供有用的交通信息。
在我们深入研究代码、示例和解释之前,先花一分钟考虑一下您可能已经使用过,但可能没有积极思考过的各种构件。当构建任何类型的交通或运输应用时,我们希望将知识和数据结合起来,然后向用户提供一些有用的选项。这些将包括:
- 我是谁?在某种程度上,将“谁”在使用您的应用看作是实际用户、他们正在使用的设备及其功能的组合是很有用的。我们将重用我们在第九章中提到的检测代码来确定一个设备是否支持地理定位。
- 我在哪里?从第九章中,我们了解了如何通过用户 Android 设备和浏览器的地理定位功能来确定用户的位置。我们将从这一基本出发点出发,推动其他选择。
- 我想去哪里?这永远是一个微妙的问题。应用用户心中是否有一个位置,或者一种交通方式,或者实际上对如何在点与点之间旅行有任何偏好。这里要记住的两个关键点是,不要过早地限制应用中可用的选项,当您限制选项时,您是故意这样做的,并且将用户带在身边。例如,让他们知道其他路线或交通方式是可行的。
- 在线服务对我和我的位置有什么了解?这是一个很大的领域,涵盖了许多通用服务。给定一个位置,以及某种期望的旅程或目的地,考虑一下作为开发人员您可以获得哪些帮助,这样您就不需要自己做所有的工作。例如,Google、Yahoo 和 Microsoft 等公司的通用 web 搜索 API 可以处理多种类型的公交搜索,包括查找公交站点、测量距离和计算公交时间。使用这些工具,这样你就不必亲自动手了(当然,除非你的目标是在那个领域竞争)。
- 哪些专业服务知道我的位置?要考虑的最大领域,但不要被吓倒。专门的 API 和服务存在于令人眼花缭乱的一系列交通和运输类型中。世界上有大量的公共交通机构提供调度和路线数据,我们将在本章后面讨论。然而,许多还提供了一个直接的 API 来回答下一个服务、最近的服务等等的查询。聚合器的存在是为了提供泛提供商服务。一个例子是 Kayak 的航空运输搜索。通过思考(并询问)用户为什么想要进行一次特定的旅行,您可以进一步使用专门的 API。如果是去看电影,在餐馆吃饭,或者参加一些其他活动,像 Yelp、猫途鹰和其他服务提供 API 来搜索特定的地点和活动。作为应用开发人员,您可以创建有用的应用,将所有这些结合起来,以针对您的用户。
- 根据这些知识,我能得出什么有用的结论?利用您的应用收集的关于用户在哪里、他们想去哪里以及当他们到达那里时想做什么的数据,您开始描绘您的应用的流程和特性。专注于使用前面段落中描述的构建块,而不是重新发明它们,你将构建出引人注目的突出应用。例如,假设您知道用户想乘船从曼哈顿到布鲁克林去参观一家特定的餐馆。你可以将体验提升到一个新的水平,使用 OpenTable 在线服务来检查可用性,并注意到用户的餐厅已经预订一空,而是建议在哈德逊河上进行晚餐巡航。你让他们省去了一次令人失望的旅行,反而给了他们一次美好的经历。
因此,现在我们有了关于我们想要构建什么,以及为什么用户想要使用这样一个应用的大图。我们开始吧!
介绍“移动我”示例应用
我们的示例应用名为“移动我”我们将使用它来探索如何从任何位置找到附近的交通选项。当然,我们将使用我们在第九章中提到的技术来探测我们的位置。也就是说,我们将使用 Android 设备的地理定位功能来确定我们的纬度和经度,然后使用这些数据来驱动搜索服务,以找到我们首选的交通方式。
检查代码
清单 10–1 显示了第一版 Move Me 的代码。不要被它的长度吓倒:我们将对它进行分解,涵盖各个组件,让您了解它的工作方式,并启发您进一步使用这个示例,并将其扩展到您自己的想法。
清单 10–1。Move Me 应用源代码,第 1 版
`
Move Me! Transit Search Example
`
首先,让我们把简单的事情处理掉。在第九章的中,你应该已经从我们的地理定位介绍中认出了几个 JavaScript 函数。我们的老朋友supportsGeo()
和changeDiv()
都在,分别执行相同的地理位置支持检测功能和<div>
内容改变功能。
我们的示例代码稍微改变了我们调用一些我们正在使用的公共可用服务的方式。在这种情况下,因为我们已经选择使用 Google 的地图 API 和它的搜索 API,我们已经重构了加载 Google 的 JavaScript 库,首先加载基本框架,如下所示
<script src="http://www.google.com/jsapi?key=AIzaSyBU-TWQkYc-ynkeYIrd_aP0UFdyRieCyR0" type="text/javascript"> </script>
然后,在我们自己的 JavaScript 中,我们使用 Google 的google.load()
函数来额外加载地图 API 和搜索 API。如果您选择另一个服务或一组服务,比如 Bing、Yahoo、Baidu 或其他服务,那么加载 API 将会改变,以匹配这些服务的适当调用。对于我们的例子,代码非常简单:
google.load('maps' , '3', {"other_params":"sensor=false"}); google.load('search', '1');
注意:如果你对谷歌地图第二版 API 更熟悉的话,你可以恢复使用它,但当你使用依赖该版本 API 的网站时,你会看到反复的警告,说它已被否决。
处理全局状态
接下来,我们设置一些全局状态变量,让您很好地了解我们要做什么来使我们的 transit 应用工作。
var myLatLong; var myTransitMap; var myLocalSearch; var searchResults = [];
变量名有望描述它们的用途,但是如果有任何疑问,我们将使用myLatLong
来保存用户的坐标;myTransitMap
表示用户位置的地图,最终还将包括附近的交通选项;myLocalSearch
是搜索对象,它将包含我们查找相关交通选项的标准;最后,searchResults
数组将保存从我们的myLocalSearch
搜索返回的结果。
定制位置标记
如果你曾经使用过在线地图服务(任何种类的),你可能对用于精确定位的虚拟“地图图钉”很熟悉。在我们的例子中,当我们使用谷歌地图时,我们使用google.maps.Marker
对象来表示地图上的特定位置。我们没有使用 Google Maps 默认自带的普通红色地图标记图标,而是为 Move Me 应用制作了一些自定义标记。我们定制的google.map.MarkerImage
图形的代码如下。
var resultMarker = new google.maps.MarkerImage( "Red_Train_Marker.png", new google.maps.Size(20, 34), new google.maps.Point(0, 0), new google.maps.Point(10, 34)); var youMarker = new google.maps.MarkerImage( "You_Marker.png", new google.maps.Size(20, 34), new google.maps.Point(0, 0), new google.maps.Point(10, 34));
参考的两个.png
图像文件包含在本书的源代码中。第一个是Red_Train_Marker.png
,是谷歌地图使用的普通红色大头针,加上字母 T,代表过境或运输。You_Marker.png
地图图钉是一个蓝色的标记,上面有“你”字。我们已经为地图图钉指定了正常大小,以帮助用户点击标记来查看“信息气泡”以了解给定点的更多详细信息。这使得小手机屏幕上的 pin 看起来有些大,但保持正常大小是很重要的,以便用户在按下屏幕时不太可能错过触摸 pin。
您会注意到两个MarkerImage
对象都有两个对google.map.Point()
的调用。第一个设置图像的原点,用作覆盖图。在我们前面的例子中,我们没有改变默认设置。对google.map.Point()
的第二次调用为图像设置锚。默认情况下,当一个 google.map.Marker
覆盖在地图上时,任何关联的MarkerImage
都会在定位点的图像左上方呈现。我们实际上希望大头针图像的尖端看起来直观地指向我们感兴趣的位置,所以我们希望MarkerImage
的中间、底部边缘与我们的位置结果对齐。为了实现这一点,您会看到我们将锚点偏移了大头针图像宽度的一半(10 像素),大头针图像的完整高度为 34 像素。
准备我们的地图
做好这些准备后,我们开始使用驱动 transit 应用的主要函数,prepareMap()
函数。
`function prepareMap() {
getLocation();
myTransitMap = new google.maps.Map(document.getElementById("map"), {
center: myLatLong,
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var yourMarker = new google.maps.Marker({
position: myLatLong,
map: myTransitMap,
icon: youMarker,
title:"You!"
});
myLocalSearch = new GlocalSearch();
myLocalSearch.setSearchCompleteCallback(null, processLocalSearchResults);
}`
prepareMap()
功能很容易理解,分为四个主要动作。首先,我们调用我们的getLocation()
函数,它主要是我们在第九章的清单 9-5 中的initialize()
函数中展示的代码的重新标记版本。为了完整起见,这里再次说明:
function getLocation() { if (supportsGeo()) { myLatitude = ""; myLongitude = ""; navigator.geolocation.getCurrentPosition( function(position) { myLatitude = position.coords.latitude; myLongitude = position.coords.longitude; }, function(error) { switch(error.code) { case error.TIMEOUT: alert ('Geolocation returned a timeout error'); break; case error.POSITION_UNAVAILABLE: alert ('Geolocation returned a position unavailable error'); break;
case error.PERMISSION_DENIED: alert ('Geolocation returned permission denied (did you deny access?)'); break; case error.UNKNOWN_ERROR: alert ('Geolocation encountered an unknown error'); break; } } ); } myLatLong = new google.maps.LatLng(myLatitude, myLongitude); }
如前一章所述,它使用浏览器的navigator.geolocation
对象来确定您的纬度和经度。为了节省空间,我们将省略重复几乎相同的代码。
然后我们创建一个名为myTransitMap
的新的google.maps.Map
对象,将地图的中心放在我们检测到的位置上。我们将缩放级别设置为 14,因为这对于步行的人来说是一个很好的比例,尽管您可以将其更改为任何所需的缩放设置。我们也选择标准的道路地图风格的地图,而不是卫星视图。
提示:这里有一些缩放级别设置的便利值,在为用户渲染地图时要记住。
缩放级别 12: 适合公路级别驾驶,显示几十英里或几公里以上的主要路线。
缩放级别 14: 适用于市内驾驶,距离超过几十码或几米时的一般步行方向
缩放级别 15: 最适合详细的行走,显示建筑物轮廓,以及显示单行道和其他障碍物的驾驶方向。
在我们的myTransitMap
对象上,我们在为手机或设备检测到的相同纬度和经度处放置一个标记。我们使用自定义的蓝色“你”标记,如前所述,甚至提供一些悬停文本来强调这个大头针将你放在地图上。
最后,我们构造一个新的本地搜索对象myLocalSearch
,然后注册回调函数processLocalSearchResults()
。当这个搜索对象完成搜索执行时,将调用这个函数。回调的机制将在本章的稍后部分展示。随着prepareMap()
功能的完成,我们实际上可以看到当用户最初在他们的设备上运行我们的应用时是什么样子,如图 Figure 10–1 所示。
图 10–1。【the Move Me 示例应用的初始屏幕
我们对开始的例子做了一些改动,将我们在 Android 模拟器中的坐标设置为一个相当著名的位置:旧金山的联合广场。这并不像听起来那么做作。想象自己是旧金山的游客。你已经看到了这个城市的风景和声音,发现自己在联合广场,想找一辆火车,公共汽车,或渡轮到您的酒店或其他目的地。这就是我们示例应用中其余功能发挥作用的地方。
执行本地交通搜索
我们示例中接下来的两个函数执行我们选择的本地搜索,并处理结果。
function execSearch() { var searchText = document.getElementById("searchtext").value; myLocalSearch.execute(searchText); }
execSearch()
函数只是读取 HTML 表单上searchtext
字段的内容,见 Figure 10–1 的顶部,并用该文本调用myLocalSearch()
对象上的execute
方法。我们将processLocalSearchResults()
方法注册为当myLocalSearch()
报告执行完成时的回调函数。
function processLocalSearchResults() { for (var i = 0; i < myLocalSearch.results.length; i++) { searchResults.push(new LocalResult(myLocalSearch.results[i])); } }
processLocalSearchResults()
函数遍历我们的结果,实例化一个LocalResult
对象,并将它们添加到searchResults
数组中。LocalResult
对象是 Google 在它的许多 API 示例中使用的常见示例对象,我们在这里复制了它。我们使用它主要是为了方便我们访问本地搜索结果的坐标。它做的工作与我们之前的代码类似,因为它在我们的地图上放置了一个标记,并且它还创建了一个文本结果列表。在我们当前的例子中,我们已经创建了 CSS 来隐藏文本列表,但是我们将很快介绍这方面的选项。
最后,就我们的 JavaScript 而言,我们将prepareMap()
函数注册为回调函数,以便在 Google Search API 完成加载后调用。
GSearch.setOnLoadCallback(prepareMap);
剩下的就是实际的 HTML 代码,它控制我们的地图、搜索字段和查找按钮的大小,以及相关的<div>
名称和嵌套。我们特意为整个显示选择了明确的像素大小,以及其中包含的地图对象,以适应手机上的垂直显示和平板电脑上的水平显示,同时还显示我们将在本章稍后介绍的文本结果。
`
Move Me! Transit Search Example
我们的 HTML 显示了您在 Figure 10–1 中看到的简单表单。
运行我们的代码
那么,当我们实际搜索交通工具时会发生什么呢?到目前为止,在我们的示例中,我们的候选用户是一名游客,他发现自己在联合广场,正在寻找最近的交通工具。如果你发现自己在旧金山,你可能会想到使用湾区交通系统,或 BART。所以让我们以此为标准来显示当我们调用execSearch()
时实际发生了什么。图 10–2 显示了通过本地搜索调用返回的结果,使用LocalResult
对象绘制在地图上。
图 10–2。 我们的 Move Me 应用显示附近的交通选项
瞧,我们找到了四个最近的地铁站,从市中心到恩巴卡德罗。您还可以看到我们在本章开始时描述的自定义 transit MarkerImage
和Marker
对象的最终渲染。
也许,作为一名游客,你真的是在寻找旧金山湾的美景,还有什么比找到最近的渡轮码头来寻找世界上最伟大的水道之一更好的方式呢?图 10–3 显示了我们的应用查找最近的渡轮码头。
图 10–3。 找轮渡运输带搬我
我们又一次找到了我们想要的运输方式。在这种情况下,您会注意到几个标记重叠在一个位置上。这是谷歌的搜索,返回了几个不同的渡轮公司和从该位置运营的渡轮选项,而不是我们使用的 HTML 或 JavaScript 中的错误。
改进“移动我”示例应用
到目前为止,您可能已经注意到,我们表单的简单性实际上变得有点麻烦了。当然,你的用户可以在他们的本地搜索中输入任何他们喜欢的东西,但是也许你想让他们不用在小软键盘上打字。我们示例代码的第二个版本,在文件ch10–example02.html
中,修改了表单代码,为流行的交通类型提供了简单的按钮,并调用了我们修改后的execSearch()
函数。我们的新表单编码如下。
`
我们修改后的execSearch()
函数不再需要读取文本字段,主要是因为我们已经移除了它!相反,它将我们调用函数时传递的文本作为参数,比如“火车站”或“渡轮码头”,并调用相关搜索的执行。
function execSearch(searchText) { myLocalSearch.execute(searchText); }
我们的应用现在看起来更容易立即使用,有地铁、火车、公共汽车等等的相关按钮,如图 Figure 10–4 所示。
图 10–4。 使用专用中转按钮移动我应用
从这里开始,按下一个按钮会产生和以前一样的结果,显然是为了匹配每个按钮的编码标准而定制的。
处理其他运输可能性
有无数种方法可以让这个例子更进一步。一个明显的例子是增加交通选项按钮的数量。你可以包括出租车,机场,电车,等等。还有其他细微差别需要考虑。旧金山的地铁可以指 BART 系统,但市政系统的大部分也被认为是地铁。在其他城市,地铁有其他的名字,比如伦敦的“Underground”或者巴黎的“Metro”。代码可以扩展为重用您的坐标来确定用户当前所在的城市,并且搜索文本可以相应地本地化。
我们方法的局限性
到目前为止,我们的例子有助于找到我们自己或当地的交通站或车站。这无疑有助于从任何给定的位置制定出可能的交通选择,但就像一次未完成的旅程,它让我们离我们真正想去的地方很近。我们可能知道拐角处就有一个公交车站,或者街道尽头就有一个火车站,但是我们知道从这些地方有哪些服务,以及它们要去哪里吗?我们知道下一次服务预定什么时候到达吗?我们到底知不知道那个车站有没有定期航班?
如果有某种方法可以为我们发现的中转站点找到匹配的中转计划数据就好了!
引入公交数据资源
到目前为止,我们已经探索了如何找到我们自己和我们周围可能的交通选择,并在地图上绘制结果。但是任何一个旅行者都会告诉你,知道何时交通工具运行和知道它们在哪里运行一样重要。如果有办法使用公共汽车时刻表、火车时刻表和其他交通时刻表来扩展我们的交通应用的可能性,那不是很好吗?嗯,有一个办法!
利用交通时刻表和时刻表
尝试使用运输机构的数据的历史是漫长而曲折的。为了避免您之前的开发人员不得不忍受的痛苦,我们将缩短我们的故事:我们将专注于“那时”和“现在”1995 年以前,与各种政府或官僚机构打交道以获取他们的交通数据是一种代价高昂的受虐狂行为。未记录的专有格式、蓄意阻挠的政府官员和扭曲的所有权概念给任何使用“公共”交通数据的尝试蒙上了阴影。
1995 年,美国俄勒冈州主要城市波特兰的 TriMet 运输机构的一名员工对自己可以使用的专有地图和运输数据系统和来源感到沮丧。她写信给当时许多领先的地图和地理定位服务,询问他们有什么计划将公共交通数据纳入他们的服务。只有一家公司回应:谷歌。
从当时开发人员的角度来看,处理保管数据的机构的不稳定——甚至有时反复无常——的态度是非常令人沮丧和复杂的。但 TriMet 的试探性问题揭示了一个想法,即希望访问数据的沮丧的开发人员与希望看到数据更好地用于服务社区的沮丧的数据保管人和公共官员相匹配。
这引发了通用运输馈送规范(GTFS)的兴起,该规范旨在为运输数据格式提供一致的协议。这个时代的任何历史学家都会注意到,GTFS 格式几乎完全是波特兰 TriMet 公司用于传输数据的格式。通常情况下,特定技术领域的先行者是事实上的标准制定者。很快,更多的政府和运输机构有远见地以这种格式发布他们持有的数据,这已经成为共享运输时间表、路线和相关数据的标准。
*几乎每个人都曾坐过公共汽车、火车、飞机或渡船,因此我们可以跳过 GTFS feed 的基本概念。相反,让我们从一个 Android 开发者的角度来看一下规范。
检查 GTFS 组件
GTFS 提要就是一堆逗号分隔格式的文本数据,分成几个文本文件,然后打包成一个 zip 文件以便于处理。作为 web 应用或原生 Android 应用的开发人员,您可以获取 GTFS 提要的 zip 文件,然后使用它将交通位置、时间表信息等合并到您的代码中。典型的 GTFS zip 文件由(并且需要)以下文本文件组成:
- Agency.txt 提供 GTFS 数据包中数据的机构和组织。
- 基于每周花名册的各种服务的日期。这包括服务的开始和结束日期的详细信息,以及服务可用和不可用的日期。
- 给定服务的停靠站集合,以及它们之间的路径。这与大多数人认为的单一公交路线、火车旅程等相匹配。
- 服务停止让乘客上下车的明显位置,包括终点站或终点。
- Stop_times.txt 给定车辆到达和离开停靠点的时间。这也允许计算“停留时间”,即服务停留在停止状态的时间。
- Trips.txt 给定路线的行程,其中给定车辆连续停靠。
除了前面的强制文件列表之外,给定的 GTFS 馈送还可以在相应的文本文件中包括以下可选附加数据中的一些或全部
- Calendar_dates.txt 在 calendar.txt 文件中列出标准排班的服务细节的例外情况。
- Fare_attributes.txt 由运营服务的机构收取的使用特定服务的实际费用的详细信息。
- Fare_rules.txt 用于应用在 fare_attributes.txt 中指定的票价的规则。这可以包括诸如高峰和非高峰规则、对学生、老年人的优惠等等。
- Feed_info.txt 关于此 GTFS 源的附加信息,例如版本号和适用日期。
- frequency . txt不按固定频率运行的服务的定时信息。
- Shapes.txt 帮助渲染路线图形的细节,比如如何表示出、回程路径的差异,回程点等。
- 关于从一个服务转移到另一个服务的任何规则的细节。
单个文件字段、格式要求和其他规则的细节可以在许多与 GTFS 相关的网站上找到,比如[
developers.google.com/transit/](https://developers.google.com/transit/)
。
世界各地的许多运输机构在[www.gtfs-data-exchange.com/](http://www.gtfs-data-exchange.com/)
利用 GTFS 数据的中央交换所。该网站不仅充当 GTFS 馈送的中央存储库,而且包括关于规范的基本数据,以及关于如何贡献新 GTFS 数据集的细节。
如果这一切听起来不可思议——近乎乌托邦——有一个小问题总是影响着运输时间表和路线的理想,以及通勤者或游客的现实。当服务没有按时运行,或者根本没有运行时,会发生什么?输入 GTFS 实时标准。
处理时间表现实
GTFS-实时时间是一个相对较新的除了 GTFS 的核心,由一组感兴趣的各方在 2011 年 8 月发布。它旨在以提供实时更新运输服务时间表的形式,解决“理论何时符合实践”的永恒问题。它向订阅数据的任何人实时提供以下类型的更新。
- 行程更新:计划服务的任何路线、时间变更或取消
- 服务警报:服务级别的变化,例如移动的站、关闭的站,或者甚至是整个网络的问题,例如电源故障或切换问题。
- 车辆位置:车辆在交通系统中的位置,以及车辆自身或相关交通基础设施提供的当地拥堵信息。
谷歌是 GTFS 实时系统发展的关键参与者,这在数据格式的选择上是显而易见的。GTFS 实时公司没有遵循基本 GTFS 标准的逗号分隔格式,而是采用了谷歌的协议缓冲格式。这只是另一种数据格式,它是基于高效的平台无关的序列化和反序列化的理念而创建的——基本上是一种与 XML 类似的简洁格式。实际上,这只是意味着需要参考协议缓冲区定义来确定文本/数据中的字段长度和属性。
探索 GTFS 的例子
我们的章节已经很长了,介绍和探索一个小的 GTFS 例子将会涉及数百行 JavaScript、HTML 和 CSS。这并不是因为这些示例很难或很复杂,而是因为它们需要处理我们在本章中已经处理过的所有地理定位和地图渲染样式代码,以及大量文本处理代码和 I/O 代码来处理 GTFS 数据。
就其本身而言,将有足够的材料来单独出版一本书,因此,与其用如此大的篇幅来淹没我们的书,我们不如在网上调出一些优秀的公开可用的例子,你可以下载并探索你内心的内容,以发现更多关于 GTFS 及其可能性的信息。
在线提供的一些著名且非常有用的 GTFS 开源示例包括:
- 谷歌公交开发者页面作为 GTFS 社区的活跃成员,谷歌在网上有大量的例子。将你的浏览器指向
[
developers.google.com/transit/gtfs/examples/display-to-users](https://developers.google.com/transit/gtfs/examples/display-to-users)
以获得大量的例子和背景信息。 - One Busway 项目最初由开发人员在普吉特湾创建,
[
developer.onebusaway.org](http://developer.onebusaway.org)
是 One bus way 项目的所在地,该项目已扩展为面向 GTFS 开发人员的全球计划。 - 社区项目如 UlmApi.de 的 Live Map,这是一次黑客马拉松的成果,它在几个小时内就构建了一个功能齐全的 GTFS 交通应用。完整的项目详情请见
[
github.com/UlmApi/livemap](https://github.com/UlmApi/livemap)
在图 10–5 中,您可以看到 Ulm 清晨的完整交通系统,由 UlmApi.de Web 应用为 Android 渲染。
图 10–5。 安卓浏览器中的 UlmApi.de hackathon GTFS 应用
本着基于公交和运输应用的真正精神,我们邀请您去探索这些和其他 GTFS 网站,并看看乘坐带您去哪里!
总结
在这一章中,我们探讨了 Android 基于位置的 Web 应用的进一步开发。我们的例子已经介绍了如何使用 Google 流行的本地搜索 web 服务来演示如何为脚步匆匆的游客找到火车、地铁、渡轮和其他交通工具。我们只是触及了像 GTFS 这样的交通数据馈送系统的表面,但是你现在已经知道了足够多的信息来探索你可以利用的许多在线选项。我们希望我们的示例应用已经展示了将基于手机的定位功能与基于云的服务相结合来创建有用的 mashups 和非常易于开发的功能是多么容易。*
十一、使用音频和视频挑战极限
音频和视频是移动网络中最难涉及的两个话题。虽然每一种技术都可以增强网站或应用的视觉和听觉吸引力,但通常这两种技术都会被移动内容创作者滥用。例如,我注意到在移动世界越来越普遍的一种滥用形式是针对移动浏览器的基于音频的广告。虽然音频和视频技术都可能被不正确地使用,或者只是以一种普通的令人讨厌的方式使用,但如果使用正确,它们确实可以达到很好的目的。
比方说,我们正在开发一个手机游戏应用,我们想添加一些轻松的背景音乐来增强用户体验。当使用audio
标签时,我们可以使用 Android 操作系统浏览器的内置 HTML5 功能相当容易地做到这一点,只需要几行代码。除此之外,我们甚至可以使用一点 JavaScript 来操作音频元素。我们可以通过按键来播放、停止或暂停音频。
通过 Android 的浏览器程序,我们还能够利用 HTML5 video
标签来增强我们的 Web 应用,包括 h.264、MPEG-4 和 WebM 格式的视频。这两个标签——audio
和video
标签——如果使用得当,可以创造出一些顶级的用户体验来增强它们所在的应用,使它们散发出卓越的魅力。
在这一章中,我们将看一看这些元素,它们的 API,以及在我们的应用中使用这些新的HTML5
标签的优缺点。
面向移动 Web 应用的音频
直到几年前,作为一名开发人员,如果您想在 web 应用中包含音频,您的选择是微乎其微的。几乎所有的解决方案都涉及到使用 Adobe Flash,并使用 Flash 作为中间人将音频传输到用户的音频输入设备。虽然可以通过embed
和object
标签给你的用户体验添加音频,但更多的时候你会得到一个非常有限且不可接受的用户体验,这让你的最终用户和开发者希望得到更多。
HTML5 采用率的上升使得在 HTML5 audio
标签的 Android 实现中处理音频(尽管在一些罕见的情况下仍然很痛苦)变得普遍顺畅。有大量的例子表明,移动 Web 应用和游戏利用音频元素将背景音乐和声音效果带到他们的项目中,这有助于为用户创造更多引人入胜的场景。
不幸的是,像 Web 开发世界中的大多数事情一样,并不是所有的音频编解码器都是一样的。在关注移动世界时尤其如此。自 2010 年年中 Android 2.0 首次问世以来,谷歌的 Android 操作系统一直在使用 HTML5 音频标签,但它只使用了两种更受欢迎的音频编解码器选择——MP3 和 AAC 音频格式。
利用 HTML5 音频标签
使用 HTML5 audio
标签非常简单。看看清单 11–1 中的例子,我们在用户页面中添加了一个重复的音频循环。
清单 11–1。html 5 音频元素的基本使用
<audio autoplay="autoplay" controls="controls"> <source src="audio/stairwell.ogg" type="audio/ogg" /> <source src="audio/stairwell.mp3" type="audio/mpeg" /> "Sorry, your browser does not support the audio element." </audio>
音频标签的基本用法与 HTML 空间中的其他标签非常相似。当声明 HTML 音频元素时,我们可以在文档中调用几个属性。让我们看看下面的一些属性:
autoplay
—如果被调用,该属性将在音频元素准备就绪并可用时播放它controls
—确定是否向用户显示该音频元素的控件。loop
—如果设置,该属性告诉音频无限循环。就像那首永远不会结束的歌!preload
—这决定在文档加载时是否将音频元素预加载到页面中。src
—音频文件的 URL。
查看清单 11–1,我们还可以注意到audio
标签也包含另外两个源标签。这些source
标签都有src
属性,指向服务器上文件的 URL。
使用多个标签的原因实际上是编写一个可以在多种浏览器上工作的 HTML 文档的巧妙方法。如果你的浏览器支持 HTML5 audio
标签,它会检查每一个源编解码器,直到找到一个适合你的浏览器。如果它成功地找到了一个合适的文件(即,您提供了一个可以在用户系统上播放的文件版本),它将播放该文件。如果没有,它将什么都不做。如果出于某种原因,你正在从一个没有显示音频标签的旧 Android 设备上查看这个页面,那么浏览器将会忽略这个元素,并显示预设的错误消息“对不起,??”。您的浏览器不支持如图 Figure 11–1 所示的音频图像。
图 11–1。Android Gingerbread 2.3 设备上的 HTML5 音频元素,带有“控件”属性集
将音频整合到“那是谁的推文”?
在我们在本书中使用的第一个应用中,我们要求人们猜测哪一个 Twitter 帐户产生了一条给定的 tweet。现在想象一下,我们想要放一点音频来抚慰那些玩我们游戏的人的野蛮的心灵。我们可以很容易地用音频标签做到这一点,使用下面的清单 11–2 中的代码。
清单 11–2。 HTML 代码那是谁的推文?带音频
`
Player 1
在本例中,我们已经开始播放我们的主题曲,并且我们已经设置了 loop 属性,这样它将简单地重复循环,直到时间结束。我们没有指定控件,如果音频无法加载,我们也不会显示任何消息。现在我们只需要找到一首好的主题曲,不会让我们的用户太烦!
使用音频编解码器
如果您正在为 Android 设备构建您的应用,您将希望坚持使用整个平台上可用的以下三种编解码器之一:MPEG-2 音频层 III (MP3)、高级音频编码(AAC)和开源粉丝最喜欢的 Ogg。虽然每一个都有自己的好处,但大多数开发者通常只坚持 MP3 和 Ogg,因为有很多免费的开源工具可以将你的音频转换成这些格式。
MP3 文件
MP3 音频格式被称为有损压缩音频格式。有损压缩格式在多媒体中非常流行,如果你以前曾经使用过 Adobe Fireworks、Gimp 或 Adobe Photoshop 等图形设计程序,那么当你试图从图像中挤出几个额外的字节时,你可能对有损压缩比较熟悉(没有双关语)。在不损害图像完整性的情况下,删除数字图像的一部分以减小图像大小的方法同样适用于音频。
在 MP3 的例子中,压缩算法在数字音频中运行,并去除普通人耳通常不能拾取的部分音轨。MP3 文件的比特率越低,表示从录音中删除的数据越多。
虽然有些人声称由于丢失了音频信息,从 MP3 中听到了微弱的声音,但大多数人似乎无法说出与无损数字拷贝相比的区别。这使得 MP3 成为一个非常棒的音频编解码器,可以用于 HTML5 音频,因为你可以获得非常小的文件,可以下载并快速传输到用户的浏览器。也就是说,你应该小心为 MP3 文件选择合适的比特率——比如 128 kbps。任何较低的声音在高质量的音频设备上可能会有微弱的声音(例如,HTC Rezound)。如果不使文件太大的话,移动到 192、256 或 320 kbps 可能是有用的;然而,考虑到相对较小的好处,您可能会发现这是不必要的。超过 320 kbps 很可能是一种浪费,除非你的应用是为发烧友打造的!
在很大程度上,MP3 是互联网上事实上的音频格式,这就是为什么几乎每个主流浏览器都支持 MP3 音频和 HTML5,无需额外的插件。
加气混凝土
高级音频编码,也称为 AAC,是另一种即将出现的音频格式。如果你曾经从苹果 iTunes 上购买过音乐,那么你很可能购买了这种音频格式的音乐。AAC 是由几家公司开发的,包括索尼、诺基亚、杜比和 AT&T。这种格式确实比它的前身 MP3 获得了更好的音频质量,并于 1997 年春天被宣布为国际音频标准。
这种音频格式受开发人员欢迎的原因之一,除了与其竞争对手相比声音更好这一明显事实之外,是因为分发或传输 AAC 编码内容不需要许可证或专利费用。注意,这种许可只适用于实际编码——你不能拿着不属于你的有版权的材料,用 AAC 编码,在你把它分发给全世界后嘲笑律师。好吧,我想你可以笑,但这对你的案子没有帮助!
Ogg
在我们探索征服 Android 设备上的 HTML5 音频的过程中,另一个崭露头角的是我个人最喜欢的编解码器之一:Ogg。Ogg 的创建者在过去声明过,他们创建 Ogg 是为了成为一种不受限制和软件专利束缚的格式。这促进了 Ogg 在大多数浏览器中作为默认 HTML5 音频编解码器的采用。如果你将你的音频编码成 Ogg 格式,它很有可能不仅能在 Android 设备上运行,也能在当今市场上的许多其他设备和浏览器上运行。
虽然 Ogg 是一种像 MP3 和 AAC 一样的有损压缩格式,但 Ogg 背后的框架可以在市场上的几种无损音频格式中找到,如 OggPCM 和 FLAC。有了 Ogg,你可以随心所欲:微小的文件或巨大的无损文件。天空是无限的。
使用 Audacity 音频编辑器
我最喜欢的开源音频软件之一是 Audacity ( [
audacity.sourceforge.net/](http://audacity.sourceforge.net/)
)。这个免费的跨平台应用可以让您轻松地从许多不同的格式导入各种各样的音频。使用 Audacity,您可以轻松地将音频拼接在一起,剪切音频文件,录制现场音频,改变音频文件的音高和速度,给音频轨道添加噪音,降低过高的音频轨道的音量,以及许多其他非常酷和有用的功能(参见图 11–2)。
图 11–2。 Audacity 1.3 Beta 编辑一个知识共享音频文件
每当我处理来自客户的音频时,当我看到一个大的 512 MB–1024 MB 的 WAV 音频文件时,Audacity 总是我的第一选择,它可以删除音频文件中我不需要的部分,或者将音频文件转换成更小、更易于管理的格式。
因为这本书只是打算涵盖 Android 移动设备设计的初学者方面,所以我不会对 Audacity 的所有特性进行过多的深入探讨。相反,我将介绍一些基础知识,比如将一种音频格式导出到另一种格式。
如果你想把你的文件转换成 MP3 格式,那么你需要下载蹩脚的 MP3 库。您可以通过 Audacity 中的首选项轻松地做到这一点。
-
转到编辑菜单并选择属性。
-
Choose the Libraries option in the left hand section of the Properties window as shown in Figure 11–3.
图 11–3。 查看 Audacity 1.3 测试版首选项对话框。
-
一旦到了那里,您可以选择将 Audacity 指向您的蹩脚库(如果您已经在计算机上安装了它的话)。如果您还没有安装 LAME 库,请继续执行步骤 4。
-
Click the Download button, which will take you a site where can download the latest and greatest copy of the LAME MP3 Library, as shown in Figure 11–4.
图 11–4。 瘸腿 MP3 库下载页面。
-
一旦你下载了这个库并解压到你的硬盘上,你就可以录制音频并导出为 MP3 格式了。
如果你看一下本节前面的图 11–2,你会发现我们已经在使用一个名为楼梯间. ogg 的创作共用音频文件。我们要做的是将这个 Ogg 文件立即转换成 MP3 文件。为此:
-
Click File in the menu bar, and then select the Export item as seen in Figure 11–5.
图 11–5。 在 Audacity 1.3 Beta 中从 Audacity 文件菜单中选择导出。
-
在下拉框中,确保将文件格式更改为 MP3,而不是音频文件的当前格式。
-
选择您想要保存文件的位置,单击保存按钮,您就完成了!
你看,我不是答应过你不会痛的吗?现在,您已经知道如何快速将音频从一种格式转换为另一种格式,是时候通过非常方便的 HTML5 音频数据 API 来处理音频了。
音频数据 API
使用音频数据 API,开发人员可以用几年前不可能的方式与音频数据进行交互。使用 JavaScript 和一些 HTML,您可以轻松地创建自己的控件来与您的音频数据交互,正如我们在清单 11–3 中看到的。
看一下下面的例子,你可以看到我们在清单 11–1 上进行扩展,并添加了一些新的定制控件来播放和停止音频。
清单 11–3。 更高级的 HTML5 音频数据 API
`
当您在浏览器中运行这段代码时,您可能注意到的第一件事就是页面上根本没有任何音频控件,除了我们添加到页面上的两个按钮,它们的 id 分别是play
和pause
。这些按钮虽然不像在图 11–6 中看到的那么漂亮,但将是我们操纵页面上音频的唯一手段。
图 11–6。 在 Android 2.3.4 姜饼设备上加载我们的演示 HTML5 音频文档
现在,如果你在 JavaScript 中查看清单 11–3 的底部,我们可以检查真正的奇迹发生在哪里。在这里,我们为audio
、playButton
和pauseButton
声明变量。接下来,我们向每个按钮添加一个事件监听器,这样当用户单击这些按钮时,我们就可以发出一些代码来播放或停止音频流。如果我们愿意,我们可以包含额外的音量按钮,这将采取与上述代码类似的形式,但调用audio.volume=X
,其中 X 将是一个 0 到 1 之间的数字,表示音量级别百分比(因此,如果您想将音量增加 10%,您的代码将读取audio.volume = audio.volume + .1
)。
你也不仅仅局限于随意暂停你的音频文件。使用 HTML5 音频数据 API,您可以轻松控制在页面中播放或欣赏音频的许多方面,例如操纵音频文件的音量,分析一个轨道中每分钟的节拍数,将canvas
的强大功能与音频数据 API 结合起来,创建您自己的可听音调,等等。
向移动应用添加视频
既然我们已经讲述了美妙的音频世界,现在是时候通过 HTML5 视频了解互联网多媒体的视觉吸引力了。视频标签是未来几年出现的 HTML5 新元素之一。它的创建是为了让开发人员的工作更容易,并让我们摆脱对 Adobe Flash 的依赖。对我们来说,不幸的是,没有人能真正就视频标准达成一致,而且有这么多的障碍、编解码器、许可协议和专利挡在我们的路上,我们不太可能很快就推出一种基于浏览器的通用视频格式。
好消息是,由于我们只专注于基于 Android 的设备,我们不必处理在桌面计算世界中使用 HTML5 视频带来的许多挫折。在这里,很简单。有一些视频编解码器可以在 Android 浏览器上工作,只要你坚持使用其中的一种或全部,你就能为你的移动用户提供更丰富的多媒体体验。
使用 HTML5 视频标签
调用 HTML5 video
标签就像调用我们在本章前面讨论的audio
标签一样简单。看一下清单 11–3,看看我们如何使用几行代码轻松地向用户显示视频。
清单 11–3。html 5 视频元素的基本使用
<video width="320" height="240" controls="controls" poster="video/big_buck_bunny.jpg"> <source src="video/big_buck_bunny.mov" type="video/mp4" /> <source src="video/big_buck_bunny.ogg" type="video/ogg" /> <source src="video/big_buck_bunny.webm" type="video/webm" /> Sorry! Unfortunately your browser does not support the video tag </video>
在这个例子中,我们只是调用一个视频文件,并将视频容器的width
设置为 320 像素,将height
设置为 240 像素。我们还告诉视频显示它的控制,这样用户可以播放,暂停,停止,并操纵视频的音量。你可以在图 11–7 中的移动设备上看到这个样子。
图 11–7。 在 Android 2.3.4 姜饼设备上观看 HTML5 视频
您还会注意到,video
标签与audio
标签的不同之处在于具有不同的属性。最突出的一个是poster
属性,它让我们添加一个截图(或任何其他图像)来表示将要播放的视频。这对于移动设备来说非常好,因为当你在 Android 设备上播放视频时,Android 设备会将视频流传递给内置的视频播放器,后者会为你播放视频,而不是像人们在使用台式电脑多年后所期望的那样在浏览器中播放视频。
虽然这个功能有趣且方便,但它完全消除了人们使用视频数据 API 的许多理由,因为它在我们的页面之外播放视频——在一个专用的视频应用中。这破坏了用户体验,使我们无法控制用户在观看我们的视频时会看到什么。
一会儿,我们将给出一个视频例子,类似于我们在上面使用的音频 API。然而,我们应该注意到,虽然你可以用视频数据 API 做一些很酷的事情,但是很多事情都超出了大多数人的需求。以下是基本情况;然而,完整的 API 可在[www.w3.org/2010/05/video/mediaevents.html](http://www.w3.org/2010/05/video/mediaevents.html).
获得参考。现在让我们来看看在开发下一个使用视频的移动 web 应用时可以使用的最有用的属性!
height—
控制播放视频的高度width—
控制正在播放的视频宽度preload
—如果设置,将在页面加载时预加载视频autoplay
—如果设置,将在视频准备就绪时自动播放loop
—如果设置,将无限循环播放视频controls
—如果设置,将向用户显示控件以控制视频src
—指向视频文件来源的 URL
编解码器
就像我们对音频所做的那样,视频必须被编码成原生 Android web 浏览器支持的几种格式之一(后面会提到)。对视频进行编码可能比对音频进行编码要稍微复杂一些,因为你要对两种东西进行编码——即音频和视频!我们将介绍您可以使用的格式,并讨论如何轻松地在它们之间移动音频和视频。
h.264/MPEG-4
多年来,MPEG-4 已经成为互联网和我们日常消费生活中视频编码的首要标准之一,尤其是在高清视频方面。最近,随着 YouTube 和 Vimeo 等社交视频共享网站的兴起,MPEG-4 进入了公众的视野,这些网站允许用户上传这种格式的视频。MPEG-4 的当前版本被称为 h.264/MPEG-4 AVC,是高质量视频压缩的标准,如蓝光光盘上的标准。
Ogg Theora
Theora 是一种免费的开源有损视频压缩格式,由负责维护 Ogg 音频编解码器的同一个基金会 Xiph 创建。Org 基金会。虽然 Theora 本身是基于一个名为 VP3 的专有视频编解码器,但它后来被发布到公共领域,从 2002 年 3 月起任何人都可以免费使用。
Ogg Theora 在设计上类似于 MPEG-4,并且在过去几年中作为一项技术得到了快速发展。虽然它在质量上仍然比不上 MPEG-4 视频,但它非常接近,以至于许多以前喜欢广泛接受 MPEG-4 的开发人员现在选择走 HTML5 视频路线,开始加入 Theora 的行列。
WebM
视频世界的一个新竞争者是谷歌自己的 WebM 视频编解码器。WebM 是在 2010 年的谷歌 I/O 大会上宣布的,是一种免版税的视频编解码器,谷歌将其投入使用,试图规避 HTML5 音频和视频中出现的许多问题。虽然每个人都很高兴在他们的 web 应用和网站中使用这些新技术,但是如果没有所有的浏览器制造商都同意一个标准,就很难做到这一点。
WebM 是完全免费的,这意味着任何人都可以获得这项技术,并将其包含在他们的网络浏览器中,而不用担心谷歌将来会敲他们的门要求支付费用。但是,请记住,这里指的是技术,而不是实际的内容。分发有版权的电影会让其他人来敲你的门要求支付!
即使有了这个免版税的安全网,与 Ogg Theora 等其他视频编解码器相比,WebM 的采用率仍然相当低。
使用手刹转码视频
现在,您已经对一些视频编解码器有了更好的了解,我敢打赌,您会问自己如何创建视频内容,以便在应用中使用它们。最简单、最省事的方法之一是一个叫做手刹([
handbrake.fr/](http://handbrake.fr/)
)的小应用。手刹是另一个令人难以置信的开源应用,旨在使视频爱好者和普通视频转码消费者的日常生活更加轻松。
看看图 11–8。这是我们打开手刹应用时看到的默认视图。正如你所看到的,这里的一切都非常用户友好,易于使用。在应用的顶部,如果您单击电影隔板图标,您可以设置您想要转码的视频源,或者从一种格式转换为另一种格式。在该图标下面,有一个区域,您可以在其中设置新创建的视频的目的地,以及更改您想要存放视频的容器。手刹提供的两个容器是MKV
和M4V
视频容器。容器是一种简单的文件格式,用于保存视频的两个独立部分(图片和音频)。
图 11–8。 在 Windows 7 操作系统上打开手刹后的视图
该应用的下半部分是一个选项卡式视图,您可以使用它来定制您的视频,甚至更喜欢。这里最重要的选项卡是视频选项卡。在这里,您可以设置您想要将视频转换为哪种编解码器。根据您选择的视频容器,手刹为您提供了三种视频转换选择——MPEG-4 或 Ogg Theora。
应用的右边将会为你带来很多奇迹。在这里,您可以从各种预配置的视频设置中进行选择,因此您所要做的就是输入一个视频源,决定您要将视频保存到哪里,选择一个您喜欢的设置,然后开始编码。
如果你看一下图 11–9,你也会注意到视频容器选项旁边的两个小复选框。这些复选框Web Optimized
和iPod 5G Support
,是您在对视频进行编码时可能想要尝试的两个选项。在某些情况下,我发现这些设置帮助我创建了质量更好的视频文件,以便在浏览器中使用。然而,在其他时候,它们只会让我心痛。这一切都归结到你作为你的源材料使用的视频文件。视频可能很棘手,尤其是将它从一种格式转换成另一种格式时。最后,人们通常必须“玩”一点设置,以获得最好的结果。如果您的视频文件很大(因此需要更多时间进行转码),您可以考虑将它分割成一小部分进行试验。一旦你找到了设置和选项的正确组合,你就可以转换视频的长版本,为自己节省一些时间。
图 11–9。 设置 Windows 7 手刹内的视频源、目的地和视频容器设置
图 11–10。 选择 iPhone & iPod Touch 为我们的视频预设配置设置
最后,手刹的选择可能有点多。如果你在寻找更简单的东西,你可能想看看更方便消费者的(即,为非开发人员或高级用户制作的)解决方案,如 DoubleTwist ( [www.doubletwist.com](http://www.doubletwist.com)
)和 Videora ( [www.videora.com](http://www.videora.com)
)。两者都可以将视频转换成在移动设备上可以观看的格式,只需很少的努力或需要定制。
现在我们已经了解了一些关于创建、显示和使用您自己的音频和视频的基本信息,我们将介绍一些您可以通过与程序员可用的不同 API 接口来扩展您的应用的领域。
自己探索:音乐服务应用编程接口
许多 web 服务通过 API(应用编程接口)向程序员提供对其平台的访问。在这一节中,我们将讨论两个流行的应用,以及它们提供的 API。您可能会发现,通过将您的应用绑定到这些服务中的之一,您不仅可以使用一些很酷的工具——您还可以增加应用对该服务用户的吸引力。
“滚动”曲目至 Last.fm
Last.fm 是一项服务,它不仅可以将音乐传输到你的电脑或手机上,还可以智能地了解你的偏好。一种方法是允许多个设备(即安装了 Last.fm 客户端的 iOS 设备和 android 手机)将收听的曲目信息发送到 Last.fm,Last.fm 将这一过程称为“scrobbling”。例如,我用谷歌音乐通过浏览器收听我的音乐收藏。通过使用 Chrome 的一个扩展将我的曲目滚动到 Last.fm,我可以在以后查看我的收听行为,Last.fm 知道我喜欢听什么类型的音乐(图 11–11)。
图 11–11。 我的 Last.fm 最近听的曲目
一旦录制了一首曲目,它就会告知 Last.fm 我的收听习惯。这有助于告诉 Last.fm 向我推荐什么,并允许我找到我可能喜欢的新音乐。
想象一下,我们想在我们的新应用 MusicDiscovery 中向用户展示音乐片段。假设 MusicDiscovery 为用户播放 45 秒的歌曲片段。用户评价他们有多喜欢这首歌,如果他们评价足够高,我们就把它滚动到 Last.fm,这样它就可以记录他们的偏好。我们将如何着手做这件事?
首先,我们需要了解 Last.fm API。大多数提供 API 访问的 web 服务会在他们网站的“面向开发者”或“API 访问”部分清楚地展示如何使用它。Last.fm 在其主页底部包含一个“API”链接,通过该链接可以进入 API 主页(图 11–12)。
图 11–12。last . FM 网络服务页面
翻看 Last.fm API 信息,好像是两步走的过程。首先,你必须申请成为一名开发者(这包括告诉 Last.fm 你打算将他们的数据用于什么目的,即商业或非商业用途),并填写一些关于你的应用的基本信息。一旦解决了这个问题,您就可以探索他们的 API 的使用了。
API Intro 页面列出了一些基本信息,包括 API 根 URL。这是我们将向其发送信息的网址。大多数请求将利用 AJAX,这是一种我们将在下一章详细讨论的技术。浏览文档,我们可以找到如何将数据发送到 Last.fm 的示例。
正如我们在 Figure 11–13 中看到的,我们对 Last.fm API 的请求采用特殊格式的 URL 或 HTTP POST 请求的形式。然后,Last.fm API 使用 XML 返回响应。
图 11–13。last . FM API 请求文档
现在我们已经大致了解了信息将如何在我们的应用和 Last.fm 之间来回传递,我们可以开始探索 scrobbling 特性了。Last.fm 的 scrobble 文档([www.last.fm/api/scrobbling](http://www.last.fm/api/scrobbling)
,图 11–14)详细地展示了整个过程,包括使用哪些 API 调用(或函数),以及发送 scrobble 请求时使用什么标准。
图 11–14。 捡破烂过程
现在我们已经得到了所有的片段,我们需要做的就是编写适当的 JavaScript 代码来获取用户已经评级的当前音乐片段,在 Last.fm 的目录中查找它,并发送 scrobble 请求。在这里你可以发挥你的编程能力并解决它。一路上,一定要看看 Last.fm 的其他广泛的查找和录制功能。不仅可以为 Last.fm 用户提升用户体验,还可以利用 Last.fm 数据为自己不使用 Last.fm 的用户提升体验!
挖掘亚马逊产品广告 API 的力量
大约 15 年前,Amazon.com 以书店起家。现在,他们已经发展成为一个巨大的零售巨头,提供从书籍到云计算服务器等等的一切。最近,他们已经扩大了他们的音乐产品,在应用中提供音乐或视频的开发者可能希望通过亚马逊提供购买这些资源的链接。
亚马逊产品广告 API ( [
affiliate-program.amazon.com/gp/advertising/api/detail/main.html](https://affiliate-program.amazon.com/gp/advertising/api/detail/main.html)
,图 11–15)是一个庞大的网站,所以我们只能在这里强调它的主要特性。你可以花上几天时间来探索你可以用亚马逊的数据做的一切,所有这些都是为了通过你的应用提供亚马逊产品来增加收入。
图 11–15。 亚马逊产品广告 API
Amazon 不仅提供关于他们的 API 的文档,还提供开发者论坛,在那里你可以向其他开发者寻求帮助,甚至提供代码示例,这可以让你快速上手并运行。
对代码数据库进行一些快速搜索,就会发现一段非常有趣的代码,对于任何希望构建一个展示特定艺术家的应用的人来说都是如此。假设你在一个乐队,想为你的乐队开发一个应用。你决定开发一个 web 应用,因为它可以在每个人的智能手机上轻松运行,而且你正在亚马逊的 S3 云存储中存储你的音乐会录音、演示和专辑。我们该如何向粉丝们推销呢?它有一个演示,叫做 io objects([
j.mp/iobjects](http://j.mp/iobjects)
,图 11–16)。
图 11–16。io objects 代码示例
虽然这比我们在上一节中讨论的 Last.fm API 要复杂得多,但它确实展示了在应用中使用 API 的强大功能。现在你不需要担心支付你的材料(因为亚马逊灵活支付系统(FPS)可以照顾这一点),你可以很容易地通过你的应用,通过亚马逊提供产品,这是你的粉丝熟悉并经常使用的机会!
正如我们所见,使用来自 Amazon 或 Last.fm 等服务的 API 不仅可以增加应用的交互性,还可以带来新的收入流和创新方式。当你的乐队(或你客户的乐队)的用户还在音乐会上就可以下载歌曲时,他们肯定会脱颖而出,而你将成为实现这一切的开发者英雄!
总结
在移动 HTML5 前端处理音频和视频可能会非常轻松,因为您必须支持的手机和操作系统数量有限。然而,正如本章开始时提到的,如果使用不当,它们都是很容易被滥用的技术,或者很快就会对你不利。在这一章中,我希望我已经向你展示了使用这些技术对你有益的工具。
当谈到音频和视频转换和操作时,我们甚至还没有开始触及到您可以使用哪些工具来完成这项工作。有太多高质量的音频和视频工具了——我们大概可以写一整本书来谈论它们。你不必使用我在本章中建议的工具,我鼓励你做一些研究,找到最适合你的解决方案。
十二、使用 AJAX 提升用户体验
我们已经阅读了这本书的大部分内容,但是我们还没有明确地触及一个长期困扰网络用户,尤其是移动用户的问题:可怕的刷新!
为什么可怕?正如我们所知,在移动世界中,速度就是一切。移动用户最不想做的事情就是重新加载整个网页。即使页面“较重”的方面缓存在用户手机上(比如图形、字体等。)手机的浏览器仍然必须将它们重新加载到浏览器中。这需要大量的时间、处理能力、电池电量和用户的耐心。谢天谢地,有一系列技术可以帮助我们,这就是 AJAX。
什么是 AJAX?
AJAX 代表异步 JavaScript 和 XML。我们来分解一下这个名字。第一个词,Asynchronous,乍听起来可能有点吓人,但是它是 AJAX 与众不同的关键。
异步?
大多数网页被编程为需要在发送/接收/显示系列中进行通信。用户按下 web 应用上的一个按钮,页面向服务器发送所需的信息,服务器发回一些响应,应用显示一个新页面,上面有该响应。如此往复,直到用户完成应用。每次用户与应用交互时,页面都会完全刷新。对某些人来说,这就是讨厌的定义。
异步传输是指我们可以将这个链简单地分解为发送/接收/显示/发送/接收/发送/接收等等,而不是在每个连续的循环中包含显示部分。换句话说,我们可以在不加载新页面的情况下发送和接收数据。异步调用就是我们通过这种替代方法向 web 服务器发出的请求。举个例子,我们只需要在手机上访问谷歌的搜索引擎。我们从一个空白的搜索页面开始,如图 12–1 所示。
图 12–1。 谷歌搜索主页
现在,假设是一大早,我想听一首很棒的歌曲开始我的一天,这首歌的背景是歌曲所讨论的一系列事件的图片。我可能会开始输入歌曲的名称,如图 12–2 中的所示,当我这样做的时候,谷歌正在使用异步调用来试图找出我在寻找什么。当我打字时,它会对照中央数据库检查每个字母,而不需要我刷新页面。然后在搜索栏下面显示这些建议。
图 12–2。 开始我的搜索
在某个时候,在图 12–3 附近,建议与我正在寻找的整个短语相匹配,我可以简单地点击它。
图 12–3。随着我输入的越来越多,网页试图填写可能的查询。有趣的是,Android 操作系统也在试图弄清楚我在写什么——就好像它们都在试图读取我的想法!
基本上,谷歌已经能够预测我在寻找什么,并给我一个直接访问这些搜索列表的捷径。当我点击符合我想要的建议时,它会将我带到我在 Figure 12–4 中看到的搜索结果。
图 12–4。 啊,我的结果——现在我可以听我一直在搜索的 flash 视频了
在谷歌在搜索页面上推出这些异步调用之前,人们在搜索框中输入完整的查询,按下“搜索”,然后等待屏幕重新加载。如果查询中有错误,用户并不知道,直到新页面加载,谷歌指出来。这占用了用户的时间(加载不必要的页面)和谷歌的带宽。最近,谷歌通过异步运行整个搜索,将这种“帮助之手”又向前推进了一步——你可以输入你的查询,然后在同一个浏览器窗口中一个字母一个字母地更新搜索结果。
那么 JavaScript 和 XML 呢?
从技术上讲,AJAX 可以与任何客户端编程语言一起工作(客户端语言,如 JavaScript,在用户的浏览器中处理。服务器端语言,如 PHP,在 web 服务器上处理)。然而,由于 JavaScript 已经成为客户端语言的通用语言,所以它被广泛使用。XML 参与进来是因为它已经成为一种简单且易于理解的移动数据的方式。虽然您可以将其他技术混合到 AJAX 宴会中,但是在我们的示例中,我们将坚持使用 JavaScript 和 XML。
既然我们已经确定了 AJAX 是什么,那么让我们来讨论一些将它整合到一些非常简单的应用中的巧妙方法。我们将从一个每日一词的例子开始,然后进入一个稍微复杂一点的例子,引用一个 web 服务和我们自己的本地数据库。
今日 AJAX
最近有报道称,2011 年的“年度词汇”是“务实”。这是因为一个词典服务网站发现它是那一年被查询次数最多的单词,因此它成为了冠军。也许你会说,“我一直想知道‘实用主义’是什么意思”(或者,“哪个白痴不知道‘实用主义’是什么意思?!?"),并想知道为什么我们没有更多的 Web 应用给我们带来这样的内容。好吧,今天是你的幸运日,因为你将要写一个。
对于今日单词应用,我们将创建一个相当简单的机制来提供该单词。在本章的后面,我们将讨论如何使用 AJAX 从数据库或 web 服务中获取信息,但是现在我们将使用一个好的、老式的平面文本文件。恰如其分地命名为:
清单 12–1。 word.txt
<em>Pragmatic</em>: relating to matters of fact or practical affairs. <i>-<A HREF=http://www.merriam-webster.com/dictionary/pragmatic>Merriam-Webster.com</A></i>
文件word.txt
中有一些 HTML(在这种情况下,我们使用简单的样式标签,但是我们可以使用 CSS 主题的 div 语句),如果我们在 web 浏览器中加载它(图 12–5,我们会看到它显示为简单的文本:
图 12–5。 当日 word.txt 文件
接下来,我将构建一个 HTML 页面,当用户按下按钮时,该页面将加载当日单词。从代码角度来看,它将类似于清单 12–2。我们会一步一步来。
清单 12–2。word.html 的 ,第一部分:加载单词的初始 JavaScript 函数
`
<head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> <title>Word of the Day</title> <script type="text/javascript">` `function loadword(){ if (window.XMLHttpRequest){ // Modern browsers use this type of request request =new XMLHttpRequest(); }else{ // Old Microsoft browsers use this type! request =new ActiveXObject("Microsoft.XMLHTTP"); }`好的,我们开始运行了。我们做的第一件事是创建一个标准 HTML 页面的顶部,然后启动一个名为loadword
的新 JavaScript 函数,它将完成页面的大部分繁重工作。在这个函数中,我们首先需要弄清楚我们使用的是现代浏览器(即最近五年发布的浏览器,如 Firefox、Safari、Opera、Chrome 或更新版本的 Internet Explorer)还是旧的 Microsoft 浏览器(Internet Explorer 5 或 6)。原因很简单:旧的微软浏览器不知道什么是XMLHttpRequest()
对象!对于他们,我们将要求创建一个 ActiveX 对象,这将为我们提供相同的功能。这只是对微软过去编写浏览器代码的一种有趣的妥协。让我们继续使用清单 12–3 中该页面的 JavaScript。
清单 12–3。word.html,第二部分:JavaScript onreadystatechange 函数,以及 JavaScript 块的结尾
` request.onreadystatechange=function() {
if ( request.readyState == 4 &&request.status == 200){
document.getElementById("theword").innerHTML = request.responseText;
}
}
request.open("GET","word.txt",true);
request.send();
}
`
现在我们有了请求对象,恰当地命名为“request
”,我们可以要求它做一些事情。代码的下一部分稍微“向后”写,因为写在顶部的代码将在写在底部的代码之后执行。这是因为 AJAX 的核心 XMLHttpRequest 对象有一个名为onreadystatechange
的特殊函数。每当我们在后台处理请求时,无论是预期的还是意外的,它都会触发这个函数。本质上,这个函数是我们放置“我用我得到的东西做什么”代码的地方。你可以把这里的代码想象成“代课老师的指令”的编程等价物作为一个聪明的学生,你可能记得,当你的老师不在时,他或她会留下替换的说明,也许在一个特殊的文件夹里。您的onreadystatechange
函数充当这些指令,告诉计算机(在这种情况下,JavaScript 引擎)当您不在身边但有事发生时该做什么。大多数情况下,这些指令与我们前面的指令相似(在本例中,“如果请求被正确处理,则在屏幕上显示它”)。我们还可以包括针对“最坏情况”的说明,比如当请求无法实现或者抛出错误时。
在这个函数中,我们首先测试一下readyState
和status
是我们的请求。前者可以返回五个不同的数字,它们对应于请求的处理阶段。返回的代码
- 0 表示请求尚未开始。这是发送新请求之前的状态。
- 1 表示与服务器的连接已经初始化。本质上,管道是开放的,数据是移动的。
- 2 或 3 分别表示请求已经发送并且正在处理。此时,您的代码所能做的就是坐着等待(或者显示一个“正在加载”的图形,如果您愿意的话)。
- 4 表示请求完成并准备好代码。现在,您可以对这些信息做任何您想做的事情。我们只想在达到 4 的
readyState
时改变页面上的文本。
我们还关心status
,它可以报告 200(表示“OK”)或 404(表示“未找到”)。因此,清单 12–3 的第二行解释为:“只有当请求准备好并且成功完成时才这样做!”从这里开始,只需一行代码就可以将省略号或文本占位符(参见清单 12–4)更改为请求返回的文本。
完成清单 12–3,我们正在创建并发送我们的请求。以request.open
开头的行调用指定我们试图检索什么以及我们想要如何检索的函数。在这种情况下,这是一个非常简单的请求——我们只是获取数据,而不是发送任何我们希望服务器解析的数据。我们将使用“GET”方法而不是“POST”方法,因为这样更快,我们将请求“word.txt
”,并且我们将 asynchronous 设置为“true”,因为我们希望在后台得到响应,从而允许脚本继续运行。关于我们为什么把它设置为 false 的讨论,以及 GET 和 POST 方法的更多细节,请参阅本章末尾的“AJAX 注意事项”一节。
最后,我们简单地调用request.send()
,它触发我们的请求。一旦我们得到请求,XMLHttpRequest 对象将触发我们的onreadystatechange
并更改网页中的行。
最后,我们通过创建基本的 HTML 结构来完成清单 12–4 中的页面。我们有一个特殊的div id
叫做“theword ”,它以省略号(…)开始。这将被我们在顶部编写的 JavaScript 代码所取代。我们还有一个按钮,启动我们的功能loadword()
。
清单 12–4。word.html,第三部分:页面的其余部分,由一个简单的正文部分和文本组成,以及“单词”部分
`
<body> <span style="font-family: Helvetica,Arial,sans-serif;">Get Today’s Word Of The Day<br> <div id="theword"> <h2>...</h2> </div> <button type="button" onclick="loadword()">Get The Word!</button>` `</span> </body> </html>`在 Android 网络浏览器上查看时,页面加载并显示默认状态(参见图 12–6)。
图 12–6。 当日单词示例第一页
现在,当用户点击“获取单词!”按钮,省略号被替换为从word.txt
中提取的当天的实际单词(参见图 12–7)。您还会注意到文本似乎缩小了一点——这是因为 AJAX 请求占据了整个‘theword’ <div>
部分,包括通常会提供更多间距的<h2>
标签。我特意这样做是为了指出,在考虑 AJAX 请求时,标记的嵌套很重要。将<h2>
放在<div>
外面可以保留空间。
图 12–7。 这个词现在已经放入页面,无需重新加载页面!
成功!我们通过调用服务器改变了页面的内容,而没有真正重新加载页面。更好的是,如果用户点击浏览器上的“reload ”,我们将避免类似于 Figure 12–8 中的难看错误,因为我们实际上没有向服务器发送任何“POST”数据(传统意义上)。有关 POST 数据的更多信息,请参见本章末尾的“AJAX 注意事项”一节。
图 12–8。??【安卓确认重装】对话框
现在,单词的基础已经完成,我们可以继续让它更漂亮一点(例如,使用前面提到的 CSS 方案)。不过,出于我们的目的,您现在应该对如何不仅通过 PHP(如前几章所示)而且通过 AJAX 包含平面文本文件有了很好的了解。我们的下一站?web 服务、XML 和 JSON 的奇妙世界。
我的消息!
我们的下一个例子调用了几个 web 服务来为我们提供不会存储在我们自己的设备上的内容。我们还必须对这个世界采取一种稍微以自我为中心的观点,但是,嘿,这是作为一名程序员的职责所在!
假设我们有许多喜欢访问的网站,我们希望我们的应用显示这些网站的最新消息。为此,我们需要将这些网站的 RSS 提要集合在一起,合并它们,然后显示它们。我们希望列表在我们的页面上实时更新,无需用户干预。听起来很复杂?不尽然——如果你知道一些窍门的话!
首先:创建管道
雅虎!Pipes 是我最喜欢的网络服务之一(图 12–9,[
pipes.yahoo.com](http://pipes.yahoo.com)
),它没有被大多数博客或网站过多报道。但是它提供的服务非常棒——它可以将 RSS 源和其他数据缝合在一起,并以你选择的易于使用的格式提供它们。
图 12–9。 雅虎!管道主页
而其他网站允许你把东西连接在一起(Dapper,另一个雅虎!属性和 ifttt (If This Then That)提供了更加可定制的“输出”,因此我们将在本例中使用它。首先,点击“创建管道”来加载管道编辑器,如图图 12–10 所示。管道编辑器允许您将一系列模块(在左侧)组织成一个逻辑规则结构。每个管道从一个或多个输入开始,然后使用模块对它们进行操作(例如,更改数据或合并数据),然后以各种形式输出结果。
图 12–10。 雅虎!管道编辑
在左侧,您会发现一系列模块,您可以将它们拖入 Pipes UI。可以连接这些模块来执行您想要的操作。我要用三个模块——获取站点 Feed、Union、Truncate——把三个网站的 Feed 集合在一起,合并在一起,然后截掉五项之后的输出(图 12–11)。
- 获取网站提要获取网站的 RSS 提要的 URL。在这个例子中,我将为三个不同的网站获取三个 RSS 提要,并将它们链接在一起。你会注意到在图 12–11 中,我有三个“获取站点馈送”模块。
- Union 只是将最多五个输入组合成一个提要。这里,工会正在合并我输入的三个提要。如果需要的话,我可以有多个联合,通过“联合——连接”到一个联合来供给五个以上的管道,等等。
- Truncate 告诉 Pipes 我只想输出五个项目。这有助于加快响应时间,因为 Pipes 只需要导出几个项目,而不是多个提要可能提供的几十或几百个项目。这是我在管道输出模块之前连接的最后一个东西。
图 12–11。 完工的管道
在调试器窗口中(Figure 12–12),我可以看到整个管道或沿途各个部分的输出。当我点击“管道输出”时,显示如下输出(在图 12–12)如果我点击了 Union,我会看到所有的提要条目,而不仅仅是 Truncate 命令后显示的五个条目。当调试管道以查看哪个组件可能导致问题时,这种中间级别的调试非常有用。
图 12–12。 我的管道的全部输出,假设我们已经在五个项目后截断了
完成后,点击“保存”并给你的管道命名。然后点击“运行管道”查看输出(图 12–13)。
图 12–13。 我创作的书管
你会注意到雅虎!管道为我提供了如何访问管道的选项。我可以将它作为 RSS 提要本身来访问,或者作为 JSON 结果输出来获取。在“更多选项”下,我还可以配置通过电子邮件或 PHP 获取结果。在这个例子中,我想得到 JSON 格式的结果。你应该还记得,在本书的前面,在第二章和第三章的应用中,我们已经使用了 JSON。这里,我们再次使用它来获取管道的输出。点击 JSON 按钮会给我一个相当难看的屏幕,里面全是内容,但这确实是我想要记住的 URL。它看起来有点像[
pipes.yahoo.com/pipes/pipe.run?_id=0a1f972788e5ee484b335cb892fc85be&_render=json](http://pipes.yahoo.com/pipes/pipe.run?_id=0a1f972788e5ee484b335cb892fc85be&_render=json)
,可以通过我的 AJAX 网页调用它。
第二:获取输出并显示!
我们将修改我们的每日一词示例来提取 JSON 输出并显示它。虽然有更简单的方法可以做到这一点(最明显的是使用 JSON 的 getJSON 函数,并循环列表项),但是清单 12–5 中的代码更长一些,这使得更容易看到我们正在做什么来解析每个项。稍后,作为 JavaScript 技能的一种灵活运用,如果您愿意,可以使用 getJSON 重写这段代码。
清单 12–5。mynews.html??
`
<head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> <title>My News</title> **<script type="text/javascript" src="jquery-1.7.1.min.js"></script>** <script type="text/javascript">` `function loadnews() { if (window.XMLHttpRequest) { // Modern browsers use this type of request request = new XMLHttpRequest(); } else { // Old Microsoft Browsers use this type! request = new ActiveXObject("Microsoft.XMLHTTP"); }request.onreadystatechange=function() {
if ( request.readyState == 4 && request.status == 200) {
var resp = jQuery.parseJSON(request.responseText);
var lister = "
- ";
- " + resp.value.items[0].title + " ";**
- " + resp.value.items[1].title + " ";
- " + resp.value.items[2].title + " ";**
- " + resp.value.items[3].title + " ";**
- " + resp.value.items[4].title + " ";**
** lister += "
lister += "
** lister += "
** lister += "
** lister += "
** lister += "
document.getElementById("thenews").innerHTML = lister;
}
}
request.open("GET","http://pipes.yahoo.com/pipes/pipe.run?_id=4659b1f4f4bfdb96c3775b61be8ca3b8&_render=json",true);
request.send();
}
...
你会注意到,这段代码的大部分与我们在《今日一词》中使用的代码相同。值得注意的是,我们改变了一些东西,这些东西在我们的代码中是加粗的。
- 我们已经将请求从
word.txt
更改为 Yahoo!管道 JSON 输出。 - 我们现在使用 jQuery,这是一个我们以前使用过的库,它包含一些读取和解码 JSON 输出的有用函数。使用 jQuery 意味着我们必须在顶部添加对
jquery-1.7.1.min.js
文件的引用。 - 我们现在使用
jQuery.parseJSON
来解析从 XMLHttpRequest 返回的文本。 - 我们得到了一个 HTML 格式的列表,一旦检索到 JSON,就会显示出来。
成品在我们的浏览器中是这样的(Figure 12–14):
图 12–14。 我的新闻应用
当我们点击“获取新闻!”显示如下(图 12–15):
图 12–15。 新闻现已加载!
现在我们已经从服务器加载了数据,从 web 服务加载了数据,我们可以开始最后一部分了:网页和数据库之间的来回交互。
用户名可用性
网站在过去几年中做的一件好事是“即时用户名可用性”字段。这是使用 AJAX 完成的,通过检查用户在“用户名”字段中输入的内容,同时检查它在数据库中的可用性。我们将使用 AJAX 创建这种体验,在后端使用 PHP 与 MySQL 数据库通信。这类似于谷歌对其即时搜索结果所做的,我们之前讨论过的功能。我们将从创建数据库开始,使用清单 12–6 中的模式。这个清单首先创建一个表,然后用几个测试用户名(“hardcore”、“恐龙”等)填充它。).
清单 12–6。 用户表的 SQL 模式
CREATE TABLE IF NOT EXISTS
users (
name` varchar(25) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
--
-- Dumping data for table users
INSERT INTO users
(name
) VALUES
('hardcore'),
('dinosaur'),
('yelius'),
('tep');`
使用前面的模式在 MySQL 数据库中创建 users 表。(MySQL 在[
www.mysql.com/products/community/](http://www.mysql.com/products/community/)
可用。)注意,您可以随意命名数据库,但是在本例中,它被称为“datab”现在我们需要一个简单的 PHP 脚本,它接受一个值并在数据库中检查它。清单 12–7 应该很好。
清单 12–7。check _ name . PHP 脚本
<?php $name = $_GET['u']; $user = "root"; $pass = ""; $server = "localhost"; $db = "datab"; mysql_connect($server,$user,$pass) or die("Can not connect"); mysql_select_db($db) or die("No such database"); $query = "SELECT * FROM
userswhere
name = '$name'"; $result = mysql_query($query); if (mysql_num_rows($result) == 0) { echo "<font color=green>Available!</font>"; } else { echo "<font color=red>Not Available :( </font>"; } ?>
前面的脚本相当简单。它连接到数据库并执行一个简单的查询,查看在多少行中输入了姓名。如果在任何行上都找不到该名称,那么它是可用的;脚本发回了“可用”(大概会有很多欣喜)。但是,如果它不可用,因为数据库中已经有一个包含该名称的行,那么脚本会返回一个更令人难过的“不可用”您可以通过访问 web 浏览器中的check_name.php
脚本来测试这一点,包括带有某种测试用户名的&u=test
。例如,在我的测试环境中,[
localhost/jon/uname/check_name.php?u=dino`](http://localhost/~jon/uname/check_name.php?u=dino)`返回“可用”,而`[`localhost/jon/uname/check_name.php?u=dinosaur](http://localhost/~jon/uname/check_name.php?u=dinosaur)
返回“不可用”如果这对你有用,那么你就可以继续了。
后端完成后,我们现在将构建一个非常简单的表单,它将在用户输入时检查用户名,并告诉他或她所需的名称是否可用。
我们将使用清单 12–8 中的代码来完成这项工作。
清单 12–8。checkname.html页面
`
<head> <title>Registration Page</title> <script type="text/javascript">function checkusername($name) {
if (window.XMLHttpRequest) {
// Modern browsers use this type of request
request = new XMLHttpRequest();
} else {
// Old Microsoft Browsers use this type!
request = new ActiveXObject("Microsoft.XMLHTTP");
}
request.onreadystatechange=function() {
if ( request.readyState == 4 && request.status == 200) {
document.getElementById("theres").innerHTML = request.response;
}
}
request.open("GET","check_name.php?u=" + $name,true);
request.send();
}
...
你会注意到,和上次一样,我们使用了与今日词汇相同的格式。这一次,我们更改了函数以接受一个参数(我们需要检查的名称),并且将按钮更改为一个文本字段。这个文本字段在每次释放一个键(onkeyup
事件)时检查名称,并更新结果。加载后,页面看起来类似于图 12–16。
图 12–16。 查看用户名页面
如果您输入一个开放的名称,如“跳过”,您将看到一条成功的消息,如图图 12–17 所示。
图 12–17。 甜蜜的胜利
然而,如果你想要的用户名已经被某个了不起的人使用了,那么你将会看到一个悲伤的消息,比如图 12–18 中的消息。
图 12–18。 “不可用”屏幕
在整个 web 表单中使用这种技术,不仅可以即时检查用户名,还可以验证信息,这样用户就不必点击“提交”按钮,却发现输入了无效的号码、电子邮件地址或用户名。
在我们结束之前,有两个关于表单的快速提示可能会对您有所帮助。首先是考虑您希望将 AJAX 检查代码绑定到什么事件。在本例中,我们已经绑定到正在检查的表单字段上的onkeyup
事件(用户名)。然而,这会导致每次按键时都有一个查找动作,这可能会使您的脚本陷入困境。另一种方法是将功能代码放在onfocus
事件的下一个元素(例如,可能是一个密码字段)中,而不是放在onkeyup
中。这将导致 AJAX 请求在用户移动到表单上的下一个字段后触发,从而减少负载。第二个考虑事项涉及表单的验证和自动提交。填写完最后一个字段后,让表单自动提交是很诱人的。毕竟,您可能会在用户输入信息时验证信息,所以为什么要让他们在完成最后一个字段后等待呢?这可能是一个问题,因为有些人可能希望在继续之前检查他们的答案。最好在页面上保留一个“提交”按钮,让用户在准备好的时候继续。
AJAX 的注意事项
在这个相对较短的章节中,我们使用的例子涵盖了在使用 AJAX 时要与之交互的三种主要数据类型。我们从服务器上获取数据,不经刷新就放入页面,我们查询 web 服务获取 RSS 标题,我们检查用户名以确保它们可用。至此,您已经拥有了编写利用 AJAX 功能的应用所需的基本工具。从这里开始构建时,有几件事情需要考虑,我们将在下面重点介绍。
POST vs. GET
您会注意到,在我们的例子中,我们一直使用 GET 方法而不是 POST 方法,如清单 12–3 和 12–4 所示。这主要是因为速度。GET 比 POST 快得多,因为它使用的格式更简单。如果您想知道为什么,这很可能是由于 GET 的原始意图——它被设计为只用于“等幂”数据。这仅仅意味着信息不能持久使用(也就是说,它应该只是一个查找值,而不是要输入数据库或以任何方式处理的数据)。
然而,有些时候 POST 是合适的。首先,POST 没有 GET 的大小限制,所以如果您要向服务器传递大量信息,就需要使用 POST。其次,当您在服务器上更改某些东西(比如数据库更新)时,应该使用 POST,而不是引用静态或缓存的内容。简而言之,如果您正在传递不再需要的数据,请使用 GET。如果要传递应该处理或输入到数据库、电子邮件或文件中的数据,请使用 POST。
使用 POST 比 GET 稍微复杂一些。您需要设置一个请求头,并通过 send 函数发送信息。这是因为 POST 数据编码在消息体中,而 GET 数据只是简单地附加在 URL 上。这就是为什么 Android 浏览器在 Figure 12–8 中提到重新发送 post data——它指的是这种特殊编码的消息。如果我们想将我们的用户名示例更改为使用 POST,我们可以更改以下几行:
request.open("GET","check_name.php?u=" + $name,true); request.send();
这一更改将导致清单 12–9 中的行。
清单 12–9。 在用户名可用性示例中使用 POST
request.open("POST","check_name.php",true); request.setRequestHeader("Content-type&","application/x-www-form-urlencoded"); request.send("u="+$name);
我们还必须修改 PHP 脚本(参见清单 12–7,将第一行从$name = $_GET['u'];
改为$name = $_POST['u'];
设置异步为假?
正如您在前面的例子中看到的,XMLHttpRequest 的open
函数的第三个参数通常设置为“true”。这种方式是有意义的——毕竟,我们为什么要将它设置为 false 呢?答案是,我们很少会这样做!尽管如此,在某些情况下你可能会。例如,如果您正在编写一个脚本,并且您绝对不希望在请求被返回并准备好进行处理之前发生任何事情,那么可以将其设置为 false。我想到的一个例子是一个用户名检查表单,类似于图 12–16 中的表单,它应该只允许用户在用户名可用的情况下继续使用表单(也许可以基于它定制其他选项)。在这种情况下,可以禁用或隐藏所有后续的表单字段,然后等待请求返回;如果成功,则可以重新启用或显示这些字段。但是要注意的是,你的整个脚本将会被延迟,所以对大量的数据这样做并不是一个好主意。人们可以想象整个浏览器“卡住”了,这在任何平台上都是令人沮丧的事情,但在对速度的需求已经超过其可用性的移动设备上尤其令人烦恼。
还要注意,如果您决定将 asynchronous 设置为 false,您不需要(也不应该使用)函数onreadystatechange
。这是因为没有要寻找的就绪状态—请求就绪后,代码将继续处理。只需将剩余的 JavaScript 代码放在send
函数之后,它就会按顺序处理。
最后,在将 asynchronous 设置为 false 之前,您可能希望问问自己,对于您想要完成的任务,AJAX 是否真的是最佳解决方案。当然,它可能看起来很酷,但是如果你需要强迫用户等待,也许用不同的方式实现你的代码会更容易。例如:
- 如果您的用户有可能禁用 JavaScript,要求 JavaScript 完成表单将会使页面对他们无用。使用异步设置为 true 的 AJAX 通常可以避免这个问题。
- 在几个选项之后需要复杂验证(例如,数据库查找)的长表单可能更适合作为多页表单,允许用户在第一页创建一个帐户,然后在空闲时返回到后续页面。每个页面都可以保存表单的进度,允许用户跳回到需要的地方,同时还提供足够的验证。
有了这些想法和您自己的测试经验,您不仅可以了解最好的表单提交方法 get 或 POST,还可以了解使用不常见的方法将 asynchronous 设置为 false 对您的脚本来说是否是个好主意。
总结
在这一章中,我们已经向前迈出了一大步,让我们的用户生活更加轻松。我们已经开始在后台对我们的表单或页面进行一些处理,这样用户就不需要刷新页面、提交表单或重新加载不必要的内容。通过在你的项目中,甚至在我们之前的例子中,实现这里概述的想法和概念,你将能够立刻创造出令人敬畏的“超级增压”设计!
十三、打包您的应用
因此,终于到了这一步:最后一章。在这本书里,我们回忆了过去的日子(也称为 20 世纪 90 年代),我们了解了 CSS3、HTML5 和 JavaScript 的神奇之处。我们玩了音频和视频。我们已经了解了使用 JavaScript 框架来提高我们的生产力并为我们的用户提供非常容易创建的页面布局的乐趣。我们甚至创建了一系列初学者应用,其中一个最终把你变成了一个国际神秘人,让你开始你未来的明星之路。希望在阅读完这本书之后,我们已经成功地强调了优化代码的重要性,并且在开发过程中要注意许多移动用户面临的有限带宽。
然而,像所有美好的事物一样,我们的时代很快就要结束了。不过,在我们结束之前,我还有更多信息要告诉你。虽然我们已经完成并构建了一个有趣的应用库,但是我们仍然没有讨论在构建这些应用时应该做些什么!在这一章的其余部分,我们将涵盖从打包和压缩你的应用,让它们为互联网做好准备;选择一个合适的托管服务来存储你的应用;将您的文件传输到您使用的任何托管服务;甚至跳过整个托管服务解决方案,而不是将您可信赖的 HTML5 代码包装在 PhoneGap 或 Appcelerator 的 Titanium Mobile 之类的框架中,这样您的代码就像您的用户可以从 Android Market 安装在他们的设备上的原生应用一样工作!
压缩应用
现在,您已经完成了代码的编写并在本地开发环境中进行了测试,您可能很兴奋地将您的应用上传到互联网,并开始告诉您认识的每个人去查看它。在任何项目中达到这一阶段都是一项成就,但是仅仅因为你完成了代码并不意味着你没有任何工作要做。在这本书里,我一直试图强调聪明编码的重要性;做一些事情,例如使用 CSS3 来创建精彩的渐变效果,以及定制 web 字体(由 Google 的惊人的 web 字体目录提供)来代替图像或其他“沉重”的资源,如 Flash 电影。这些最终会帮助你的代码在用户的设备上快速加载。
当然,我们正处于向任何想要的人提供全国范围的第四代移动互联网的尖端,但实际上,你的运营商的宽带服务有多快并不重要,因为移动网络严重缺乏延迟(在数据传输之前,你的手机与你试图加载的网站或服务进行通信所需的时间)。)为了帮助我们的代码更快地到达我们的用户,并反过来帮助减轻我们糟糕的 web 服务器的压力,我们将做一些通常被称为缩减代码的事情。缩小或压缩我们的代码包括通过一个应用或服务运行我们的 CSS 和 JavaScript 文件,该应用或服务将获取代码并删除所有换行符和不需要的空格,以使最终产品比以前小一点。
什么是压缩?
让我们再一次看看许多 web 开发人员使用的非常流行的 jQuery 框架。正如您在下面的图 13–1 中看到的,我们有两个版本的主 jQuery 库。该脚本的一个版本是未压缩的,重量高达 231KB,而另一个版本已经压缩并经过了一轮模糊处理,总重量降低到仍然很高,但更易于管理的 90KB。在代码编辑器中打开我们的 JavaScript 文档,我们可以立即看到两个文件之间的区别。如图图 13–2 所示,jQuery 的开发版本格式非常好,并且有一堆非常有用的注释,而 jQuery 的较小的生产就绪版本看起来就像一种奇怪的外星语言,几乎无法理解。看一看图 13–3 来更好地理解我指的是什么。这就是模糊代码的作用。我们不仅去掉了所有的换行符和漂亮的格式,而且代码本身也被压缩和处理,使代码模糊不清,更难理解。
图 13–1。 我们在这里检查两个不同版本的 jQuery 1.6.2 的文件大小
图 13–2。 在 Adobe Dreamweaver 5.5 中查看未压缩的 jQuery 脚本
图 13–3。 在 Adobe Dreamweaver 5.5 中查看压缩的、模糊的、陌生的 jQuery 代码
有人可能会问为什么我们要隐藏代码——毕竟,难道我们不想让别人看到我们巧妙的技巧吗?嗯,在像这样的书里,我们当然知道,但是在现实生活中,把一些事情“藏在秘密里”会变得非常重要虽然模糊的代码不会阻止无畏的代码——读者猜出你所有的秘密(因此,永远不要依靠模糊的代码来保护诸如密码、信用卡号、核发射代码等。),它将阻止那些知识不多的人窃取你长期努力工作的代码,只是为了重用你整洁的过渡或酷的布局。
压缩工具和实用程序
trustyol 的互联网上有大量的程序可以帮助你使你的产品代码更小,减轻你的服务器和用户资源的负担。其中最受欢迎的是雅虎!sYUI 压缩机(图 13–4)。与其他一些压缩解决方案不同,YUI 压缩程序是一个用 Java 编写的应用,必须在您的开发工作站上运行。在运行应用并从命令行给它你想要的选项后,应用将获取你的代码,如我们所讨论的那样压缩它,甚至将你的许多不同的 JavaScript 或 CSS 文件合并成一个超级压缩的文档,准备好上传到你的服务器。你可以在[
developer.yahoo.com/yui/compressor/](http://developer.yahoo.com/yui/compressor/)
找到 YUI 压缩机。
图 13–4。
使用 YUI 压缩器看起来有点吓人(当查看所有选项时,如图 13–4 所示),但它相当简单。例如,假设我想压缩“那条推特是谁?”中的main.js
文件来自第二章的 app。首先,我会从网站上下载 YUI 压缩机。然后我提取文件,打开一个命令行窗口,切换到包含 Java JAR 文件的目录(在本例中是yuicompressor-2.4.7.jar
;该名称会因版本而异)。接下来,我将复制我想要压缩的文件(到我的当前目录中)并运行压缩命令。我将使用的命令是:
java -jar yuicompressor-2.4.7.jar main.js -o main.small.js
这告诉 YUI 压缩器将main.js
压缩成一个名为main.small.js
的较小文件。在这种情况下,压缩程序将文件大小从 3,763 字节减少到 1,946 字节——略多于一半!
还有一些其他压缩应用绝对值得一提。我想看的第一个是一个由狄恩·爱德华兹创建的叫做 Packer ( [
dean.edwards.name/packer/](http://dean.edwards.name/packer/)
)的方便的小应用,如图图 13–5 所示。这个 web 服务将允许您将您的开发 JavaScript 复制到站点上的 textarea 字段中,只需快速点击一个按钮,就会显示出压缩的 JavaScript 代码,您只需复制并粘贴到一个空白的文本文件中,就可以上路了。如果你认为 YUI 对你的特殊需求来说可能有点多余,那么 Packer 可能是一个非常棒而且非常简单易用的替代品。
然而,需要注意的是,虽然 Packer 是一个很好的压缩代码的工具,但是也很容易通过简单地禁用阻止您将代码粘贴到“复制”框并点击“解码”的 JavaScript 来隐藏这些代码!因此,应该将它简单地用作压缩器,而不是安全机制。
图 13–5。 使用 Packer 我们可以毫无痛苦地缩小我们的“那条推特是谁?”第二章的 JavaScript 代码,在顶部的文本区域,只占原始文档大小的一小部分,如底部的文本区域所示
我想讨论的最后一个神奇的软件是一个代码和应用的小集合,叫做 HTML5 样板,由 Paul Irish 创建,在[
html5boilerplate.com/](http://html5boilerplate.com/)
找到。虽然它不是一个专门的压缩工具,但它是一个非常有用的框架,你可以考虑用于应用开发,它包括自己的压缩工具。HTML5 样板文件是一个非常年轻的项目(它于 2010 年 8 月 10 日推出),对于不想一遍又一遍地编写相同的旧基本代码的开发人员来说是一个很好的起点,因为它包含在他们创建的几乎每个项目中。有人喜欢将 HTML5 样板文件与框架混淆,但实际上远非如此。我更愿意把这个项目看作是一个组织结构和工具箱(图 13–6);它帮助您保持项目文件有序,并通过提供一种简单的方法来实现开发人员必须重复做的许多事情(例如指定某些文件、JavaScript 库等),让您避免重复工作。).
图 13–6。 一看 HTML5 样板文件的文件夹结构。所有神奇的压缩功能都发生在“build”文件夹中。
在样板文件包中,目前是 2.0 版本,可以找到大量有用的项目和特性,比如已经编码好的index.html
和404.html
文件的准系统框架布局;jQuery 的最新副本;一些预定义的 CSS 样式用于在浩瀚的互联网海洋中重置大量 web 浏览器的样式;在移动环境中使用的特殊优化;甚至还有一个漂亮的小代码压缩器,当您准备启动应用的生产版本时,它会为您压缩 HTML、CSS 和 JavaScript 文件。
要用内置工具压缩你的文件,包括我们之前讨论过的像 YUI 压缩器这样的压缩器,你所要做的就是编辑位于构建目录中的build.xml
文件(见图 13–7)。在这里,您会发现一系列选项,您可以指定将您的 web 应用压缩到您想要的内容。你可能需要一点时间来适应处理这个文件,但是一旦你按照你想要的方式设置好了一切,你所要做的就是运行runbuildscript.bat
文件,你的代码将很快被压缩和优化。
图 13–7。 在 Adobe Dreamweaver 5.5 中配置 HTML5 样板构建设置
寻找托管解决方案
既然我们已经设法从代码中挤出了所有不需要的字节,使代码尽可能的小,那么是时候把这个美丽的东西上传到互联网上,让全世界都看到了。寻找一个适合你和你的项目的托管解决方案有时会是一件令人沮丧的事情,尤其是当你不确定你到底在找什么的时候。在这里,我们将谈论三个最大的托管公司,我们对他们的印象以及他们提供的功能。首先,让我们考虑一下选择宿主的过程。
评估主机提供商
并非每个提供商都是相同的,对一个开发人员有效的服务可能不适合另一个开发人员的需求。你需要了解提供商,明确自己的需求和目标。评估任何主机时,以下问题都是一个很好的起点:
- 我有多少个域名或子域名?当许多 web 开发人员开始工作时,最大的问题是,“我有多少空间?”或者“有多少传输量(你每个月可以发送的数据量,而不会产生超额费用——基本上就是你可以有多少访问者)?”。然而,现在大多数网站主机为初级程序员甚至一些高级程序员提供了充足的空间和转移。更大的问题可能是,提供了多少个域名(whatever.com)或子域名(something.whatever.com)?原因很简单:开发人员很少一次只有一个客户端。你可能会建立多个项目、多个客户端,甚至为你构建的应用或其他产品运行多个公共站点。你会希望每个品牌都不同,所以 newname.com 比 yourname.com/newname—更好,也更容易记住。
- 他们的技术支持响应速度如何?有常见问题或知识库吗?在我使用现在的网络主机的六年里,以及使用以前的十年里,我不得不给技术支持发五到十次电子邮件,询问各种各样的问题,从次要问题(“服务器是否支持…?”)到少校(“我所有的站点都瘫痪了。”).每次我都想立刻得到答复,尤其是后者,因为等待的时间太长了。不过更好的是,我目前的虚拟主机提供了一个非常好的 FAQ 和知识库,涵盖的内容远远不止如何重置密码或创建新的电子邮件帐户。它讨论了 PHP 版本、服务器软件、shell 访问等等。要点是:当你遇到麻烦时,你要确保信息已经可以帮助你,或者这种帮助是一封快速的电子邮件回复。
- 有哪些管理工具可用?你会希望选择一个在你可以用账户做什么方面有足够可管理性的网络主机。有些主机可能希望您在每次需要设置一个新的域、数据库或其他任何东西时联系他们。其他人可能通过控制面板完全自动化这一过程。我倾向于后者,因为前者通常总是可用的。我现在的主机甚至允许我在网上取消域名或我的整个账户,如果我决定离开的话,就不用打一个尴尬的电话了。
- 如果我超出了我的计划,会发生什么?几年后,您可能需要访问更多功能或资源。接下来,在我们对 Dreamhost 的讨论中,我们注意到它提供了某些特性来“扩展”或“升级”你的包。如果你担心未来,你可以调查或询问潜在的主机对你来说改变计划或升级有多难。如果他们说您必须重新设置一切,您可能希望找到一个主机来为您迁移一切,或者简单地升级您而无需任何必要的更改。
- 我有 shell 和/或 CRON 访问权限吗?最后一条有点技术性,但如果你正在做一些开发工作,它可能很重要。Shell 访问或 shell 帐户是指您作为客户端以交互方式登录到主机服务器的能力。通常通过一种叫 SSH 的服务,shell 帐户可以让你直接在服务器上运行命令,就像现在你电脑上的命令提示符或终端窗口一样。CRON 是服务器上的一个特殊软件包,可以让您安排以后或定期运行命令。如果您创建一个需要定期自我检查的应用,比如一个向您的用户发送电子邮件的应用,这可能会非常有用。每隔 20 或 30 分钟,您可能需要自动运行一个脚本来检查是否有电子邮件要发送。CRON access 允许您这样做。然而,CRON 也可能被滥用,这就是为什么一些 web 主机不允许您使用它,或者根本不允许您交互登录服务器。
除了前面的指导方针,当决定选择什么主机时,你也可以咨询评论网站。不过要小心:这些网站上一些过于热情或过于负面的评论可能并不代表实际的服务。在一天结束时,我们将永远相信我们知道真正使用过服务的人的建议,而不是粗略的评论,这就是为什么我们会很快花一秒钟来查看我们过去使用或工作过的一些主机提供商,以给你一个很好的起点,让你找到一个最适合你的。
请注意,以下部分讨论了许多主机提供的功能,我们提到的特定主机只是我们个人使用过的主机。虽然我们显然对这些服务很满意,但我们从不认为它们适合所有人。拿起你在以下段落中读到的,考虑你的选择和价格范围,找到适合你的主机!
梦幻主机
在过去的六年里,我(罗科)个人一直是托管公司 Dreamhost ( [
dreamhost.com/](http://dreamhost.com/)
)的超级粉丝,这是一家由四个雄心勃勃的计算机科学本科生在 1997 年创建的伟大的小公司(见图 13–8)。Dreamhost 已经存在了一段时间,虽然他们在过去经历了一些成长的烦恼,但他们为我提供的服务非常可靠,很少打嗝。他们的价格极具竞争力,通常每月 9 美元,他们甚至为客户提供无限的带宽和无限的存储空间。
图 13–8。自 1997 年以来,Dreamhost 已经在互联网上托管了数百万个网站。目前,他们有超过 500,000 个网站运行在 WordPress 开源内容管理解决方案上
Dreamhost 提供了各种各样的主机,基本的主机形式是在一个共享的环境中。虽然共享主机对于基础项目来说很好,但是走这条路有一些主要的限制。首先,您正在与该服务器上的其他所有人共享一个环境,如果有人向服务器上传了一个脚本,该脚本设法占用了服务器的大量资源,甚至使服务器崩溃,那么该服务器上的所有人都会受到影响。最重要的是,您可以在共享服务器上执行的定制非常少。您不能安装在后台运行的自定义应用或服务,也不能对 web 服务器配置文件进行任何更改。
如果你像我一样是个控制狂,你可能会考虑给自己买一台虚拟私人服务器或专用服务器。虚拟专用服务器基本上是一台运行在计算机内部的计算机。使用特殊的虚拟化软件,可以在安全的沙盒软件解决方案中模拟计算机的硬件,允许您在另一个操作系统中安装和运行整个操作系统,就像它是自己单独的计算机一样。当你从 Dreamhost 租用虚拟专用服务器(VPS)时,你仍然可以在与其他人共享的环境中使用它,但是你有巨大的优势,即受到沙箱保护,免受与你共享该物理服务器的任何其他人的有害行为。
你不仅不用担心因为别人的疏忽而导致网站瘫痪,而且你的资源也得到保护。使用 Dreamhost 管理面板中的一个简单的滑块,您可以动态、实时地为您的服务器分配任意多的 RAM 和 CPU 处理能力(参见 Figure 13–9)。然而,给你的服务增加更多的 RAM 和 CPU 能力,确实会增加你的月租费;但是,如果您有一个应用,您知道它需要专用的 1GB RAM 和 CPU 能力来及时快速地为您的所有用户提供服务,那么知道这些资源会自动为您并且只为您提供保护是一件非常棒的事情。如果你有兴趣在 Dreamhost 上建立一个 VPS,它会给你提供第一周的免费服务,这样你就可以根据自己的需要调整服务器的大小,找到网站所需的最佳资源量。
图 13–9。
Dreamhost 还让你可以选择拥有自己的专用环境。通常这些机器是相对强大的服务器,具有两到四个 CPU 核心,甚至高达 12GB 的 RAM。如果您是一名新的开发人员,您可能不需要那么多的处理能力,但是当有一天您确实需要它时,您现在知道去哪里找了。
除了拥有大量的 Dreamhost 托管站点的选项外,您还可以访问一个反应非常迅速的技术支持团队。如果你有一个问题,或者在半夜你的服务器出了问题,在大多数情况下,你可以在几分钟内与技术人员进行文本聊天,并迅速解决你的问题。在我过去用过的所有托管公司中,当你需要帮助的时候,Dreamhost 是我最喜欢的公司之一。
1 和 1
转换作者(乔恩在这里),现在是时候听听我的网络主机在过去六年,1 和 1。1and1 实际上是一个相当大的主机,业内大多数人对它的评价都不冷不热。不是最好的(虽然谁是值得商榷),也不是最差的。Rocco 讨论过的 Dreamhost 提供的许多功能也可以从 1 和 1(以及许多其他主机,就此而言)中获得,因此我觉得最好简单地概述一下我认为 1 和 1 真正有用的部分。这分为两类:文档和可靠性。文档参考了 1 和 1 常见问题部分,让我惊讶的是,在一般问题中有我之前写的详细信息。我遇到了一个脚本问题,当 1and1 FAQ 网站上有关于服务器设置、PHP 版本和可用软件的非常具体的信息时,我感到非常惊讶。我能够调试我的问题,而不必写一封冗长、复杂的电子邮件。我真正喜欢 1 和 1 的第二个特点是我的服务的可靠性。虽然没有人可以吹嘘 100%的正常运行时间(服务器或服务可用的行业标准术语),但据我所见 1 和 1 非常接近。作为一个最底层的客户(也就是说,我每月支付大约 9 美元的基本共享主机包),如果我在这里或那里有不稳定的服务,我不会感到惊讶。事实上,相反的事情发生了——我在那里的整个时间里,我的网站一直保持正常运行。
如果你正在考虑另一台主机,并想知道它的可靠性如何,你可能会在论坛上找到一个使用它的朋友或人,并询问他们的域名是什么。从那以后,我会建议在 Pingdom ( [
www.pingdom.com](http://www.pingdom.com)
)这样的服务上建立一个免费账户,这是一个正常运行时间监控服务。Pingdom 可以让你免费监控一个网站,所以直接指向你朋友的网站吧。几周或几个月后,您将会看到一些报告,您可以参考这些报告来查看主机是否可靠地启动。
现在,你知道了,1 和 1 对我来说是可靠和全面的。现在回到罗科来讨论最后一个主持人!
媒体神殿
我(Rocco)过去使用过的另一家托管公司,也是我在本章将与您讨论的第三家托管公司,是一家业界最喜欢的公司,名为 Media Temple ( [
mediatemple.net/](http://mediatemple.net/)
)。从很多方面来说,对于更专业的用户来说,Media Temple 提供的服务可以被认为比 Dreamhost 更好。Media Temple 提供了大量的解决方案,首先是基本的网格服务器包(见图 13–10),它将您的站点托管在一个集群上,该集群由数百个其他服务器提供支持。它还像 Dreamhost 一样提供专用的虚拟服务器,以及各种非常强大的专用服务器。
我对 Media Temple 的唯一真正问题是,它往往比 Dreamhost 等其他提供商贵一些,但它确实可以为你提供更好的硬件,以及与 Dreamhost 等公司一样的技术支持;所以,如果你正在寻找力量,一定要去看看媒体圣殿能提供什么。
图 13–10。 媒体寺的网格服务登陆页面
文件传输协议
现在,我们已经为您指出了寻找满足您需求的主机提供商的正确方向,我们必须将我们在本地开发服务器上创建的那些文件上传到我们全新的生产服务器上。这一步通常由文件传输协议(FTP)应用完成。FTP 是网络中的标准协议,用于将文件和文档从一台计算机传输到另一台计算机。在你的主机上有一个运行的 FTP 服务器,它允许你使用桌面或工作站上的 FTP 客户端连接到该主机。有很多免费的 FTP 程序,但是我个人最喜欢的是一个叫做 FileZilla ( [
filezilla-project.org/](http://filezilla-project.org/)
)的免费开源软件,如图 13-11 所示。
图 13–11。 用 FileZilla 把这本书的代码转移到我的一个测试服务器上
我喜欢 FileZilla 的主要原因之一是因为该软件不断更新,与这个项目相关的开发人员努力工作,以确保它是最快速和最稳定的 FTP 程序之一。
使用 FTP 客户端实际上非常简单明了。一旦你注册了一个托管解决方案,你将获得证书,插入到你的应用中,一旦连接,如果你的 FTP 客户端支持该功能,你就可以拖放文件到服务器上你有权限访问的任何文件夹中。以下是一些 FTP 凭据的示例:
url: ftp.myawesomesite.com user: testuser112 password: pssw0rdz port: 21 path: /var/www/myawesomesite.com/
如果你以前从来没有这样做过,这可能看起来有点混乱。在下一节中,我们将从头到尾介绍一个应用的部署。
使用安全 FTP 部署应用
在这个例子中,我们将带你通过使用 SFTP 上传整个应用到一个虚拟主机服务。我们将在 Windows 机器上使用 FileZilla,但如果你使用的是 Mac,你可以使用一个由免费捐赠支持的名为 Cyberduck 的程序。步骤大致相同,尽管在 Mac 上屏幕会有所不同。
首先,您需要收集以下信息:
- 您的 web 主机需要的连接方法。一些网络主机不再支持 FTP,更喜欢加密版本,SFTP。你需要知道你的 FTP 地址(这通常是 ftp.yourdomainname.com,其中你的域名是你的个人域名,比如
jonwestfall.com
)、你的 FTP 用户名和 FTP 密码。 - 您需要知道您的域的文件是否在您的 web 主机上的任何特定目录下。默认情况下,许多 web 主机会将您的 FTP 信息放入您的根目录(即,一旦您登录到服务器,您在“/”下看到的文件与您的域名下的文件相同。所以
/test.txt
对应[
jonwestfall.com/test.txt](http://jonwestfall.com/test.txt)
。其他主机可能会将您的文件放在一个以域名命名的目录中(即/jonwestfall.com
)或一个名为/public_html
或/web
的目录中。 - 你还需要考虑你希望你的文件放在哪里。在这种情况下,我想要“那条推特是谁?”住在
[
jonwestfall.com/whosthattweet](http://jonwestfall.com/whosthattweet)
,所以我需要创建那个目录并将我的文件上传到那里。
一旦你有了你需要的信息,下载并安装 FileZilla 客户端(从[
filezilla-project.org/download.php](http://filezilla-project.org/download.php)
)。安装后,您应该在开始菜单的 FileZilla FTP 客户端组下找到它。打开 FileZilla,您应该会看到类似于 Figure 13–12 的屏幕。
图 13–12。 主 FileZilla 窗口没有任何配置
你会注意到它很空,分成六个窗格。顶部窗格显示了 FileZilla 发送到服务器的 FTP 命令。左侧窗格显示您计算机上的文件(以树形格式列出您正在查找的目录中的文件),右侧窗格将显示您服务器上的文件(在我们连接后,以相同的排列方式)。底部显示了我们当前正在从一端传输到另一端的文件(从我们的计算机传输到服务器,反之亦然)。
首先在顶部的主机框中输入您的 FTP 主机名、您的 FTP 用户名和您的 FTP 密码。如果您的主机支持 SFTP,这是更安全的方式,您将需要输入 SFTP 的端口号,通常在端口框中输入“22”。但是,如果您只能使用常规 FTP,您将使用 FTP 端口号,通常为“21”(注意:如果您的主机更改了这些值,这对于主机来说是非常罕见的,您应该能够在其 FAQ 部分或知识库中找到它们。输入所有信息后,按“快速连接”连接到服务器(图 13–13)。
图 13–13。??【SFTP】FileZilla 主机连接信息
如果这是您第一次连接到此服务器,并且您正在使用 SFTP,您可能会看到以下对话框(图 13–14),要求您验证服务器身份。通常情况下,您不知道要验证什么值,但是您可能希望第一次就注意到它,以确保它总是相同的。这是一项安全功能,可确保您始终连接到正确的计算机。
图 13–14。 FileZilla 未知主机按键提示
登录后,屏幕右侧的窗格会发生变化,显示服务器上的文件。点击文件夹,直到你找到你想上传文件的地方。在我的例子中,我将转到/new-jonwestfall.com
目录(jonwestfall.com
的 web 文件所在的位置)并创建一个whosthattweet
目录。我可以通过双击右侧窗格中的new-jonwestfall.com
,并右键单击文件旁边的空白区域来完成此操作。在出现的菜单中(图 13–15,我选择“创建目录”,在下一个窗口(图 13–16),我给它一个名字。
图 13–15。 右击显示上下文菜单,允许你创建一个目录
图 13–16。FileZilla【创建目录】对话框
现在我确保我已经正确设置了目录:我想要移动的文件在左边,目标位置在右边,如图图 13–17。
图 13–17。 显示源(左边)和目标(右边)
现在只需选择左侧的文件,右键单击它们,然后选择“上传”(参见图 13–18)。
图 13–18。 我现在可以点击“上传”选项将文件上传到服务器
这样做之后,我应该在两个窗格中看到相同的文件,如图 13–19 所示。
图 13–19。 我电脑上的文件(左边)现在在服务器上(右边)
现在大考验来了。现在可以访问这些文件了,我应该可以通过移动浏览器(或桌面)访问相应的地址来查看它们。在本例中,地址是[
jonwestfall.com/whosthattweet/index.html](http://jonwestfall.com/whosthattweet/index.html)
。通过在桌面网络浏览器中访问它,我可以看到文件是在线的,如图图 13–20 所示。这可能看起来很难看,因为这些文件是要在移动网络浏览器上查看的;但是,我们已经确认这些文件在线并且可以访问!祝贺您,您已经将您的项目上传到了网络上!
图 13–20。 游戏在桌面浏览器上无法正常渲染,但是这个测试验证了文件已经被正确上传
软件版本化
FTP 通常适用于只有一个开发人员的小型开发项目,尤其是当项目不是很复杂的时候。然而,举例来说,假设你正在和一个开发团队一起做一个项目,他们中的每一个人都可以访问 FTP 服务器,并且可以随时上传和修改文件。最终,您会遇到这样的情况:一个用户结束了对一个文件的更改,然后一分钟后有人上传了他们对完全相同的文件所做的更改,完全覆盖了第一个人刚刚上传的工作。
这是一个非常令人沮丧的情况,在现实中,这种事情实际上是相当普遍的,并且在程序员没有组织的时候经常发生。防止这种情况的一个简单方法是使用某种形式的软件版本控制,比如 Subversion,来为您备份和控制文件。这样,假设 Jon 对我们共享的一个文件做了一些更改,而我意外地签入了该文件的一个新版本,其中没有 Jon 添加的代码。我们可以很容易地将文件恢复到它本身的早期版本,并保存我们认为已经丢失的代码,而不是惊慌失措。见鬼,使用版本控制,你甚至可以将一个文件的两个先前版本合并在一起,形成一个超级文件。
有大量的版本控制解决方案等待使用,但是最流行的两个解决方案是 Apache Subversion ( [
subversion.apache.org/](http://subversion.apache.org/)
)和 Git ( [
git-scm.com/](http://git-scm.com/)
)。Subversion,也称为 SVN,已经存在了很长时间,是一个非常成熟的产品,在我过去使用的几乎每一个托管解决方案中都有广泛的支持。最好的部分是有一个非常漂亮的 Windows 应用,它将所有的 SVN 选项添加到你的右键菜单中,这将在帮助那些害怕使用命令行的新手开发者方面创造奇迹。
尽管我很喜欢 SVN,但在过去的几年里,我越来越倾向于使用 Git 来满足我所有的版本需求。Git 是另一个用于版本控制的免费开源软件解决方案,与 SVN 相比,它的速度快得惊人,而且非常容易使用,如你在图 13–21 中所见。
图 13–21。 一个快速入门指南,通过几个简单的击键就可以开始使用 Git
Git 也变得更加用户友好,因为它有一个非常受欢迎的网站 GitHub ( [
github.com/](https://github.com/)
),该网站将自己标榜为一个社交编码中心,利用 Web 2.0 的操作方式,让一切尽可能“社交化”(见图 13–22)。有了 GitHub,你可以免费将所有项目存储在 GitHub 云中;然后,您可以让其他开发人员派生或复制您的存储库,以便他们可以对其进行任何他们想要的更改,同时保持这些更改与您的工作完全分离。这有点噱头,但 Git 本身是一项非常棒的技术,如果你不喜欢,你可以学会忽略整个社交活动。
图 13–22。 使用社交编码 hub,GitHub
最好将版本控制视为备份代码和应用的一种方式。事实上,即使您是项目中唯一的开发人员,最好也是在为应用创建第一个文件夹或文件时就对其进行版本化。这样,如果在此过程中发生任何事情,比如你的笔记本电脑爆炸,或者你的孩子觉得你的电脑看起来很渴,把它浸在苹果汁中,你将能够恢复代码,并将其下载到新的机器上继续工作。即使您的计算机没有爆炸,一旦您准备好将您的项目部署到您的服务器上,您所要做的就是登录并在提示符下运行一个简单的命令,将您的版本化程序的内容复制到您的生产服务器(即您的 webhost)上,而无需接触 FTP 程序。
如果你的计算机老师像我成长过程中的老师一样,他们可能会反复强调不断保存你的作品的重要性。就像在大学里,当你不想在写一篇大文章的过程中被断电而失去一切时,确保经常保存你的工作,然后在版本控制解决方案中根据你的选择推出那些新的修订。
本土化
因此,现在我们已经构建了一个应用,通过压缩应用的永久生命优化了应用的大小,为自己找到了一个可接受的托管解决方案,并学习了如何使用 FTP、SVN 或 Git 将文件传输到该服务器——但如果您不想将应用打包,以便通过数据连接在互联网上使用,该怎么办呢?如果您正在构建一个应用,并且您知道该应用将用于没有任何数据覆盖的领域,该怎么办?如果您知道您的应用确实是资源密集型的,并且包含大量您不想等待加载到设备上的音频文件或图像,该怎么办?如果你的项目太棒了,你不想免费赠送,而是想卖给你的用户,怎么办?好吧,不要担心,因为我们有解决方案可以让你把难以置信的 HTML5 和 JavaScript 应用变成一个完全成熟的本地应用,可以在你的用户的手机上运行,而无需打开他们的互联网浏览器。
最重要的是,通过让你访问手机的许多功能(如访问手机的联系人,或创建一个利用手机或平板电脑加速度计的游戏,或在手机的文件系统上创建文件),走原生路线为你开辟了许多事先不可用的途径。这些都是你在使用标准网络浏览器时通常不允许使用的功能,而且理由也很充分!如果有人能够从互联网上修改你的电脑、平板电脑或手机的文件系统,那将会是全球范围内的大规模恐慌。如果你认为现在的计算机世界存在病毒和木马的问题,想象一下,如果你访问了Amazon.com
,一个愤怒的流氓开发者决定使用他的文件系统访问权限删除你计算机上的所有文件,会是怎样的情况?是的,我告诉过你,集体歇斯底里。这就是为什么你的浏览器被锁定了,正如我们在创建我们的 Twitter 应用时所讨论的“谁在发微博?”和“我爱火腿”
然而,有时候需要这种访问,这就是 PhoneGap ( [
www.phonegap.com/](http://www.phonegap.com/)
)和 Titanium Mobile ( [
www.appcelerator.com/products/titanium-mobile-application-development/](http://www.appcelerator.com/products/titanium-mobile-application-development/)
)等框架发挥作用的地方。这两个应用框架都允许开发人员将他们现有的 HTML5 和 JavaScript 代码转化为成熟的应用,可以上传到您选择的任何移动应用商店。虽然这两种产品都允许您将应用带入本机路径,但它们都以非常不同的方式实现,我觉得这值得一提。
语音间隙
PhoneGap 是一个免费的开源解决方案,就像许多流行的 web 开发解决方案一样,它在很短的时间内取得了很大的进步。PhoneGap 是由 Nitobi Software 在 2009 年 iPhone 开发营的 48 小时编码狂欢中创建的,它诞生了,并一直是希望进入移动开发领域而无需花费大量时间学习新编程语言的 web 开发人员的最爱。
随着 Adobe Dreamweaver CS5.5 的最新发布,Adobe 已经将 PhoneGap 框架集成到 Dreamweaver 中,因此开发人员只需简单地单击鼠标,就可以创建一个本地应用,并在 Android 模拟器中进行测试(参见图 13–23)。与如此强大的开发力量的合作,让 PhoneGap 在“一次编写,随处运行”的移动领域比其竞争对手拥有巨大的优势。
图 13–23。 配置 Adobe Dreamweaver CS5.5,使用 PhoneGap 将我们的 HTML5 应用构建为原生应用
让我们快速看一下来自 PhoneGap 示例的一些代码,在清单 13–1 中,这样你就能感受到如何构建你的 HTML5 PhoneGap 应用。
清单 13–1。phonegagpexample
`

This is where you are
正如您在前面的厨房水槽示例中看到的那样,这里发生的事情与您通常构建页面的方式没有什么不同。实际上,这是一个 HTML 文档,这也是 PhoneGap 与其竞争对手相比的不足之处。看,当在你的手机上使用 PhoneGap 应用时,即使该应用现在是本地的,并且可以以正常的互联网托管的 Web 应用永远不能访问的权限挂接到操作系统,你仍然只是在设备上加载网页。这在大多数情况下没问题,但在 Android 设备上,我们必须承认浏览器不像 iPhone 浏览器那样是硬件加速的;所以,如果你的应用中有很多花哨的效果和动画,它仍然会像在网络浏览器中查看时一样缓慢和呆滞。仅仅因为你以一种我们称之为“本机”的方式包装你的应用,它就不是真正意义上的本机。这就是我最喜欢的移动平台发挥作用:钛移动!
钛金属手机
多年来,我一直大力支持 Appcelerator 使用 Titanium Mobile 所做的工作。他们开始采取与 PhoneGap 相同的方法,允许用户用纯 HTML 和 JavaScript 创建内容,但很快意识到有时最好完全本地化以提高性能和速度。大约在 Titanium 移动软件成熟到生产就绪 1.0 版本的时候,Appcelerator 发布了一组新的 API,仍然允许开发人员使用 HTML5 在 web 视图中创建应用,但现在他们可以完全跳过 HTML5,用纯 JavaScript 创建他们的应用。
这样,他们就能够在构建时获取这些 JavaScript 文件,并使用操作系统自己的代码将它们完全转换成真正的本地应用来生成页面布局。这意味着,你不用创建自己的按钮或样式来使用你的应用,而是可以通过为你正在使用的任何平台使用原生用户界面元素来使你的应用看起来更像一个真正的原生应用。
为了不被集成到 Adobe Dreamweaver 中的 PhoneGapwith 超越,Appcelerator 的优秀人员购买了集成开发环境(IDE)公司 Aptana,并创建了他们自己的成熟 IDE,用于他们的框架。自从 Titanium Studio 在 2011 年初发布以来,使用 Titanium 移动环境已经变得轻而易举。
虽然 Titanium Mobile 是一个开源项目,可以免费使用,但有一些特殊的模块只提供给付费用户。成为 Titanium Mobile 的付费用户有其额外的好处,比如可以提前获得 Titanium Mobile 的新版本,并使用一些模块来访问 AdMob 等公司的应用内广告 API 和 Paypal 的应用内购买 API。成为 Titanium Mobile 的订阅会员也很容易,免费基本会员和付费会员的起价为每月 49.99 美元。
如果你能负担得起,并且想要访问某些模块,如 GameKit 和 OpenGL 游戏模块,那么跳基本的独立计划是显而易见的。如果你只是想摆弄软件,创建没有花哨模块的应用,那么你也可以免费这样做!有了 Titanium Mobile,你将能够使用纯 JavaScript 或 HTML5 和 JavaScript 的组合来创建真正令人难以置信的移动应用。
图 13–24。 钛金工作室 IDE,钛金手机官方集成开发环境
关门时间
既然我们已经经历了在移动开发世界中起步和运行所需的一切,是时候说再见了,关掉灯,慢慢走开,而远处某处的钢琴上正在播放 20 世纪 70 年代末电视节目 The Incredible Hulk 的主题曲。我们真的很高兴与你分享所有这些知识和信息,并希望你能在不久的将来使用它来把自己变成一个 10 级的网络巫师,并击败任何棘手的开发情况。我们确信你现在笑得合不拢嘴,准备跳上你的电脑,开始编写你的下一个项目,我们笑得合不拢嘴是因为我们为你感到兴奋;但是在今天离开你之前,我们要再次提醒你一点点 20 年前给罗科的友好建议——保持简单,笨蛋!
感谢您花时间阅读这本书,如果您碰巧知道任何初露头角的开发人员可以从这本关于移动 web 开发的大部头的内容中受益,那么请随意将您的副本借给他们,并将这些知识和信息传递给其他人。保重,编码愉快!
乔恩、罗科和格兰特
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)