第五章 天气预报——图片版

第一节 功能描述

本章将在第四章的基础上,增加如下功能:

  1. 设置默认城市,如果已经设置了该项,应用启动时将在输入框中自动填写地名;
  2. 设置默认信息,如果已经设置了该项,应用启动后自动设置列表选择框的选中项;
  3. 保存近期数据,以便在无法联网时查看近期天气信息,一旦保存过天气信息,系统启动后,将自动加载并显示此信息;
  4. 在七日预报及天气实况信息中,用天气图标提示天气状况:
    • 天气实况:显示当前时间段的天气信息,带有一个图标;
    • 七日预报:每日有两个图标,分别提示白天与夜间的天气概况。

注:第四章的功能设计中包含显示当前系统日期及时间,这里取消该项设计。

第二节 用户界面设计

一、页面布局

与前几章的应用相比,图片版的天气预报要呈现更为复杂多样的信息,包括文字及图片信息,而且图文混杂在一起,这为用户界面的设计增加了难度。为了合理地使用用户界面组件,在正式创建用户界面之前,我们需要用简单的框图画出每一类信息的呈现方式,并给出所需组件的数量及摆放位置。具体的设计如表5-1所示。

表5-1 不同类型信息的呈现方式及所需组件

从表5-1中可以看到,需要组件种类及数量最多的是七日预报(5个标签、2个图片)。我们希望设置一组组件,使得它们能够显示不同类型的信息,为此我们需要取各类信息所需组件的最大集,即,我们需要用七日预报组件来呈现所有类型的信息。在程序运行过程中,通过设置组件的可视属性,来显示或隐藏部分组件,进而完成信息的呈现。从表5-1中还可以看到,有2项信息需要重复显示,其中小时预报的重复次数不确定,为1~8次,而七日预报的重复次数是固定的,为7次。对于这类需要重复显示的信息,我们的策略是每次只显示一条,通过用户的操作(点击按钮)来实现对每一条信息的浏览。

七日预报组件需要用垂直及水平布局组件的组合来实现组件的定位。具体设计如图5- 1所示。

图5- 1 七日预报组件的布局设置

整个屏幕的组件布局如图5- 2所示,图中黄色方框为水平布局组件,蓝色方框为垂直布局组件,这些布局组件不仅可以实现组件的定位,在需要的时候,还可以一次性地显示或隐藏某些组件。

图5- 2 页面布局

二、添加并设置组件

项目在设计视图中的样子如图5- 3所示。为了提示标签的位置及功能,暂时保留标签显示文本内容,待开发完成后,统一将标签的显示文本属性设为空。

图5- 3 设计视图中的图片版天气预报

组件清单及组件名称设置见表5- 2。

表5- 2组件清单及属性设置(按照自上而下、由外而内的顺序排列)

以上组件的属性设置如下:

  1. Screen1:水平对齐属性设为居中,标题设为“天气预报_图片版”;
  2. 垂直布局1:宽度设为98%(内容与屏幕边界之间留空),高度设为充满;
  3. 垂直布局2、垂直布局3:水平对齐设为居中;
  4. 除垂直布局1之外的所有布局组件:宽度设为充满;
  5. 地名输入框:提示属性为“请输入地名”,宽度为充满;
  6. 信息选择框:显示文本设为“选择信息种类”,标题设为“选择显示分类信息”,宽度设为充满;
  7. 所有按钮的显示文本:参见图5- 3;
  8. 左移按钮、右移按钮:宽度设为充满,勾选粗体属性;
  9. 日期时间标签:背景颜色设为黑色,文本颜色设为白色,宽度为充满,勾选粗体属性;
  10. 左标签、右标签:宽度为充满
  11. 所有标签的显示文本属性设为空。

第三节 编写程序——请求并整理数据

一、请求数据

仍然沿用第四章中的请求数据方法,网址为:

    https://api.heweather.com/x3/weather?city=城市名称&key=密钥

