第七章 幼儿加法启蒙

第一节 功能描述

这是一个寓教于乐的应用,在规定的时间内,由应用随机出题(1位数加法),配合对应的图形,以适应幼儿形象思维的特点。对回答结果进行声音及图像的提示,以增加对行为的反馈。下面具体说明应用的功能。

  1. 时间因素:
    • 设置五档练习时间,分别为1~5分钟;
    • 用进度条显示本次练习的剩余时间,每秒钟更新一次进度条;
    • 在练习开始前可以重新设置时长,一旦开始答题,将隐藏设置功能;
    • 当剩余时间为0时,练习结束;
  2. 空间因素:用户界面自上而下分为五个区域
    • 顶部:显示得分、判断对错、题目图示选择以及练习时长选择;
    • 第二行:剩余时间进度条;
    • 第三行:题目的数字显示(如:3+8=);
    • 第四行:题目的图形显示(两行彩色圆点,数量与两个加数相匹配);
    • 底部:12个按钮排列成3行,分别为0~9的数字键、确定按钮及清除按钮;数字键用于输入答案,确定按钮用于提交答案,清除按钮用于清除输入的答案。 . 情节:
    • 应用启动后,自动随机出题,题目有两种显示方式——数字方式及图形方式,用户可以选择隐藏图形;
    • 此时可以选择练习时长,选择完成后,重新出题,并重新开始计时;
    • 此时也可以选择回答问题,用按键的方式输入答案、提交答案或修改答案;
    • 一旦提交答案,将隐藏选择时长的下拉框,直到下一次练习开始;
    • 对于提交的答案,应用将判断对错,如果正确,则闪现红色对勾图片,并播放一段清脆的铃声;如果回答错误,则闪现红色叉号图片,并播放一段短促的噪声;
    • 判断对错之后,将自动生成并显示下一道题,如此循环往复,直到剩余时间为零时,弹出对话框;
    • 对话框中显示当前时长的最好成绩以及本次得分,并提供三个按钮供用户选择:
      • 清除记录:将所有时长的历史记录设为0,并重新开始练习;
      • 结束练习:退出应用;
      • 返回:重新开始练习;
  3. 记分规则:每道题回答正确加10分,答错不得分,也不减分;
  4. 历史记录:五种时长分别记录最好成绩,历史记录保存在手机中,用户可以将全部成绩清零。 应用运行时的外观如图7-1所示。
图7-1 应用的用户界面

第2节 素材准备

一、素材清单

图7-2中显示了应用中的全部素材文件,将文件上传到项目中,以备使用。

图7-2 应用中使用的素材文件

二、素材规格

  • 图片:均为png格式
    • 0~9:宽48像素,高60像素;
    • +(plus):宽55像素,高60像素;
    • =(equal):宽48像素,高32像素;
    • 对号(right)、叉号(wrong):宽高均为40像素;
  • 声音:均为wav格式,时长不足1秒钟。

第3节 技术要点

一、用图片组件显示数字

顾名思义,图片组件可以用于显示图片,通过动态设置组件的“图片”属性,来改变组件的外观。应用中用素材中提供的图片来显示数字及数学符号,以改善应用的视觉效果;当不需要显示图片时,可以将组件的“图片”属性设为空字符,如图所示。

二、用画布组件绘制图形

画布组件可以用于绘制图形,包括画线、画圆等,通过设置线宽、坐标、半径等参数,来控制图形的位置及大小。应用中在画布上绘制一定数量的彩色圆,来匹配题目中的数字。

画布上还可以写字,字的位置也可以用坐标来控制。应用中在两行圆点之间写一个加号(+)。

三、用随机数随机合成颜色

在画布上可以采用不同的颜色进行绘图或写字,颜色可以用随机数来合成。三原色(红绿蓝)的取值范围为0~255,如果三个颜色的值均为0,则合成色为黑色,如果均为255,则合成色为白色,取中间值时,共可合成出16.7M(256×256×256)种颜色。

四、用计时器组件控制应用的节奏

计时器组件不仅可以用于控制整个练习的时长,也可以在每道题之间设置一定的时间间隔,来显示判断对错的图片。在本应用中使用了两个计时器,一个命名为时长计时器,另一个为快闪计时器。快闪计时器有两个用途,一是在屏幕初始化时设置1毫秒的时间延迟,以便在用户界面加载完成之后,在画布上绘制图形,此时计时间隔属性为1毫秒;另一个用途是控制对错图片的显示时间,此时计时间隔属性为100毫秒。

