开发体验

第16章 应用的存储

就像人类需要记忆一样,应用需要存储。本章将探究如何在应用中实现信息的存储。

如果刚刚有人在电话里告诉你一家比萨店的电话号码,你的大脑中会留下一段记忆;如果有人说出几个数字,让你来求它们的和,你也会将这些数字以及运算的中间结果保存到记忆中。这些过程几乎是人类的本能,你可能不会意识到,你的大脑是在保存或提取信息。

应用同样具备记忆功能,但它的内在机制并不像大脑那样神秘。本章将学习如何设置应用的存储功能,即在应用中如何保存信息,以及之后如何将保存的信息提取出来。

被命名的存储空间

应用的“记忆”由一组被命名的固定长度的存储空间组成(想象一下划分整齐的大型停车场。——译者注)。一旦将组件拖到应用中,就会自动创建一组被称为“属性”的存储空间。同样,声明了一个变量,就是创建了一个与特定组件无关的、被命名的存储空间。如果说属性通常与应用的外观呈现有关,那么变量则被认为是应用中不可见的“暂时”记忆。

属性

用户在应用中看到的可视组件,如按钮、文本输入框以及标签等,仅仅是组件的外观。在应用内部,这些组件其实是一组数据——每个组件都由一组属性值来定义。属性值保存在存储空间中,是它们决定了组件的外观。

在设计视图中可以直接设置组件的属性值,例如,在图16-1中显示了画布组件的属性面板,在这里可以对属性值进行修改。

图16-1 在设计视图中设置这些属性的初始值(不同于应用运行时的值)

画布组件有若干个不同类型的属性,其中背景颜色属性及画笔颜色属性中保存的是颜色值,背景图片属性中保存的是文件名(kitty.png),允许显示属性中保存的是布尔值(真或假,取决于其下方的选择框是否被勾选),宽度及高度属性中保存的是数字或指定值(如,充满)。

在设计视图中设置组件的属性,实际上是设置这些属性的初始值,即应用启动时的属性值。在应用的运行过程中,可以用程序来修改这些属性值。不过,用程序修改属性值,并不会改变它们在设计视图中显示的值,像图16-1中显示的那些属性将始终作为初始值而存在。在测试应用过程中,你可能不会意识到这一点,因为这些属性值在用户界面中是不可见的。

定义变量

像属性一样,变量也是被命名的存储空间,只是它们不与任何组件相关联。在应用中如果需要记住某事,而又无法用组件的属性来记住它时,就需要定义一个变量。例如,一个游戏类的应用可能需要记住玩家到达的等级。如果等级数要用标签组件显示出来,那就不需要定义变量,因为标签组件的显示文本属性可以用来保存这个值。但是,假如不想把等级数显示给用户,就应该定义一个变量来保存它。

另一个使用变量的例子是第8章的“总统问答”应用。在这个应用中,用户界面上一次只能显示一个问题,而其余的问题用户是看不见的(而且看不见的问题总是多数),因此,需要定义一个列表变量来保存全部问题。

在设计视图中拖入一个组件,它的属性就自动生成了。相比之下,变量则需要在编程视图中单独定义:从内置代码块的变量抽屉中拖出一个“声明全局变量(我的变量)”块,点击“我的变量”为变量命名,并为变量设置初始值,方法是拖出另一个块放在声明变量块的插槽中,这个块可以是数字块、文本块、颜色块或者是列表块等。跟随下面的步骤就可以创建一个初始值为0、名称为“分数”的变量。

  1. 从编程视图的内置块分组中找到变量,点击打开抽屉并拖出“声明全局变量”块。
  2. 为变量命名:点击“我的变量”,输入“分数”。
  3. 设置变量的初始值:从数学抽屉中拖出数字块0,填充到声明全局变量块的插槽中。

定义一个变量,就是通知应用开辟一块有名称的存储空间,来保存某个值。像属性值一样,在应用运行时,这些存储空间用户是看不见的。

在应用启动时,会完成变量的初始化——将预先设置的数字0存放到名为“分数”的存储空间中。可以用数字或文本对变量进行初始化,除此之外,也可以插入一个“列表”块或“空列表”块,通知应用这个变量(存储空间)用于保存一个列表,而不是单个的值。关于列表的更多内容请参考第19章。

变量的设置与读取

变量定义之后,App Inventor会生成两个属于这个变量的块:设置块及读取块。只要将鼠标悬停在声明全局变量块的变量名称之上,就可以呼出这两个块。如图16-2所示。

图16-2 在声明变量块中包含了变量的读取块和设置块

其中的“设 global 分数 为”块可以用来修改变量的值,例如在图16-3中,将数字块5放在变量分数的设置块中。“设变量”块中的“global”一词意为“全局的”,指的是变量的适用范围。一个全局变量可以被应用中所有事件处理程序及过程所引用。新版的App Inventor中还可以定义一种“local”(局部)变量,这种变量只能在一个事件处理程序或某个过程的内部进行定义,而且定义变量的同时,也定义了变量的有效范围(本章稍后有详细介绍)。

图16-3 将数字5放在全局变量分数中

标有“global 分数”字样的块被称为变量的读取块,用于提取变量的值。例如,你想知道保存在变量分数中的值是否大于100,就可以将读取块放在条件语句中进行判断,如图16-4所示。

图16-4 用全局变量读取块获得保存在变量中的值

设变量为表达式