其中的城市名称及密钥需要在请求数据时,替换成具体的值,例如城市名称为“北京”;密钥需要开发者到和风天气网申请,网址为 http://www.heweather.com 。 关于网址中各段信息的含义,请参见第四章第二节。

将App Inventor的开发环境从设计视图切换到编程视图,首先创建两个用作常量的全局变量——网址片断1及网址片断2,并在查询按钮的点击事件处理程序中,调用Web客户端组件的请求数据过程。当应用接收到返回的数据时,用日期时间标签来显示收到的信息,如图5- 4所示,

图5- 4 请求数据相关代码

测试结果如图5- 5所示。

图5- 5 用标签显示请求数据的结果

又见到了熟悉的大括号、中括号以及这些看似无序实际严整的数据。为了让我们在开发过程中,始终对数据的结构有清醒的认识,本节在第四章的基础上,对数据给出更为形象的的描述,为代码的编写做好充分的准备。

二、数据整理

(1)提取有效数据 在第四章的第四节中,我们对天气预报数据进行了分析,并用表格的方式呈现了数据的结构(完整的数据请参见第四章结尾的附表),这些数据原本是JSON格式的,经过Web客户端组件的解析,转化为一个10级的列表,需要强调的是,第4级列表是一个分水岭,其中包含了7个5级列表,其中除了一个状态列表(键为status)之外,另外六个列表就是我们将要呈现的不同种类的信息。10级列表的整体结构如图5- 6所示。

图5- 6天气预报数据的整体结构

还记得在第四章中,我们将列表划分为四类:

  1. 值为文本的键值对:图中的灰色部分(aqi中的第9级、倒数第二个第5级);
  2. 值为列表的键值对:图中的绿色部分(第2、5、7级);
  3. 单项列表:列表中只有一个列表项,图中的红色部分(第1、3、6级);
  4. 多项列表:列表中包含多个列表项,且每个列表项本身也是列表,如果其中的列表项为键值对,则称该列表为键值列表,见图中的蓝色部分(第4级、aqi数据中的第8级以及其它数据中的第6级,状态数据除外)。

分类的目的是为了获得有效的数据,并将它们以合理的方式呈现出来。图中用钥匙图标 来表示键值对中的键(key),用美元符号 来表示键值对中的值(value),以便能清楚地分辨出列表的类型。从图中可以看出,真正有效的数据保存在6级列表中。

我们要做的第一件事情就是将第6级列表解析出来,单独保存在一个列表变量中,以便根据用户的选择来读取并显示正确的信息。需要注意的是,4级列表中的第6项为Web API的数据返回状态,如果数据请求成功,将返回“OK”,我们暂时不需要显示这项数据,因此分类列表中不包含此项。代码如图5- 7所示。

图5- 7 提取有效数据

在上述代码中,我们用标签显示了分类数据列表中的第一项——空气质量指数信息,测试结果如图5- 8所示。

图5- 8 测试:提取有效数据

(2)了解键的含义