第4节 界面设计

一次性将全部组件添加到项目中。Screen1中添加一个垂直布局组件,所有可视组件均放置在该垂直布局组件中;垂直布局组件中放置了7个水平布局组件,分别用来容纳每一行中的可视组件。页面布局如图7-3所示。

图7-3 设计视图中的用户界面及组件列表

垂直布局组件的宽度设为98%,是为了让界面组件与屏幕边缘之间保留一定的空间,而非紧邻屏幕边缘。组件清单及属性设置见表7-1及表7-2。

表7-1 组件清单

表7-2_1 组件属性设置

表7-2_2 组件属性设置

对组件属性设置的解释:

  1. 尽可能保持各行组件之间的空隙均等,为此所有可视组件都放置在布局组件中,可以通过设置布局组件的宽、高,来控制各行组件之间的间距;
  2. 为了保持12个按钮组件具有相同的尺寸,设容纳它们的布局组件的宽、高相同,同时,设所有按钮的宽、高为充满,这样,所有按钮均分父容器的宽度,且高度相等;
  3. App Invenor中,有些组件的属性只能在设计视图中设置,如按钮组件的字体属性;有些属性既可以在设计视图中设置,也可以在编程视图中通过程序进行设置,如按钮组件的显示文本、字号等;还有些属性虽然可以用程序设置,但某些属性值无法设置,例如按钮组件的宽高属性,可以用程序将宽高属性设置为具体的像素值,却无法将属性值设为“充满”。因此,我们可以用循环语句批量地设置12个按钮的字号,但宽高的“充满”属性值只能在设计视图中逐一设置。
  4. 七个水平布局组件中,只有容纳画布的水平布局4的高度为充满,其他设置为固定值或自动,这样既可以保证最后一行按钮组件贴紧屏幕底部,又可以让画布与相邻的行之间保持合理的距离。
  5. 快闪计时器的计时间隔暂定为1毫秒,当屏幕初始化完成后,将改为100毫秒;
  6. 数字滑动条的启用滑块属性设为假(不勾选),以保证计时的客观性,否则,用户可以手动滑动滑块以增加或减少练习时间。

第5节 编写程序——应用初始化

在屏幕初始化程序中,要完成以下操作:

  1. 初始化按钮列表,并设置按钮的字号属性;
  2. 动态设置屏幕的标题属性
  3. 设置动态组件属性的初始值
  4. 出题并显示题目

一、按钮初始化

  1. 声明全局变量“数字按钮列表”,并设其初始值为空列表;
  2. 创建过程“初始化按钮列表”:为数字按钮列表添加列表项——10个数字按钮的组件对象(在每个组件的代码块抽屉中,最后一个块代表组件本身,称之为组件对象),利用列表循环,设置数字按钮的字号,然后再分别设置确定按钮及清除按钮的字号;
  3. 在屏幕初始化程序中调用该过程,代码如图7-4所示。
图7-4 在屏幕初始化时创建按钮列表,并设置所有按钮的字号属性

上面过程中设定的变量及组件的属性值,在整个应用运行过程中都保持不变,因此该过程仅供屏幕初始化程序调用。向这样仅供一个程序调用的过程,其存在的意义不是为了提高代码的复用性,而是为了优化程序的结构,提高代码的可读性。

二、动态设置屏幕的标题属性

应用中有五档练习时长,我们希望将时长能够显示在屏幕的标题栏中,以便用户随时查看。应用启动时,时长下拉框的默认选中项为1(1分钟时长),因此在标题栏中将显示“幼儿加法启蒙【1分钟】”字样,代码如图7-5所示。

图7-5 设置屏幕标题以显示练习时长

三、动态组件初始化

所谓“动态组件”,就是在应用运行过程中,其属性值会发生变化的组件。它们像舞台上的演员,通过改变属性值来表现不同的情节;相比之下,那些属性值保持不变的组件,则相当于布景和道具。创建一个过程“初始化动态组件”,将应用中可能发生变化的组件属性值一次性地设为初始值,并将图7-5中的设屏幕标题的代码也纳入其中,如图7-6所示。

