想象一下,你像往常一样,正在一条熟悉的路上慢跑,突然被一个念头打动——一款全新的杀手级移动应用!一路上,你甚至忘记了回 ...

App Inventor编程教程-目录

想象一下,你正在逛一家你非常喜欢的书店,并且找到了一本心仪已久的书,你想知道它在亚马逊网上书店的售价,这时“亚马逊掌上书店”应用就可以帮你实现这一愿望。扫描书上的条码,或输入图书的ISBN,应用将告诉你这本书当前在亚马逊网上书店中的最低售价。你还可以搜索特定主题的同类书籍。。。

“亚马逊掌上书店”演示了如何使用App Inventor来创建一个可以访问网络服务的应用。本应用中使用的中文版网络服务由开源教育的志愿者创建。我们将访问该网络服务,并从中获取数据。在学完本章之后,你将能够创建一款个性化的网络应用,来访问亚马逊网上书店。

这个应用界面非常简单,用户输入图书的关键字或ISBN后,应用将显示搜索结果:书名、ISBN以及新书在亚马逊上的最低售价。也可以使用条码扫描器组件扫描书上的条码来实现搜索(从技术上讲,是扫描器替你输入了书的ISBN),这免除了在手机上输入信息的不便。

学习要点

如图13-1所示,本章将学习以下内容。

  • 在应用中使用条形码扫描功能。
  • 用网络数据库组件访问网上信息源(Amazon API)。
  • 处理从网络信息源返回的复杂数据。所谓复杂数据,具体来说就是图书列表,其中的每个列表项包含了一本书的多项信息,而这些信息又构成了二级列表,二级列表包含三个列表项:书名、价格及ISBN。

图13-1 手机中运行的“亚马逊掌上书店”
此外我们还将介绍一些有用的源码,它们用Python语言编写,并部署在谷歌的云应用平台上,使用这些源码可以创建自己的网络服务。

什么是API

在正式开始设计组件和编写程序之前,我们来解释一下什么是API(Application Programmer Interface,应用程序接口),以及如何在我们自己的应用中使用它。可以把API理解为一个网站,只不过它的存在不是为了与人交互,而是为了与其他计算机程序交互。也可以说,网络上提供的信息服务有两类:一类是用于与人类用户交互的,被称为“客户端”程序;另一类就是API,通常被称作“服务器端”程序,它的特点是为“客户端”程序提供信息。我们所创建的App Inventor应用就是一种“客户端”程序,它是面向人类用户的程序。如果你曾经在手机上使用过微博应用,你实际上是通过微博的客户端程序与微博的服务器端程序(API)进行通信。

本章将创建一个安卓的客户端程序,与亚马逊网上书店的服务器端程序(以下简称亚马逊API)进行通信。应用将向亚马逊API请求书的信息(书名、书号及价格),而亚马逊API将向应用返回最新的图书列表,用户将在应用中看到这些信息。

我们即将使用的亚马逊API是App Inventor专用的API。这里我们不想过多地解释细节,但有关配置的知识是非常有用的,正是由于有了这些配置,我们才能用网络数据库组件与亚马逊API进行通信。好在你已经学会了使用网络数据库组件!首先调用网络数据库组件的请求数据功能,然后在获得数据事件中处理收到的信息,就像使用其他的网络数据库一样。(如果需要,可以去复习一下第10章的“出题”与“答题”应用,来唤醒你关于网络数据库组件的记忆。)