在我们提取到的数据中,有效的信息是以键值对的方式组织起来的,例如,在空气质量数据中,pm2.5的浓度表示为(pm25 8),第一项pm25为键,第二项8为值。所有键值对中的键都是一个英文单词的简写或缩写,这样做是为了编写程序的方便,但不适合人类用户的阅读,因此,我们必须了解这些键的含义,才能正确地呈现相关的信息。在和风天气网的API文档中(http://heweather.com/documents/api ),提供了每个键的中文含义,这里我们用表格的方式列出这些键以及它们对应的中文,以供下一步使用。如表5-3 所示。

表5-3-1 键值对中中键的含义

表5- 3 -2 键值对中中键的含义

(3)为列表选择框组件设置数据源 我们需要创建一个与分类数据列表相对应的的中文字串列表,作为列表选择框组件的数据来源,当屏幕初始化时,将分类信息选择框的列表属性设置为该字串列表,如图5- 9所示。

图5- 9信息分类名称列表

注意:分类数据名称列表中各个列表项的顺序必须与分类数据列表的顺序一一对应。

第四节 呈现有图标的数据——七日天气预报

一、设置用户界面组件的可视属性

在本应用中,通过设置某些组件的允许显示属性来实现组件的复用,即,用同一套组件显示所有类别的信息。允许显示属性值为假的用户界面组件在屏幕上不占据位置(宽度及高度均为0),当我们需要用左标签来显示没有插图的文字信息时(空气质量指数、城市基本信息及生活指数),需要将图片布局组件以及右标签组件的允许显示属性设为假,此时左标签将在水平方向上充满水平布局3。

考虑到共有四种信息呈现方式,我们创建一个“显示有用组件”过程,来具体设置相关组件的允许显示属性,如图5- 10所示。

图5- 10 设置用户界面组件的允许显示属性

这个过程暂时看起来没有什么用处,因为用户界面组件允许显示属性的默认值原本都是真,因此这里的设定显得有些多余,但是到了后面,当用户从其他信息跳转到七日预报时,上述过程就是必须的了。

二、图标文件的获取

如表5- 3-2所示,在七日预报专用键中,包含两个特殊的键——code_d、code_n,它们分别为白天天气代码及夜间天气代码。这些代码用数字来表示,但却不仅仅是数字,它们是某些图片文件的文件名。还记得在第四章中,我们请求过一个图片 ,使用的网址是:

http://files.heweather.com/cond_icon/100.png

其中文件名100.png中的100就是一个天气代码。关于天气代码所对应的具体内容,可以查看和风天气网的API文档,地址为:

http://heweather.com/documents/condition-code

在我们的应用中,为了显示带有图标的天气信息,需要使用这些天气代码,从和风天气网获取相应的图片,具体方法是设置图片组件的图片属性为URL网址,网址包含以下三个部分:

  1. 文件的位置:http://files.heweather.com/cond_icon/
  2. 文件名:就是天气代码中的数字;
  3. 文件扩展名:为“.png”。

我们在屏幕初始化事件中请求一个代码为101的图片,如图5- 11所示,测试结果如图5- 12所示。

图5- 11 从网络获取与天气代码对应的图片
图5- 12 代码101对应的图标

三、图标的呈现

七日预报信息在我们已经提取的分类数据列表中位列第三,我们先来熟悉一下该项信息的数据结构。如图5- 13所示。

图5- 13 七日天气预报信息的数据结构

图标信息包含在每日信息——第7级列表的第2项中(第9级列表)。第7、9级列表均为键值列表。App Inventor提供了一个键值列表查询块:

该块的第一个输入项为键值列表,也就是图中第7、9级列表,第二个输入项为“键”,就是图中钥匙图标后面的文字(cond、code_d、code_n等),返回的结果是“值”,键“cond”的返回值是第9级列表,键“code_d”的返回值是一个字串(如100),就是我们要提取的天气代码信息。我们创建一个有返回值的提取天气代码过程,通过两次调用键值列表查询块,来获取最终的天气代码。程序代码如图5- 14所示。

图5- 14 获取白天或夜间的天气代码

上述过程中参数的含义:

  1. 日信息:是原始数据中的第7级列表,共有7个这样的列表;
  2. 键:取值为“code_d”或“code_n”,分别用于提取白天天气代码及夜间天气代码。

利用上述过程返回的天气代码,可以获取七日中任意一天的白天及夜间天气代码,并以此来设置图片组件的图片属性。我们首先获得第1天的天气代码,程序代码如图5- 15所示。

图5- 15 显示七日预报中第1天的天气图标

在图5- 15中,我们声明了一个名为索引值的全局变量,该变量只对七日预报及小时预报有用。由于这两项信息中都包含了多项结构相同的信息,因此,当用户查看这两类信息时,默认只显示第1项,用户通过点击左移或右移按钮,来查看邻近项信息。这两个按钮的点击事件处理程序如图5- 16所示。

图5- 16 左右移动按钮的点击事件处理程序

在上述代码中,我们利用屏幕的标题属性来显示当前的索引值,以便于对程序的测试。为了测试上述代码,我们需要在Web客户端组件的收到文本事件中,调用显示天气图标过程,来显示第一天的图标,然后再点击左右移动按钮,来查看后面几天的图标信息,代码如图5- 17所示,测试结果如图5- 18所示。

图5- 17 当web客户端收到文本时,显示天气图标
图5- 18 显示天气图标的测试效果

四、显示文字信息

在第四章中,我们创建了一个可以递归调用的过程——显示数据列表(图4-23),通过判断列表项是否为列表,来决定对数据的操作。本章我们想尝试一种不同的方法,这个方法不见得比此前的方法好,不过可以提供另一种呈现信息的方式。

在图5- 13中,我们观察数据的结构,在第7级列表中,有三种颜色的数据:绿、蓝、灰,分别对应于值为列表的键值对、多项列表(键值列表)以及值为文本的键值对,我们要展示的信息在灰色的区域中。灰色区域有等级的差别,有些数据直接隶属于第7级列表,如日期、湿度、降水量等,另一些数据则隶属于第9级列表。这两类数据虽然处于不同的等级,但访问方式有相似之处。由于要显示的信息量并不算太多,因此我们采用“点名”的方式来访问这些数据,即,使用键值列表查询块,用“键”来查询“值”。举例说明,如果要访问白天天气代码,则需要两次使用键值列表查询块,如图5- 19所示:

图5- 19 查询第9级列表中键为code_d的值

在图5- 19中,我们要呈现的信息就保存在局部变量日代码中。

使用这个查询键值列表的块,我们可以很方便地提取到任何需要的信息,下面我们将全面地呈现七日天气预报信息。由于这段程序的代码比较多,因此将代码拆解成几个功能简单的过程,最后再将它们合并成一个过程——显示七日文字信息,如图5- 20~图5- 24所示。

图5- 20 求日出日落时间
图5- 21 求白天、夜间的天气状况及温度
图5- 22 求湿度、降水、气压等天气信息
图5- 23 求风势信息
图5- 24 显示文字信息过程

最后,在Web客户端组件的收到文本事件中调用显示七日文字信息过程,如图5-25所示。

图5- 25 显示七日天气预报

测试结果如图5- 26所示。

图5- 26 显示七日预报图文信息测试

目前显示的文字信息只是首日信息,我们需要改在左右移动按钮的点击事件处理程序,实现对每日信息的浏览,代码如图5- 27所示。

图5- 27 浏览七日天气预报

由于七日文字信息中包含了日期信息,当点击左右移动按钮时,日期标签的文字会随之变化,因此原本用屏幕标题测试索引值的代码就可以删掉了。代码的测试结果如图5- 28所示。

图5- 28 测试浏览七日预报

第五节 显示其他种类信息

一、显示城市基本信息

如图5- 29所示,这是一组极其简单的数据,其中只有文字信息,我们将用标签来显示所有信息。

图5- 29城市基本信息的数据结构

城市基本信息中的update键对应的是数据更新时间,在显示天气实况及生活指数等其它信息时,需要用到该项信息,因此这里创建一个过程——数据更新时间,过程的返回值为数据更新的本地时间,代码如图5- 30所示。

图5- 30 返回值为数据更新时间的过程

下面创建显示城市信息过程,这里需要注意的是,数据更新时间需要显示在日期时间标签中,其余信息显示在左标签中。代码如图5- 31所示.

图5- 31 显示城市信息过程

在web客户端的收到文本事件中调用显示城市信息过程,代码如图5- 32所示,测试结果如图5- 33所示。

图5- 32调用显示城市信息过程
图5- 33 显示城市信息过程的测试结果

另外两个只包含文字内容的单条信息是“空气质量指数”及“生活指数”,我们先来处理这两项信息,最后再来处理小时预报及天气实况信息。

二、显示空气质量指数

如图5-34所示,空气质量指数的数据结构非常简单,可以说是几项信息中结构最简单的,不过请注意,此前的两项数据,第6级列表为蓝色,即,为多项列表,如图5-6所示,而这里的第6级列表为红色,即单项列表,而我们所需要的数据保存在第8级列表中。

图5- 34 空气质量指数的数据结构

我们有两种方式访问第8级列表,代码如图5- 35所示。

图5- 35 访问第8级列表

此外,空气质量指数信息中还缺少一项数据,即:数据更新时间,如上一个标题所述,该项信息来自于城市基本信息。下面创建显示空气质量指数过程,与城市基本信息相同,用日期时间标签显示数据更新时间,用左标签显示具体内容,代码如图5- 36所示。

图5- 36 显示空气质量指数过程

在web客户端的收到文本事件中调用显示空气质量指数过程,代码如图5- 37所示,测试结果如图5- 38所示。

图5- 37 调用显示空气质量指数过程
图5- 38显示空气质量指数过程的测试结果

三、显示生活指数

生活指数信息的数据结构有些特别之处,如图5- 39所示,第6级列表中有7个键值对,是7项生活指数,其中的值(第8级列表)包含两项要显示的信息——简介及详情,这7项指数信息从数据结构上看,是完全相同的,因此它们的显示方式也完全相同。

图5- 39生活指数信息的数据结构

首先我们创建一个拼生活指数字串过程,如图5- 40所示。该过程包含两个参数:标题及指数,标题是7项指数的文字提示,指数是包含两项数据(简介及详情)的列表。

图5- 40 拼字串过程

然后创建显示生活指数过程,如图5- 41所示。该过程的第一行代码设置屏幕的允许滚动属性为真,这是因为生活指数信息的内容较多,超出了单个手机屏幕的范围,允许滚动设置可以让用户上下滚动屏幕,以便查看完整的信息。当然,在显示其他数据时,还须将允许滚动属性重新设为假。

图5- 41显示生活指数过程

在web客户端的收到文本事件中调用显示生活指数过程,代码如图5- 42所示,测试结果如图5- 43所示。

图5- 42调用显示生活指数过程
图5- 43显示生活指数的测试结果

在上述三个标题中,虽然显示的都是简单的文字信息,但由于数据的结构各不相同,因此每项信息的显示都需要单独创建过程,这就使得代码的复用性极低,这也是内容类应用的特点之一。

四、显示天气实况信息

天气实况信息的数据结构与七日天气预报信息有相似之处,如图5- 44所示。

图5- 44 天气实况信息的数据结构

天气实况信息几乎相当于七日预报的首日信息,增加了体感温度信息,减少了一组图文信息,为此要隐去右垂直布局组件。首先创建一个实况温湿度过程,将天气实况信息中与温湿度等信息相关的内容拼成字串,代码如图5- 45所示

图5- 45 求天气实况过程

然后创建显示天气实况过程,利用白天图片及白天标签组件来显示天气状况信息,代码如图5- 46所示。

图5- 46 显示天气实况过程

最后在Web客户端的收到文本事件中,调用显示天气实况过程,如图5- 48所示,测试结果如图5- 48所示(有时实况信息中会缺少某些数据,此时将它们显示为下划线“_”)。

图5- 47 调用显示天气实况过程
图5- 48 测试结果

五、显示小时天气预报

如图5- 49所示,小时天气预报信息的数据结构相对简单,不包含天气代码信息,也就不需要显示图像,仅显示时间、温湿度及风势信息。与七日预报相同的是,小时预报中包含多项结构相同的数据,不同的是,七日预报中包含7项结构相同的数据,7这个数字是固定的,而小时预报中这个数字是变动的,最小可能是1,最大是8,因此在程序的编写上要格外小心,与此相关的代码主要体现在左右移动按钮的点击事件处理程序中。

图5- 49 小时天气预报信息的数据结构

针对小时预报数据项数不确定的情况,我们创建一个有返回值的过程小时预报项数,一旦应用从WebAPI收到返回的数据,就可以调用该过程,求出项数的值,代码如图5- 50所示。

图5- 50求小时预报中的项数

如图5- 49所示,小时预报中的温湿度信息,与七日预报及天气实况中的温湿度信息略有差别,没有降雨量及体感温度等,因此针对小时预报,我们也必须创建一个小时温湿度过程,该过程的返回值将显示在左标签中,具体代码如图5- 51所示。

图5- 51 返回小时温湿度字串的过程

此外,在小时预报的风势信息中,缺少能见度一项,为了提高代码的复用性,我们需要改进风势过程,让该过程也适用于小时预报,代码如图5- 52所示。

图5- 52改进之后的风势过程

有了温湿度及风势信息,下面创建显示小时预报过程,将小时信息显示出来,代码如图5- 53所示。

图5- 53显示小是预报过程

在Web客户端的收到文本事件中,调用显示小时预报过程,来显示第一个时段的天气预报,如图5- 54所示,测试结果如图5- 55所示。

图5- 54显示第一个时段的小时天气预报
图5- 55 测试结果

最后,我们需要修改左右移动按钮的点击事件处理程序,为此我们临时设置一个全局变量当前信息,来记录用户当前正在查看的信息种类。全部应用开发完成后,这个变量的值可以从分类信息选择框的选中项属性中获得,届时将删除此临时变量。

图5- 56改进后的左移按钮点击程序
图5- 57改进后的右移按钮点击程序
图5- 58测试结果

到目前为止,我们已经实现了所有信息的显示功能,在测试过程中,你可能已经发现了程序中存在的问题。首先,程序中存在错误:当我们在地名输入框中输入北京、上海这样的大城市时,测试结果没有问题,但当输入像鞍山、永嘉这样的县市地名时,程序就会出错。这个错误的原因来自于数据,稍后我们会加以分析和改进。其次,代码的复用性极低:针对三类不同的天气信息(七日、小时及实况),温湿度信息需要用三个雷同的过程来获取,这样的代码是否可以通过技术手段,合并为一个过程,我们也会在后面给予讨论。

下面的任务是针对分类信息选择框编程,当用户选择不同种类的信息时,应用将呈现相应的信息;另外要实现默认地名、默认信息种类的设定,以及保存数据功能。

第六节 其他功能

一、选择显示分类信息

在前两节中,我们在Web客户端组件的收到文本事件中调用显示各类信息的过程,来完成对程序的测试,现在可以让用户来决定要显示哪类信息了。我们定义一个显示选中信息过程,根据分类信息选择框的选择结果来决定要显示的信息,代码如图5- 59所示。

图5- 59 根据用户的选择来显示分类信息

关于上述代码,有几点需要说明:

  1. 全局变量索引值的设定:该项设定也可以分别放在第3及第4个分支中,因为只有这两类信息与索引值有关,不过考虑到减少代码量,因此将其置于首行;
  2. 设置分类信息选择框的显示文本属性为该组件的选中项属性:如果不进行这项设置,选择框的显示文本将是“请选择信息种类”(在设计视图中设定的值);由于手机屏幕的空间有限,我们没有单独设置一个标签组件,来显示当前信息的种类(相当于标题),而是试图用选择框的显示文本属性来代为实现提示信息种类的功能;
  3. 全局变量当前信息的设定:这两行代码在应用的最终版本中将被删除,这里只是为了测试之用,我们将在下一节代码优化时完成这项修改。

然后,在分类信息选择框的完成选择事件中,调用上述过程。除此之外,当用户点击查询按钮向WebAPI发出请求并收到返回的信息时,我们希望应用能够显示其中的一项信息,因此在Web客户端收到文本事件中,也将调用显示选中信息过程,代码如图5- 60所示。此时,你不妨思考一下,假如用户尚未设定默认信息种类,当应用收到WebAPI返回的数据后,会显示哪一类信息。测试结果如图5- 61所示。

图5- 60 在两处调用显示选中信息过程
图5- 61测试结果

二、默认设置与数据保存

依照我们对应用的功能设定,利用本地数据库组件,来保存两项默认信息:默认城市及默认信息种类;也可以将当前信息保存到手机中,以便没有网络时,也能查看近期的天气信息,代码如图5- 62所示。

图5- 62设置默认项及数据的保存
在应用启动时,可以读取本地数据库中已保存的信息,并利用这些信息,设置相关组件的属性,如果数据库中保存过天气信息,则显示其中的默认项,代码如 图5- 63所示。
图5- 63应用启动时从本地数据库中读出已保存信息

经过测试,程序运行正常。

第七节 纠错及代码优化

一、纠错

在第五节的末尾我们提到过程序中存在的错误,为了重现这些错误,我们修改少量代码,来凸显出关键性错误。如图5- 64所示,我们希望在收到网络数据之后,立即显示空气质量指数,通过禁用显示数据更新时间的语句,来排除非关键因素。

图5- 64修改少量代码以排除错误中的非关键因素

在进行测试时,我们在地名输入框中输入“永嘉”,然后点击查询按钮,此时手机屏幕及开发环境的编程视图内,都会显示同样的错误提示,如图5- 65所示。

图5- 65 试图查询永嘉的空气质量指数时,应用出现错误

错误提示的内容:查询键值列表的操作不能接受“aqi”、“永嘉”以及“空”这样的参数。如图5- 36所示,在显示空气质量指数过程里,与查询键值列表有关的语句共有8条,很容易判断出错的一定是第一条,如图5- 66所示。其中参数aqi、空是固定的值,是不会错的,问题应该出在第一个参数——局部变量空气质量上,而这个局部变量来自于分类数据列表的第1项,那么我们就来看看此时分类数据列表的第1项究竟是什么。

图5- 66 初步判断产生错误的代码

我们用日期时间标签来显示分类数据列表中的第1项,代码如图5- 67所示,测试结果如图所示。

图5- 67 显示数据分类列表中的第1项
图5- 68分类数据列表中第1项的内容

为什么会这样,我们本应该得到空气质量指数信息,但这里却显示的是城市基本信息,那么空气质量信息哪里去了呢?经过仔细察看WebAPI的返回数据,发现并不是所有城市的天气信息中都包含空气质量信息,像鞍山、永嘉这样中小规模的县市天气信息中,就不包含空气质量信息。因此,在返回的数据中,第1项数据是我们想象中的第2项数据——城市基本信息。

类似这样的问题,在软件开发过程中经常会遇到的问题,因为我们是针对一个数据模型编程,数据模型是我们对现实世界的一种简化和抽象,而实际数据有时可能会偏离这个模型,因此导致程序的错误。此处我们之所以保留一定的篇幅,来解释这个错误产生的原因,也是希望提醒读者,程序编写过程中,错误在所难免,遇到错误不必气馁,仔细分析终会找到原因,并因此而积累更多的开发经验。

下面我们来解决这个问题。问题的根源在于分类数据列表,修改要从提取有效数据过程着手,修改过的代码如图5- 69所示。

图5- 69 修改提取有效数据过程

注意:分类数据列表中列表项的顺序变了!我们重新编排了信息的顺序,主要是考虑到用户对不同种类信息的关注程度,同时也考虑到空气质量指数的问题。这里将空气质量指数信息放在列表的末尾,无论查询地点的信息中是否包含该信息,都不会改变其他数据在列表中的位置,也就不会影响对这些数据的查询。这项改变对于整个程序的稳定性是一种致命的破坏,因为分类数据列表是整个程序的基础,所有显示数据的操作都要依赖于它,因此我们必须小心谨慎地查看所有已经写好的过程以及事件处理程序,找出那些与分类数据列表操作相关的代码,并逐一加以修改,否则,这些代码就会在某个时刻跳出来,给你点颜色看看!

首先是图5- 9的分类数据名称列表,它的作用仅仅是作为分类信息选择框的数据来源,其中列表项的顺序需要调整到与分类数据列表的顺序完全相同。修改过的列表如图5- 71所示。

图5- 70 更新后的分类数据名称列表

接下来,我们逐一查看所有代码,并修改那些于分类数据列表的查询有关的代码。首先查看那些名字以“显示”开头的过程,首当其冲的是显示空气质量指数过程,修改过的代码如图5- 71所示,原来的代码参见图5- 36。

图5- 71修改之后的显示空气质量指数过程

另外两个需要修改的是显示生活指数及显示城市信息过程。以上三个“显示过程”的共同之处在于它们都是与天气无关的信息,而与天气有关的“显示过程”由于使用的参数,因此避免了对数据源的依赖。从中我们也可以得出这样的经验:把数据本身与对数据的操作隔离开来,可以降低代码之间的耦合性,并因此提高程序的坚固性。修改过的代码如图5- 72所示。你也可以进一步修改这三个过程,为其添加一个参数,来隔离数据与数据处理。

图5- 72 对“显示过程”的修改
另外还需要修改两个有返回值的过程,与分类数据列表的顺序有关,如图5- 73所示。
图5- 73 修改两个有返回值的过程

与分类数据列表顺序相关度最高的是显示选中信息过程,原来的代码参见图5- 59,修改后的代码如图5- 74所示。这里我们顺便删除了对临时变量当前信息的设置,稍候要在左右按钮的点击事件中做出相应的修改。

图5- 74修改最多的过程

下面来修改左右移动按钮的点击事件处理程序,代码如图5- 75所示。

图5- 75修改左右移动按钮点击事件的处理程序

最后将临时变量当前信息删除,完成对代码的修改。测试结果如图5- 76所示。

图5- 76 纠错之后的测试结果

二、关于代码改进的讨论

前面提到过,在三类天气信息(七日、小时及实况)中,温湿度等信息有共同点,也有些微小的差别,这三类信息的提取使用了三个形式上雷同的过程(七日湿度、小时温湿度及实况温湿度)。能否通过添加参数及条件判断语句,将这三个过程合并为一个过程呢?

我们对三类信息的“键”进行了对比,如表5- 4所示,三类信息都包含湿度及气压(红星),只有天气实况信息中包含体感温度(灰星),其它键在各类信息中分别出现两次(橙星)

表5- 4 比较三类天气信息的“键”

我们尝试来写一个通用过程,来拼凑三类天气信息的温湿度字串。由于这三项信息都由左标签来显示,因此将过程命名为拼左标签字串,代码如图5- 77所示。

图5- 77用一个过程替代三个过程

然后分别修改三个显示左标签的过程,代码如图5- 78所示。

图5- 78调用拼左标签字串过程

这样的改进从表面上看代码量减少了,但代码的稳定性及易读性变差了。在拼左标签字串过程里,使用了四个条件分支语句,加之每行信息中包含标题、数据、单位及回车(\n)四个部分,稍有疏忽,就会出现差错。

关于“天气预报”这个应用,我们用两章的篇幅讲解了两种不同的开发思路,它们代表了应用开发(软件编写)的两种极端方法:

  1. 极少的界面组件 + 少量睿智的编码 —— 逻辑复杂且复用性高。
  2. 较多的界面组件 + 众多的硬编码 —— 逻辑简单但复用性低;

对于开发方法的选择,没有绝对的优劣之分,这要看应用的特点,或者说要看数据的结构。就图片版的天气预报而言,六组分类信息的数据结构各不相同,信息的种类也不相同(有些信息包含图片),如果一味追求代码的复用性,势必要添加大量的条件判断语句,让代码的逻辑变得复杂,从而也增加了出错的机会。

值得一提的是,这两章的代码中,涉及到大量列表操作,而且我们几乎遇到了所有可能类型的列表。虽然列表中数据的量并不大,但是数据的结构足够丰富,有心的读者不妨自己加以总结,以期获得更多的体会。

最后,就本章的程序而言,由两点可以总结:

  1. 在创建过程时,合理地使用参数,可以将数据与数据处理隔离开来,以降低代码之间的耦合度,从而提高代码的独立性;
  2. 在用标签显示文字信息时分两步走,即,先拼串,再显示。概括一点说,就是将数据的处理与数据的显示隔离开来,这样可以保证代码功能的单一性,同样也提高了代码的独立性。