图7-6 设置动态组件的初始状态

如果你阅读过前几章,就会知道,每个应用中都会创建这样一个初始化过程:每当游戏结束,用户选择重新开始游戏时,需要对全局变量及动态组件的属性值进行初始化设置。由于有了前面的经验,因此我们可以将这个过程提前写出来,以便后面调用。现在可以在屏幕初始化程序中调用该过程。此处调用该过程的意义并不大,因为大部分的属性值都在设计视图中完成了设置,只有屏幕标题、时长计时器的启用以及确定按钮的启用设置是有必要的。

特别说明一下,确定按钮在应用运行过程中,会频繁地改变启用状态:在出题之后,用户尚未输入数字之前,禁用确定按钮,以免用户误操作;当用户输入1个数字之后,将启用确定按钮,以便用户提交答案。

四、出题并显示题目

创建一个过程“出题”,随机生成两个1~9的整数,并将其显示为相应的图片,如图7-7所示。

图7-7 随机出题并显示题目

五、测试

测试结果如图7-8所示。

图7-8 阶段测试

接下来,我们将实现题目的另一种呈现方式——题目的图示。

第6节 编写程序——题目图示

这一步是要在画布上绘制两行彩色的圆,并在两行圆之间画一个加号(+)。对于初学加法的幼儿,如果不习惯于用数字进行思考,可以通过数数的方式求出结果。

一、画圆遇到的问题

画圆需要确定四个参数——圆心的x、y坐标、圆的半径以及画笔的颜色。我们先来做一个实验,在屏幕初始化程序中绘制一个半径为50的圆,圆心在(50,50),颜色为默认的黑色,代码及绘制结果如图7-9所示。

图7-9 屏幕初始化时在画布上画圆

我们再来做一个实验,在画布的触摸事件里画圆,测试结果如图7-10所示。

图7-10 触摸画布时画圆

为什么会是这样?问题在于画圆的时机。屏幕在初始化过程中,将创建应用中的全部组件,并为组件设置属性。由于应用中的垂直布局1的宽度设置采用了百分比的方式,因此,在进行屏幕初始化时,需要根据设备屏幕的实际大小,来计算组件的具体尺寸,而我们画圆的时机选择,可能恰好在计算完成之前,因此圆的位置和大小都出现了偏离(图7-9中仅显示了圆的局部)。我们试着将画布从布局组件中拖出,直接放在屏幕上,注意:这时画布宽度设为充满,再画圆时,就不再出现偏离;但是,如果此时将画布的宽度设为98%,则测试结果又会出现偏离。

如何才能保证在屏幕初始化完成之后,再来画圆呢?我们想到了快闪计时器,用它来延迟画圆的操作,代码如图7-11所示(注意,我们重新将画布拖到水平布局4中)。

图7-11 将画圆的操作延迟10毫秒

1毫秒对于人的感觉来说,完全察觉不到,但是对于每秒钟运算10亿次的CPU来说,这个时间足够长了,可以完成这点简单的运算(确定画布的尺寸)(很快你会发现这个说法有问题!)。

二、绘制数量相当的彩色圆

对于画圆有如下考虑:

  1. 圆的数量:根据出题过程中生成的随机数加数1及加数2,利用循环语句来绘制数量相当的彩色圆。
  2. 圆的颜色:为了避免这些圆过于呆板,利用随机数来合成颜色,随机设置每个圆的颜色;
  3. 圆的半径:每行最多有9个圆,假设手机屏幕宽度为320,它的98%为313,再去掉左右边距各10像素,剩余293,平均每个圆大约占有33像素;圆与圆之间要保留6个像素的距离,留给每个圆的大小为27,因此半径设为13;
  4. 圆心的x坐标:圆心之间的距离为33像素,如果第一个圆的x坐标为10+13(23),则第二个圆的x坐标是23+33…以此类推,得出公式x = 23 + (n-1) x 33,其中n为圆的序号;
  5. 圆心的y坐标:画布高120,去除上下10个像素的空白,剩余的100像素平均分成三份,每份33,因此第一行圆的y坐标暂定为23(10+13),第二行的y坐标暂定为97(110-13);