如前所述,可以设变量的值为单个的数字5,然而更多时候,会设变量为一个复杂的"表达式"(“表达式”是一个计算机科学术语,可以理解为公式)。例如,在“总统问答”应用中,用户点击下一题按钮时,要设变量“当前问题索引值”为其当前值加1,来显示下一道题。又如在游戏类应用中,如果玩家操作失败,有可能从他现有的成绩中减去10分。还有像第3章打地鼠的游戏中,通过设置地鼠的x坐标为一个随机数(表达式),来改变地鼠在画布组件中的水平位置。总之,可以将若干个块组成的表达式填充到变量的设置块中。

变量的递增

最常见的表达式可能是变量的递增,或依据变量的当前值设置变量的新值。例如,游戏中当玩家操作成功一次,分数就将增加5。图16-5显示了实现这一功能需要的块。

图16-5 分数变量递增5

如果能够理解这些块的含义,那么你离程序员又近了一步。这些块可以理解为“让分数在当前值的基础上增加5”,这是变量递增的另一种说法。要理解这些块的工作机制,需要按照从内向外而不是从左到右的顺序来解读它们。最里面的块是“global 分数”以及数字“5”,它们是最基础的块,然后是“+”块执行加法运算,并将计算结果设定为变量分数的值。

假设在执行这组代码块之前,存储空间中分数的当前值为10。经过这些块的运算,程序执行了以下步骤:

  1. 从分数的存储空间中读取当前值10;
  2. 用10加上5得到结果15;
  3. 将15放回到分数的存储空间中(完成设置)。

构造复杂的表达式

在数学抽屉中,App Inventor提供了许多数学函数,就像你在电子表格或计算器中见到的一样。其中有算术运算符(如,+、-、×、÷)、求随机数、开平方以及三角函数(sin、cos)等。

利用这些块可以构造出复杂的表达式,并将它们作为一个整体填充到变量的设置块的右侧。例如,想让一个飞碟(精灵)在画布的边界内产生随机的水平移动,就需要使用一个乘法块(×)、一个减法块(-)、画布和飞碟的宽度属性,以及一个随机小数块,来组织一个表达式,如图16-6所示。

图16-6 使用数学块来构造图中的复杂表达式

正如前面在变量递增的例子中所说的,程序对这些块的解读遵循从内而外的顺序。假设画布的宽度属性为300,飞碟的宽度为50,程序将执行下列步骤:

  1. 分别从画布宽度及飞碟宽度的存储空间中读取数值300及50;
  2. 执行减法运算:300 - 50 = 250;
  3. 调用随机小数块,获得一个0~1之间的随机数(比如说0.5);
  4. 执行乘法运算:250 * 0.5 = 125;
  5. 将125放在飞碟x坐标属性的存储空间中。

显示变量

在前面的例子中,修改一个组件的某些属性,将直接影响到用户界面的外观。而变量则不然,改变一个变量并不会直接影响应用的外观。如果你只是递增了分数的值,而不设法修改用户界面的话,用户永远都不知道发生了变化,就像众所周知的“树倒在森林里一般:如果森林里没有人,怎么证明树倒下时真的发出声音了呢?

有时,当变量变化时,不希望在用户界面上立即显示出来。例如,在游戏中,可能会记录某些统计结果(如失败次数),只有游戏结束时才会显示其结果。

与组件的属性相比,这是使用变量来存储数据的一个优势:可以在需要的时候显示必要的数据。这样做可以将数据的处理与数据的呈现分离开来,以便于此后对应用逻辑以及用户界面的独立修改。

例如,在游戏中,可以将得分直接保存在标签的显示文本属性中,也可以保存在变量中。如果保存在标签中,得分时可以让标签的显示文本属性值递增,用户可以直接看到成绩的变化;如果得分被保存到变量中,并用变量的递增记录得分,则需要另外设置块,将变量值显示到标签中。

尽管使用变量保存并显示数据要多出一些步骤,但当你决定要修改应用,以不同的方式在用户界面上显示分数时(如使用数字滑动条),变量的方法让改变很容易实现。你不必修改那些计算分数的块,而只需要修改与显示分数有关的块。

局部变量

到目前为止,本章中所描述的变量指的都是全局变量,全局变量用“声明全局变量”块来定义。“全局”指的是变量的有效范围。应用中的所有事件处理程序及过程,都可以使用已经定义的全局变量,也就是说,全局变量在全局范围内有效。

在App Inventor的最新版本中,也可以定义局部变量。顾名思义,局部变量只在某个事件处理程序或过程的内部有效。如图16-7所示。

图16-7 变量“总分”为局部变量,仅在加和按钮点击事件处理程序中有效

如果一个变量只在一处被用到,就可以将其定义为局部变量,例如图16-7中的变量“总分”。这样做有利于限制应用中代码的关联程度,可以确保变量不会被误修改。可以将局部变量理解为你大脑中记住的秘密——显然你不希望其他的大脑知道这些秘密。

小结

应用启动之后,会执行一系列的初始化操作,并准备好对即将发生的事件进行响应。在响应事件的过程中,程序有时需要记住一些东西,如游戏中每个选手的成绩,或者某个对象的移动方向等。

应用中可以用组件的属性来保存数据,但当需要保存的数据与组件无关时,就需要定义变量。可以将数据保存到变量中,也可以从变量中读取数据的当前值,就像使用组件的属性一样。

无论是属性值,还是变量值,对最终用户来说都是不可见的。如果你想让最终用户看到保存在变量中的信息,就要用标签或其他用户界面组件,通过添加相应的代码块来显示这些信息。

注释:

树倒在森林里:这个典故源于一个问题:如果一棵树在森林里倒下,而森林里没有人,那么树是否发出了声音呢?提问者是17世纪末18世纪初的一位英国哲学家,这涉及哲学的根本问题,即,是物质决定意识还是意识决定物质。