在创建应用之前,需要先了解一下亚马逊API协议,协议规定了请求数据的方式以及返回数据的格式。就像不同的民族有不同的礼仪(两人相遇时,是握手、鞠躬还是点头呢?),计算机之间的交互(术语叫作通信)则需要有协议 。我们即将使用的亚马逊API为调用者提供了一个网页,来说明API的使用方法。虽然设计API的目的是为了与其他计算机交互,但在这个页面上,你可以看到这种交互的过程。 按照下列步骤,你可以尝试在网页上调用API的请求数据功能,并在页面上看到返回的数据,这与你在App Inventor中使用网络数据库组件请求数据的结果完全一致。下面我们开始操作。

  1. 在浏览器中访问网站http://aiamazonapi.17coding.net/ ,你会看到如图13-2所示的页面。
    图13-2 App Inventor专用的亚马逊API的网络页面
  2. 在本页面上,可以对此API的请求数据功能进行测试:在标记输入框中输入搜索词(如“新华字典”),然后单击请求数据按钮。页面将显示从亚马逊API返回的排在前五位的图书列表,如图13-3所示。
    图13-3 调用亚马逊API来搜索与“新华字典”有关的图书
    返回值是一个三层列表。最外层由一对方括号包围[像这样],我们称之为一级列表,其中第一项“VALUE”是API协议中的返回值标记,意味着数据成功返回;第二项“新华字典”是我们输入的关键字,也是网络数据库组件请求数据时使用的标记;第三项由一对方括号包围,是一个两层列表,是我们的查询结果,我们称之为书籍列表(或二级列表)——注意,这个书籍列表才是我们在应用中要处理的数据!在书籍列表中,每本书的信息由一对内层的方括号包围,其中提供了三项数据:书名、售价及ISBN,这三项数据构成一个列表,我们称之为图书信息列表(或三级列表)。我们重点关注的是书籍列表。仔细观察会发现:书籍列表中的每个左括号([)标志列表的开头,第一个左括号标志外层列表(书籍列表)的开始,紧挨着它的左括号是第一个子列表,即第一本书的开头:
    [['新华字典(第11版)(双色本)', '¥ 24.90', 'B0058BZOWA']

    三级列表中包含三个部分:书名、该书在亚马逊书店的最低售价及这本书的ISBN。当你的App Inventor应用取得这些信息时,就可以使用“列表()中的第()项”块来访问其中的每个部分。对于三级列表来说,用索引值1访问书名,索引值2访问价格,索引值3访问ISBN。(如果淡忘了有关列表及索引值的使用方法,请复习第10章的“出题”应用。)

  3. 除了搜索关键字,你还可以通过ISBN来精确地搜索一本书,只要在标记后面直接输入书号即可,试试输入“isbn:9787508028538”,如图13-4所示。返回结果如下: [“VALUE”,”isbn:9787508028538″,”[[‘汉字源流字典’, ‘¥ 50.00’, ‘B00183IACI’]]”]。观察返回结果,其中列表中的第一项“VALUE”表示数据返回成功,第二项“isbn:9787508028538”是请求数据时使用的标记,第三项是我们请求的数据——书籍列表。注意观察,书籍列表仍然以两个“[[”开头。虽然书籍列表中只有一本书,但数据的结构是不变的,即返回的数据依然是一个三层列表,这一点对应用来说非常重要,它保证了程序的稳定性。
    图13-4 用ISBN作为标记从亚马逊API查询书籍

准备开始

登录App Inventor网站,创建一个名为“亚马逊掌上书店”的新项目,设置Screen1的标题属性为“亚马逊掌上书店”,然后连接测试设备或模拟器,准备进行实时测试。

设计组件

“掌上书店”应用的用户界面比较简单:一个文本框、三个按钮以及两个标签。其中的文本框用于输入搜索关键字或ISBN;两个按钮用于启动搜索,另一个按钮用于启动手机中的条码扫描软件(稍后会用到);一个标签用于显示搜索结果的标题,另一个标签用于显示搜索结果。还有两个非可视组件:网络数据库及条码扫描器。如图13-5所示。

图13-5 设计视图中的“亚马逊掌上书店”用户界面
表13-1中列出了图13-5中所示的所有组件,请对照检查你的结果。

表13-1 “亚马逊掌上书店”应用中的所有组件

设置搜索内容输入框的提示属性为“请输入搜索关键字或ISBN”。组件的属性设置如下。

  1. 设置按钮及标签的显示文本属性,如图13-5所示。
  2. 设置网络数据库组件的服务地址属性为http://aiamazonapi.17coding.net 。

设计行为

在编程视图中实现应用的下列功能。

  • 按关键字搜索
    用户输入关键字并点击关键字按钮来启动图书搜索。通过调用网络数据库组件的请求数据功能来实现这一点。
  • 按ISBN搜索
    用户输入一个ISBN并点击ISBN按钮。注意,在搜索之前为书号添加前缀“isbn:”。
  • 条码扫描
    用户点击扫条码按钮,启动条码扫描应用,扫描书上的条码来自动获取ISBN。当扫描完成时,开始启动图书搜索。
  • 处理图书列表
    先采用默认方式显示亚马逊API返回的数据。稍后再加以修改,采用有组织的方式来显示每本书的书名、售价及ISBN。

搜索关键字

用户点击关键字按钮时,从搜索内容输入框中获取搜索关键字,并以此为标记,使用网络数据库组件的请求数据块向亚马逊API请求搜索数据。当亚马逊API返回搜索结果时,将触发网络数据库组件的获得数据事件。现在用搜索结果标签直接显示返回结果,如图13-6所示。稍后,当你确信有数据返回后,可以采取更为规范的方式来显示数据。

图13-6 向API发出数据请求,并显示收到的数据
块的作用

当点击关键字按钮时,使用网络数据库的请求数据块向API发出请求。随请求一同发送的还有参数标记,这正是用户在搜索内容输入框中输入的内容。从第10章的“出题”应用中得知,网络数据库的请求数据操作并不能立即获得返回结果,应用会侦测是否有数据从API返回,当有数据返回时,将触发网络数据库的获得数据事件。在获得数据事件的处理程序中,首先检查返回值是否为列表,如果是,则数据被写入搜索结果标签。(如果亚马逊API停止服务或搜索的关键字不存在,将没有数据返回。)

测试:在搜索内容输入框中输入搜索词,如“黄帝内经”,然后点击关键字按钮。你将得到类似于图13-7的结果。注意:手机上返回的结果与网页上的结果略有不同:方括号变成了圆括号。(这个界面看起来很糟糕,稍后我们会处理。)(第一本书的价格显示“无”,意味着书的库存为0,因此没有价格信息可以返回。——译者注)

图13-7 搜索关键字“黄帝内经”返回的结果
按ISBN搜索

对ISBN进行搜索的代码与关键字类似,只不过使用的标记参数不同。当亚马逊API收到以“isbn:xxxxxxxxxxxxx”为标记的数据请求时,将返回与书号相匹配的图书信息(这是亚马逊API为数据访问者设置的通信协议。当API识别出标记以isbn开头时,将返回与后面的书号相匹配的数据)。用户不必了解这些协议,只需在输入框内输入想查找的书号,并点击ISBN按钮即可。在书号前添加“isbn:”前缀的任务由程序来完成——用拼字串块来合成最终的标记字串。具体代码如图13-8所示。

图13-8 用带有“isbn:”前缀的标记来搜索某本特定的书
块的作用

这里用拼字串块将前缀“isbn:”与用户输入的书号进行整合,并以此为标记向亚马逊API发出数据请求。

与关键字搜索一样,API将对ISBN搜索返回满足条件的列表数据,不过此时列表中只有一个列表项,因为与搜索书号相匹配的图书只有一本。此前我们为关键字搜索设置的获得数据事件处理程序,也可以直接用于处理ISBN数据请求返回的结果(尽管这次列表中只有一项)。

测试:在搜索内容输入框中输入一个ISBN(如“9787115308108”),并点击ISBN按钮,是否收到了图书的信息?

消除用户的困惑

从前几章中我们了解到,使用网络数据库请求数据的过程需要一点时间。在收到返回的数据之前,用户界面上悄无声息,这时用户会感到困惑,因此从用户体验的角度考虑,添加一点提示是非常必要的。这里我们用搜索结果标签来显示提示信息。如图13-9所示。但如果网络速度很快,数据很快返回并触发获得数据事件,那么,用户几乎没看到提示信息,返回的数据就显示出来了。

图13-9 添加提示信息以消除用户的困惑
块的作用

无论是搜索关键字,还是搜索ISBN,在数据请求发出的同时,在搜索结果标签中显示“正在搜索亚马逊书店…”。不过,一旦获得数据事件被触发,这条信息将被来自亚马逊的数据覆盖。

扫描条码

现实情况是:在手机上输入字符通常不是很方便,而且总会在不经意间出错。如果能够在应用中使用条码扫描功能,那么问题就会变得简单(并且几乎不会出错)。 这是安卓手机内置的另一项强大的功能,而使用App Inventor,可以轻而易举地实现它。

条码扫描器的开始扫描块用于启动安卓手机中的条码扫描应用,可以利用扫描按钮的点击事件来调用开始扫描块。一旦扫描条码的操作完成,将触发条码扫描器的完成扫描事件。该事件携带了一个参数——返回结果,其中保存了被扫描的条码中所包含的信息。在本例中,返回结果就是用于搜索的ISBN,如图13-10所示。

图13-10 用户扫描条码得到ISBN并启动搜索
块的作用

当用户点击扫描按钮后,调用条码扫描器组件的开始扫描块,来启动设备中的扫描器。扫描完成时触发完成扫描事件。该事件携带参数返回结果,在本例中为图书的ISBN。用搜索结果标签提示用户正在进行搜索,并在搜索结果标签中显示返回结果——扫描获得的ISBN,最后调用网络数据库组件的请求数据功能来启动搜索。仍然使用之前定义的网络数据库组件的获得数据事件处理程序来处理返回图书信息。

测试:点击扫描按钮并扫描书上的条码。应用中是否显示了该图书的信息?

改进信息的呈现方式

我们所创建的这类客户端应用,可以按需要来处理收到的数据,可以与其他网上商店进行价格比较,也可以用书名信息来搜索其他图书馆中的同类书籍。通常的做法是,先将来自网络的信息保存到变量中,然后再做进一步的处理。现在我们只是在网络数据库的获得数据事件处理程序中,将亚马逊API返回的信息直接显示在搜索结果标签中。接下来,我们要对数据做如下处理:(1)将返回数据中的每本书的书名、售价以及ISBN分别保存到单独的变量中;(2)以一种有序的方式显示这些数据项。如果你已经完成了前面几章的学习,相信你已熟知如何定义列表变量,以及如何利用它们来显示信息。现在,根据自己的需要来设置全局变量,并将结果逐行地显示出来,然后将你的代码与图13-11进行对照。

图13-11 提取每本书的书名、售价及ISBN,并逐行显示它们
块的作用

这里定义了四个变量:图书列表、书名、售价及ISBN,用来保存亚马逊API返回的每一项数据。从API返回的数据保存在获得数据事件的参数“数据”中,这里将它另存为图书列表。其实程序可以直接使用事件中的“数据”,但通常会将它另存为一个变量,以便在该事件处理程序之外也可以使用这一数据。(事件中携带的像“数据”这样的参数仅在事件处理程序内有效,事件处理程序之外无法访问该参数值。)

使用遍历列表循环来处理返回结果中的每一项数据。回想一下,从亚马逊返回的数据是一个列表的列表,每个子列表代表一本书的信息,因此遍历列表循环块中的占位符变量“项”被命名为“图书”,它保存了当前正在被处理的图书的信息(子列表)。

现在我们要清醒地面对占位符变量“图书”,它是一个列表,其中第一项是书名,第二项是售价,第三项是ISBN。因此,选择列表项块利用索引值将这些数据逐项提取出来,并保存在事先定义的变量中(书名、售价及ISBN)。

好,现在数据都已经被分解到变量中,可以随意处置它们。这里利用拼字串块将变量的值显示出来,即让书名、售价及ISBN各占一行。

图13-11中的代码还可以有另一种更为优化的写法,即利用局部变量代替书名、售价及ISBN三个全局变量,从而减少应用对内存的占用,同时提高代码的安全性。如图13-12所示。理解局部变量的关键是要搞清楚它的有效作用范围。全局变量直接在编程视图中定义,单独占一行,不属于任何一组代码块,而所有的块都可以读取或改写全局变量;局部变量则定义在某个独立的程序块内部(如事件处理程序或自定义过程),局部变量中包含的代码块,可以读取或改写局部变量,而局部变量之外的任何代码都无法访问到它。

图13-12 用局部变量替代全局变量
测试:尝试搜索其他书,看看返回的信息是如何显示的。应该类似于图13-13。

图13-13 用更有条理的方式显示搜索结果

完整的“亚马逊掌上书店”应用

图13-14中显示了“亚马逊掌上书店”应用中全部代码的最终版本。

图13-14 完整的“亚马逊掌上书店”应用

定制化API

我们所连接的API(http://aiamazonapi.17coding.net )是由Python语言编写,并部署在阿里云应用平台上,开发者也可以在这个平台上创建并发布自己的应用及服务(API),这可能需要一些费用。(原书中的内容是:亚马逊API部署在谷歌云平台上,只有当网站或服务广为人知,吸引了大量点击时,才需要向谷歌的服务支付付费。——译者注)

这里使用的API只能访问全部亚马逊API中有限的部分:每次搜索最多只能查询到五本书。如果想提供更多灵活的访问,例如不仅限于搜索图书,你可以从http://appinventorapi.com/amazon/ 下载源代码,并按照自己的需要来修改它。修改源代码确实需要有Python编程的知识,所以要小心!但是如果你已经通过本书完成了App Inventor的学习,那么是该考虑迎接新的挑战了。想要学习Python,可以阅读交互版的图书《如何像计算机科学一样思考:学会使用Python》,并查看本书第24章的“创建App Inventor API”部分。

改进

一旦应用运行起来,你可能会尝试以下改进。

  • 如果用户的搜索没有收到任何返回的数据(比如当用户输入了一个无效的ISBN时),这时程序不会有任何的反馈。修改或添加代码,当搜索没有结果时,向用户提供反馈。
  • 修改程序,只查找低于10美元的图书。
  • 修改程序,当扫描一本书后,用声音来报告亚马逊的最低售价(使用第7章中用过的语音合成器组件)。
  • 下载http://aiamazonapi.appspot.com (原书中提供的这个网址已经失效。——译者注)API源码,并修改源码,使其能够返回更多的信息。例如,返回每本书在亚马逊网上书店的网址,并与该书的其他信息一同显示;当用户点击该网址时,可以打开对应的网页。正如前面提到的,修改API需要用Python语言和谷歌云应用平台的一些知识。更多信息请参见第24章。

小结

以下是本章涵盖的内容。

  • 使用网络数据库组件以及专用的API,在应用中可以实现对互联网信息的访问。将网络数据库组件的服务地址属性设置为API的地址(URL),使用网络数据库组件的请求数据块,向API发出数据请求。请求的数据不会立即返回,可以在网络数据库的获得数据事件中处理返回的数据。
  • 条码扫描器组件的开始扫描块可以启动安卓设备的扫描功能。当用户完成条码扫描时,将触发条码扫描器的完成扫描事件,并将扫描获得的数据保存在参数“数据”中。
  • 在App Inventor中,用列表,或列表的列表,来容纳复杂的数据。如果预先知道从API返回的数据格式,就可以使用遍历列表循环以及选择列表项的功能,来提取列表中的每一项数据,并将其保存到变量中,这样就能按照所需要的方式,对变量进行处理或显示。

注释

亚马逊网上书店:Amazon.com,美国规模最大的网上书店。本应用中的信息来自亚马逊中国,网址是http://www.amazon.cn/ 。

ISBN:International Standard Book Number的缩写,国际标准书号,是国际通用的图书或独立的出版物(定期出版的期刊除外)的代码。

中文版亚马逊API:新浪微博的@roadlabs实现了与网络数据库组件兼容的中文版亚马逊API,原书中英文版的API目前已经失效。