首先定义一个“随机颜色”过程,来产生随机颜色,代码如图7-12所示。

图7-12 生成随机颜色

然后再定义过程“图示题目”,依照上述分析,编写代码如图7-13所示。

图7-13 绘制两行数量相当的彩色圆

三、添加加号

画布具有写字功能,可以设置字符的x、y坐标、字符颜色以及字体大小。在设计视图中我们已经将字体设为46,颜色依然采用随机色,x坐标设为22,y坐标设为74,代码如图7-14所示。

图7-14 绘制加号

这里的x、y坐标没有任何计算依据,完全凭多次尝试获得。无法得知字号与像素之间的对应关系,也不能确定字符定位的中心点。不过,根据实验结果可以猜测,46号字的宽高大约为23像素,中心点可能位于文字的中央。以上结论仅供参考。

四、阶段测试

图7-15 阶段测试结果

第7节 编写程序——答题

一、显示输入的数值

对于答题功能,最初想到的是用一个文本输入框来输入答案,这样做起来非常简单,不过Roadlab提醒我,对于幼儿来说,在手机这样的设备中输入数字,不是一件令人愉快的事儿,于是按照他的建议,采用数字键的方式输入答案。

我们首先针对数字键1来编程,然后再将代码改写为带有参数的过程,最后,每个数字键的点击事件处理程序都可以通过提供参数来调用该过程。

程序的思路如图7-16所示,代码如图7-17所示。

图7-16 按钮1的点击事件处理程序流程图
图7-17 按钮1的点击事件处理程序

注意,如前所述,当用户输入第一个数字后,启用确定按钮!此时在手机上测试该程序,点击两次数_1按钮,在等号右边将显示两个数字1的图片。

下一步创建一个带有参数的过程“输入数字”,参数名为“键”,代表用户点击的数字键。将数_1的点击事件处理程序加以改造,代码如图7-18所示。

图7-18 创建输入数字过程【第一次批量设置数字按钮的启用属性】

最后,在所有数字键的点击事件处理程序中调用该过程,代码如图7-19所示。

图7-19 所有数字键的点击事件处理程序

经测试,程序运行正常。当用户输入两个数字后,所有数字按钮被禁用,这时用户可以点击确定按钮提交答案,也可以点击清除按钮修改答案,下面我们先来实现答案的修改。

二、清除错误答案

如果用户想修改已经输入的答案,可以点击清除按钮,来清除已经输入的内容。代码如图7-20所示。

图7-20 清除错误答案【第二次批量设置数字按钮的启用属性】

三、判断对错

用户在输入数字后,点击确定按钮提交答案,此时,应用首先要对用户的回答给出评价(发出声音并显示图片),然后再进入下一道题。

首先考虑对答案的判断,这里有两个方案。一个比较简单的方法是设置一个全局变量“和”,当用户点击数字键时,将用户的输入结果保存到“和”中,并与“加数1”及“加数2”之和进行比较。

另一个稍微复杂一点的方法无需设置全局变量,仅从和1、和2的图片属性中截取第一个字符,并拼接两个字符,来求得用户的输入结果。这种方法只用局部变量来保存中间结果,在程序运行结束后,局部变量占用的内容空间将被释放掉。

从程序的可读性方面考虑,前者更好一些,但是从节省内存的角度考虑,后者会更好些。无论哪种方式,对于这个小型的应用来说,不同的选择对整个程序的影响都是微不足道的,不过我们需要有这样的分辨及判断,以便未来应对更大规模的应用。这里我们采用第一个方案。

首先声明全局变量“和”,并设其初始值为空字符,在输入数字过程里,添加两条语句,来记录用户输入的数字,代码如图7-21所示。

图7-21 判断对错的程序流程图【第三次批量设置数字按钮的启用属性】

然后在确定按钮的点击事件中对答案的正确与否进行判断。

图7-22 判断对错

注意,在图7-21中,我们将“和”初始化为空字符,并利用字符的拼接功能,取得最终的输入结果。虽然这个结果的数据类型为字符型,但在图7-22中,我们却将它当作数字,与另一个数字进行比较,而结果证明这样的比较结果是正确的。通常传统的编程语言是禁止这样的比较的,比较或运算只能在同类型的数据之间进行,因此这种类型的语言也成为强类型语言。App Inventor不是这种强类型语言,它的数据类型会因操作符或使用环境的不同而改变。

我们已经实现了判断对错功能,不过,我们希望判断对错的图片(对号或叉号)闪现之后很快隐藏起来,并显示下一道题,同时禁用确定按钮,启用所有数字键。

四、显示下一题

无论用户回答是否正确,都会闪现相应的图片,我们希望图片可以在屏幕上停留瞬间,以便让用户了解到自己的回答是否正确。因此在判断对错与出下一题之间需要间隔一段时间,我们用快闪计时器来实现这一功能,停留的时间为100毫秒。

首先要在点击确定按钮时启动快闪计时器,然后在快闪计时器的计时事件处理程序中,完成出题以及显示题目的操作。代码如图7-23所示。

图7-23 闪现判断对错的图片后出下一题【第四次批量设置数字按钮的启用属性】

我们发现程序中有多处需要统一设置数字键的启用属性,要么全部设为启用,要么全部设为禁用,为了提高代码复用性,我们创建一个带有参数的“启用数字键”过程,来实现这一属性值的切换,代码如图7-24所示。

图7-24 定义过程实现对所有数字键启用属性的设置

分别在初始化动态组件过程(参数为真)、输入数字过程(参数为假)、快闪计时器计时事件处理程序(参数为真)、清除按钮点击事件处理程序(参数为真)中调用该过程。

五、禁用选择时长功能

屏幕的右上角有一个下拉框,用于选择练习时长,其中有五个选项,分别为1、2、3、4、5,已经在设计视图中完成了备选项的设置(逗号分隔字串属性)。我们希望在每次练习开始时,显示该组件,以供用户进行选择;一旦用户输入了第一题答案,并点击了确定按钮,将隐藏该组件,直到练习结束,开始下一轮练习时,再重新恢复该组件的可视状态。这项设置与两个过程有关,一个是初始化动态组件过程,另一个是确定按钮点击事件处理程序,前者已经完成了设置(见图7-6,第五个块),后者添加代码如图7-25所示。

图7-25 用户一旦点击确定按钮,将禁用选择练习时长的下拉框

在第九节中,我们再来讲解游戏时长的选择。

六、隐藏图示

画布上绘制的彩色圆点,可以帮助初学加法的幼儿理解加法运算的意义,也是一种辅助运算的工具,不过对于那些已经认识数字,并能够用数字进行思考和运算的儿童,图示的作用就不大了,因此可以选择将其隐藏起来。屏幕右上角的复选框用来设置图示的隐藏(默认为显示图示,已在设计视图中勾选),具体代码如图7-26所示。

图7-26 设置图示画布的显示与隐藏

七、阶段测试

经过测试,程序运行正常。由于判断对错的图片显示时间短暂(100毫秒),因此图7-27中测试图片的截取颇费了一番周折,不过这个时长对于练习者来说,应该是足够了,况且还有声音提供另一种提示。

图7-27 阶段测试结果

第8节 编写程序——时间控制及得分

一、控制练习时长

下面开始对时长计时器及进度条编程,暂时以默认的1分钟时长为例,来实现对练习时长的控制。

首先声明全局变量“剩余时间”,设其初始值为60(秒),然后在时长计时器的计时事件中,让该变量值递减,直到为零,则练习结束;同时设置进度条的滑块位置=剩余时间,来提示用户练习的进度。代码如图7-28所示。

图7-28 控制练习时长

当剩余时间为零时,停止计时,弹出对话框,显示本次得分及历史记录,并允许用户退出应用、清除历史记录或返回应用,无论是清除记录还是返回应用,都将开始新一轮的练习。这里的两个参数——消息及标题,暂时用文字替代,下一节中将添加具体内容。

二、计算并显示得分

每次用户点击确定按钮,在判断对错的同时,累计得分;记分规则很简单,每做对一道题加10分,利用得分标签的显示文本属性保存并显示得分,代码如图7-29所示。

图7-29 计算并显示得分

三、选择练习时长

在每次练习开始,允许用户选择练习时长,与这项设置有关的变量是剩余时间,有关的组件包括Screen1、进度条的最大值、滑块位置,需要在下拉框的完成选择事件中进行这些设置。代码如图7-30所示。

图7-30 选择练习时长

经测试,程序运行正常,下面我们来处理与游戏结束相关的操作。

第9节 编写程序——游戏结束与重新开始

一、提取、显示及保存历史记录

游戏结束时弹出对话框,其中将显示当前时长的历史记录,为此,需要从本地数据库中读取该记录,如果用户是第一次使用该应用,数据库中尚未保存任何记录,则显示0。

由于有五档练习时长,需要为每档时长保存一个记录,因此数据的存储方式为列表,其中包含5个列表项,分别为5个数字,默认值为0。

创建“游戏结束”过程,来实现相关功能,代码如图7-31所示。

图7-31 提取、显示及保存历史记录

上述过程中,同时显示了历史记录与本次得分,并提供了三个按钮供用户选择,因此功能已经完整,下面需要在时长计时器的计时事件中调用该过程,如图7-32所示。

图7-32 剩余时间为零时,调用游戏结束过程

二、处理用户选择

在对话框组件的完成选择事件中,针对用户的不同选择,执行相应的下一步操作。代码如图7-33所示。

图7-33 处理用户的选择

只要用户没有选择“结束练习”,程序将自动进入下一轮练习,此时需要对所有的动态组件及全局变量进行初始化,因此,除了要调用初始化动态组件过程,还需设置剩余时间的初始值。我们希望将功能相近的代码归拢到同一个过程里,即,将剩余时间的设置归拢到初始化动态组件过程里,但该过程的名称中没有初始化全局变量的意思,因此要对名称加以修改,改为“初始化动态组件及全局变量”,修改后的代码如图7-34所示。

图7-34 当练习重新开始时,初始化动态组件及全局变量

最终的对话框1完成选择时间处理程序如图7-35所示。

图7-35 最终的对话框1完成选择事件处理程序

三、最终测试

在测试手机的AI伴侣环境中,程序运行正常。将项目编译为apk文件,下载安装到手机上进行正式测试,发现在应用启动后显示第一道题时,画布上的彩色圆点不能正常显示(与图7-9类似),猜想可能与快闪计时器的1毫秒计时间隔有关。重新回到设计视图,将快闪计时器的计时间隔设为10毫秒,再重新编译安装运行,问题解决了。(还记得前面关于1毫秒的讨论吗?)

第10节 代码整理

这个应用的技术难度并不大,大体上与前几章持平,甚至可能更简单,但这个应用的环节比较多,处理起来需要非常小心。最后我们来整理一下代码,以便进行适当的优化。

一、代码清单

如图7-36所示,应用中共有5个全局变量、8个过程以及18个事件处理程序。

图7-36 应用中的代码清单

二、要素关系图

从屏幕初始化程序开始,逐一打开折叠的代码,绘制一张要素关系图,如图7-37所示。

图7-37 要素关系图

从图中观察到,快闪计时器、时长计时器、时长下拉框、确定按钮等组件的事件处理程序都有红色箭头指向了具体的组件及变量,这些红色箭头就是代码优化的线索。

首先来看快闪计时器的事件处理程序,如图7-38左图所示,我们可以将否则分支中对图片属性的设置归拢到出题过程里,而“出题”的含义可以包含对这些图片属性的设置,不超出“出题”操作所涵盖的范围。修改后的代码如图7-39所示。

图7-38 有待优化的代码
图7-39 优化之后的代码

其次,我们来简化时长下拉框的完成选择事件,用初始化动态组件及全局变量过程替代原有对变量及组件属性的设置。由于该过程中包含清除画布操作,致使原有题目的图示被清除,因此需要调用出题及图示题目过程,来重新显示题目。修改过程如图7-40所示。

图7-40 让事件处理程序通过调用过程来设置变量及组件的属性值

最后来优化确定按钮的点击事件,很简单,将全部代码放在一个名为“判断对错”的过程里,这样做的目的仅仅是为了提高代码的可读性,让程序的结构清晰。修改后的代码如图7-41所示。

图7-41 让事件处理程序调用过程

在做了以上修改之后,重新整理要素关系图,结果如图7-42所示。

图7-42 调整之后的要素关系图

这个应用试图起到抛砖引玉的作用,希望年轻的家长们能够积极行动起来,自己动手为孩子制作一批优秀的应用,以增进亲子双方的学习热情,并从中获得快乐。