一名计算机入门课上的新生首创了这款应用,与美国国家农场保险公司开发的一款装机量巨大的应用相类似。App Inventor可以利用Android手机中的某些强大功能,包括SMS短信处理、数据库管理、文本转成语音以及位置传感器等,本应用就是一个典型的例子。
美国国家安全委员会(NSC)于2010年1月发布的研究结果表明,每年有至少28%的交通事故——将近160万次车祸源于司机在开车时使用手机,其中至少20万次交通事故与发短信有关(http://www.nsc.org/pages/nscestimates16millioncrashescausedbydriversusingcellphonesandtexting.aspx)。因此,许多国家已经全面禁止司机开车时使用手机。
Daniel Finnegan,一个旧金山大学的学生,在2010年秋季学期的App Inventor编程课上,提出了一个了不起的想法,用一个应用来解决开车发短信泛滥的问题。如图4-1所示,他创建的应用可以对收到的任何短信进行自动回复,如回复“我正在开车,稍后与您联系”之类的内容。
图 4-1 “开车不发短信”
课堂上的头脑风暴增加了应用的功能,也丰富了本教程,这些内容发布在App Inventor网站上:
用户可以根据情况改变回复内容:例如,如果你正在开会或看电影,而不是开车,回复内容可以做相应的修改;
应用可以大声朗读来信内容:尽管有自动回复,但对来信的好奇心也会置你于死地;
回复内容可以包含您当前的位置信息:如果你的另一半正在家做晚饭,他或她可能想知道你何时能到家,但又担心回短信会危及安全。
就在本应用发布到App Inventor网站后的几周,State Farm Insurance创建了名为“On the Move”的Android应用,http://www.statefarm.com/aboutus/newsroom/20100819.asp【译者没找到On the Move,不过该公司的确有几个好的应用值得尝试】,其功能与“开车不发短信”类似。该公司将这项服务作为“掌上代理”应用的升级内容,免费向所有人开放,并在YouTube网站发布视频:http://www.youtube.com/watch?v=3xtjzO0-Hfw【译者访问该网址,提示“此视频未公开”】。
我们无法确认Daniel的应用或App Inventor网站的教程是否对“On the Move”产生了影响,但有趣的是,这让我们思考一种可能性:在一门编程基础课上创建的应用(竟然由一个创意写作的学生创建!)也许会促成软件的批量生产,或至少是对软件生产的生态系统有所贡献。当然,这也说明了App Inventor降低了软件开发的门槛,以至于无论是谁,只要有一个好主意,都可以快速、低成本地把想法变成一个实实在在的、可交互的应用。
相比前几章来说,这是一个更加复杂的应用,因此我们每次只完成一项功能,从自动回复开始。以下是将要学习的内容:
Texting组件:具有收发短信功能;
TextBox组件:用于提交自定义回复信息(需要与Button组件配合使用);
TinyDB数据库组件:用于保存自定义信息,即使应用已经关闭,信息也不会丢失;
Screen.Initialize事件:在应用启动时加载自定义回复内容;
Text-to-Speech组件:用于大声朗读文字;
LocationSensor组件:报告司机的当前位置。
本应用的运行,需要手机上Text-To-Speech(TTS)模块的支持。该模块包含在Android 2或更高的版本中,如果你的操作系统是Android 1.x,需要从Google Play下载。在手机上:
1. 打开Google Play应用;
2. 搜索TTS;
3. 选择应用Text-To-Speech Extended并安装。
TTS模块安装后,可以测试其功能。对于Android2以上版本,在”设置->辅助功能”中打开“文字转换语音输出”功能,根据需要设置默认语言及语速,然后选择“收听示例”。如果没听到任何声音,请确认手机音量已调高;还可以更改TTS引擎默认属性设置,来改变声音效果。
TTS模块设置完成后,登陆App Inventor网站开始新项目“NoTextingWhileDriving”(项目名中不能有空格),然后设置屏幕的标题为“开车不发短信”,最后通过WiFi与测试手机(AI伴侣)连接。
应用的用户界面很简单:一个显示自动回复内容的Label,一个编辑自动回复信息的文本输入框和一个提交输入结果的按钮。还需要拖入一个Texting组件、一个TinyDB组件、一个TextToSpeech组件以及一个LocationSensor组件,这些组件都将出现在“不可视组件”区。你可以在图4-2中看到上述组件在设计器中的截图。
图 4-2 组件设计器中的“开车不发短信”应用
将表4-1中列出的组件拖到预览窗口中,创建出图4-2中所示的用户界面。
表4-1 “开车不发短信”应用中的全部组件
组件类型 |
面板中分组 |
命名 |
作用 |
---|---|---|---|
Label |
User Interface |
PromptLabel |
让用户了解应用的功能 |
Label |
User Interface |
ResponseLabel |
显示即将发送的自动回复信息 |
TextBox |
User Interface |
NewResponseTextbox |
用户在此处输入定制的回复信息 |
Button |
User Interface |
SubmitResponseButton |
用户点击来提交、保存回复信息 |
Texting |
Social |
Texting1 |
处理短信事务 |
TinyDB |
Storage |
TinyDB1 |
将自动回复信息保存在数据库中 |
TextToSpeech |
Media |
TextToSpeech1 |
大声朗读来信 |
LocationSensor |
Sensors |
LocationSensor1 |
感知电话所在位置 |
按照以下方式设置组件属性:
设置PromptLabel的Text属性为“当本应用正在运行时,将用下面的文字来回复所有收到的短信。”
设置ResponseLabel的Text属性为“我正在开车,稍后与您联系。”并勾选FontBold(粗体字)属性;
设置NewResponseTextbox的Text属性为“”(让文本框为空,等待用户输入);
设置NewResponseTextbox的Hint(提示)属性为“输入新的回复内容”,Width设为“Fill Parent”;
设置SubmitResponseButton的Text属性为“修改回复内容。”
从基本的自动回复行开始,再逐步添加更多功能。
自动回复功能需要用到App Inventor的Texting组件,可以把它想象成一个藏在手机里的小矮人,它知晓如何收发短信。该组件用Texting.MessageReceived事件块来响应“收到短信”,拖出该事件块,并在块内放入一些命令块,看看当你收到短信时,会发生什么事情。这里我们希望能够自动回复预先编辑好的内容。
发送短信的操作需要调用Texting1.SendMessage块,将其放在Texting1.MessageReceived块内。Texting1.SendMessage块只能发送短信,至于要发什么内容,以及发给谁,需要在调用之前,由你来告诉它:表4-2列出了自动回复行为所需要的块,而图4-3显示了它们在块编辑器中的样子。
**表4-2 自动回复短信所需要的块
块的类型 |
所在抽屉 |
功能 |
---|---|---|
Texting1.MessageReceived |
Texting |
手机收到短信时会启动该事件的处理程序 |
set Texting1.PhoneNumber to |
Texting |
在发送短信前设置接收者电话号码 |
get number |
Variables |
获取短信发送者的电话号码 |
set Texting.Message to |
Texting |
设置要发送的短信内容 |
ResponseLabel.Text |
ResponseLabel |
用户预输入的短信内容 |
call Texting1.SendMessage |
Texting |
发送短信 |
图 4-3 对收到的短信进行自动回复
手机收到短信将触发Texting1.MessageReceived事件。如图4-3所示,发送者的手机号保存在参数number中,短信内容保存在参数messageText中。自动回复就是要向发送者发送一条短信,为此要先设置Texting组件的两个关键属性:PhoneNumber及Message。PhoneNumber设置为发送者的手机号,Message设置为ResponseLabel中显示的内容:“我正在开车,稍后与您联系。”设置完成之后,调用Texting. SendMessage实现自动回复。
注释是编程工作的重要组成部分,它可以告诉其他程序员那些与代码有关的重要信息。在块上单击右键,在快捷菜单中选择“Add Comment”(添加注释),此时块的左上方会出现蓝色问号,点击蓝色问号,会弹出文本输入框,可供输入注释信息;点击蓝色问号还可以隐藏注释信息。注释在应用中不是必须的,这里添加注释是为了介绍每个块的功能。
很多人用注释来记录创建应用的过程;注释可以解释程序的功能,但不会改变程序的行为。无论是你自己今后要修改程序,还是其他人要对程序做个性化设置,注释都是非常重要的。“没有不变的软件”已经成为共识,因此,代码的注释是软件工程中非常重要的环节,尤其像App Inventor这样的开源软件。
测试: 需要用第二部手机来测试程序。如果没有,可以注册申请Google Voice或其他类似的服务,并从注册的服务中给你的手机发送短信。用第二部手机给正在运行本应用的手机发短信,第二部手机是否收到了回信?
下面来添加更多的程序块,允许用户输入自定义的回复内容。在组件设计器中,已经添加了名为NewResponseTextbox的TextBox组件,用于输入自定义回复信息,当用户点击SubmitResponseButton时,NewResponseTextbox中的内容被复制到ResponseLabel中,这就是自动回复短信的内容。表4-3列出了在ResponseLabel中显示新的回复内容所需的块。
表4-3 显示自定义回复所需的块
块的类型 |
所在抽屉 |
功能 |
---|---|---|
SubmitResponseButton.Click |
SubmitResponseButton |
点击按钮提交新的回复信息 |
set ResponseLabel.Text to |
ResponseLabel |
为该Label设置新的文本内容 |
NewResponseTextbox.Text |
NewResponseTextbox |
用户在这里输入新的回复内容 |
一个典型的输入表单 的作用是:首先在文本框中输入文字,然后单击提交按钮来通知系统做处理。图4-4显示了用户点击“修改回复内容”按钮时, SubmitResponseButton.Click事件被触发。
图 4-4 将用户输入的信息设置为自动回复内容
事件处理程序将用户在NewResponseTextbox中输入的文字复制到ResponseLabel中,而ResponseLabel保存的是自动回复信息,因此要确保新输入的信息显示在ResponseLabel中。
测试:输入一段自定义信息并提交,然后用第二部手机发送短信到测试手机上,看看这次自动回复的是新定制的内容吗?
你创建了一个伟大的应用,却留下了一个陷阱:用户输入了定制回复,然后关闭应用,当再次启动应用时,定制回复却不见了(取而代之的是默认回复)。这种状况可不是用户所期望的,他们希望在重启应用时,定制的内容还在,为此需要信息的永久保存。
你可能认为数据放在ResponseLabel组件的Text属性中,也应该算作“储存”,但实际上组件属性中的数据是临时数据,就像人的短时记忆,只要应用关闭,数据就会被“忘记”。如果希望应用能永久记住某些数据,就需要将数据从短时记忆(组件的属性或变量)转移到永久记忆中(数据库)。
要永久地保存数据,需要使用TinyDB组件,它可以将数据存储在Android设备内置的数据库中。TinyDB提供两个功能: StoreValue(保存值)和getValue(获取值)。前者允许应用将信息存储在设备数据库中,而后者则允许应用重新读取已存储的信息。
对于多数应用,可以采取如下策略:
1. 每当用户提交新值,将其存储到数据库;
2. 应用启动时,从数据库中加载数据并将其赋给一个变量或属性。
为了实现数据的永久保存,必须修改SubmitResponseButton.Click事件处理程序,表4-4中列出了所需要的程序块。
表4-4 用TinyDB数据库存储定制回复所需要的块
块的类型 |
所在抽屉 |
功能 |
---|---|---|
TinyDB1.StoreValue |
TinyDB1 |
将用户的定制信息保存在手机内置的数据库中 |
”responseMessage” |
Text |
以此作为保存数据的标签 |
ResponseLabel.Text |
ResponseLabel |
已设定的回复信息显示在这里 |
TinyDB从ResponseLabel的Text属性中提取内容,并将其保存在数据库中。如图4-5所示,向数据库中保存数据时,要为数据设置一个tag(标签),本例中的tag是“responseMessage”。可以把tag想象成数据在数据库中的存放地址,是数据的唯一标识。在下节中你将看到,必须使用相同的tag(“responseMessage”)才能将数据从数据库中读取出来。
图 4-5 永久保存自定义回复信息
将定制回复信息保存在数据库中,以便用户再次启动应用时,保存的数据可以被重新读取出来。App Inventor提供了一个特殊的事件块:Screen1.Initialize,当应用启动时,将触发该事件(我们在第3章MoleMash中使用过)。将Screen.Initialize块拖出来,并将某些程序块放在其中,那么这些程序块会在应用启动时逐一执行。
在本应用中,Screen1.Initialize事件的处理程序会检查数据库中是否存放了自定义回复内容。如果是,则使用TinyDB.GetValue函数加载存储的内容。实现这一功能所需的块见表4-5。
表4-5 应用启动时用于加载数据的块
组件类型 |
所在抽屉 |
作用 |
|
---|---|---|---|
Initialize global response to |
Variables |
用于存放数据库中读出的定制回复信息 |
|
“” |
Text |
变量的初始值可以是任意值 |
|
Screen1.Initialize |
Screen1 |
应用启动时会触发该事件 |
|
set global response to |
Variables |
用从数据库中读出的值为该变量赋值 |
|
TinyDB1.GetValue |
TinyDB1 |
从数据库中读取已存储的定制回复信息 |
|
“responseMessage” |
Text |
插入TinyDB.GetValue的tag插槽,与之前TinyDB.StoreValue使用相同文本 |
|
If |
Control |
判断读出的数据中是否包含文字 |
|
> |
Math |
检查读出的数据长度是否大于0 |
|
Length(text) |
Text |
检查文本类型数据的长度 |
|
get global response |
Variables |
从变量中读出的数据(定制回复信息) |
|
数字0 |
Math |
用于比较长度 |
|
set ResponseLabel.Text to |
ResponseLabel |
如果读出的数据有内容,放在label中 |
|
get global response |
Variables |
从变量中读出的数据(定制回复信息) |
如图4-6所示,要想理解这些块的功能,必须设想用户的使用过程:首次打开应用,输入自定义回复,随时退出并再次打开应用。用户首次启动应用时,数据库中没有定制回复可供加载,因此ResponseLabel中显示的是默认回复。再次启动时,才有可能从数据库中加载定制回复,并将其显示在ResponseLabel中。
图 4-6 应用启动时从数据库中加载定制回复
应用启动时触发Screen1.Initialize事件,并用tag “responseMessage”来调用TinyDB1.GetValue,该tag与之前用户存储定制回复时采用的tag相同。读出的值放在变量response中,并对其进行检验,然后才能在ResponseLabel中显示。想想看,为什么从数据库中读出的数据,在向用户显示之前,要经过检验呢?如果数据库中不存在与指定tag相对应的数据,TinyDB将返回空文本;而第一次启动应用时,数据是不存在的,直到用户输入了自定义回复,数据才会有。由于变量response中保存了数据库返回值,因此可以用if块来检查其长度是否大于0。如果大于0 ,说明的确从TinyDB读出了定制回复信息,就会将信息显示在ResponseLabel中;如果长度不大于0,说明之前没有保存过定制回复信息,因此将不修改ResponseLabel的显示内容(保留默认自动回复内容)。
测试:上述功能无法进行实时测试,因为每次连接“AI伴侣”启动应用时,数据库都会被清空。因此需要选择“build->App(provide QR code)”,然后扫描条码,将应用下载安装到手机上。安装之后,在NewResponseTextbox中输入新的回复信息并单击SubmitResponseButton按钮;关闭应用并重新启动它,这次定制回复信息出现了吗?
本节将修改应用:收到短信后,手机将大声朗读发送者的电话号码以及短信内容。开车收到短信,虽然有自动回复功能,但你还是禁不住想知道短信的内容。使用text-to-speech功能,就可以手不离方向盘而收听到短信的内容。
Android设备提供了text-to-speech功能,而App Inventor提供了一个TextToSpeech组件,它可以读出任何text(文本信息 )(注意,此处“text”指的是一般意义上的字/word:一串字母、数字以及标点符号组成的文本,而不是短信文本 。)
在本章的“准备开始”部分,我们要求你从Android Market下载一个text-to-speech的模块。如果你还没做,现在该去做了。根据需要安装并配置完模块之后,就可以在App Inventor中使用TextToSpeech组件了。
TextToSpeech组件的使用非常简单,只需调用它的Speak函数并插入要朗读的文字即可。例如,图4-7中的函数会说“Hello World”。
图 4-7 会说“HelloWorld”的块
在本应用中,朗读的内容则更为复杂,既要包含短信发送者的电话号码,也要包含短信内容,而不只是像“Hello World”那样的静态文本。这里要用到极为重要的join块,它可以将若干文本片段(或数字以及其他字符)连接成单一的文本对象。
在之前的Texting.MessageReceived事件处理程序中,加入对TextToSpeech.Speak的调用。在之前的事件处理程序中,通过适当设置Texting组件的PhoneNumber和Message属性,然后发送回复信息。现在需要加入表4-6中所列出的块来扩展该事件的处理程序。
表4-6 朗读收到的短信所需的块
块的类型 |
所在抽屉 |
功能 |
---|---|---|
TextToSpeech1.Speak |
TextToSpeech1 |
大声读出收到的短信 |
join |
Text |
连接生成将被朗读的文字 |
“SMS text received from” |
Text |
被读出的第一段话 |
get number |
Variables |
获得短信发送者的电话号码 |
“. The message is” |
Text |
在读完电话号码之后稍加停顿,然后说”The message is” |
get messageText |
Variables |
获得收到的短信文本 |
在自动回复动作完成之后,将调用TextToSpeech1.Speak函数,如图4-8的下半部分所示。你可以在TextToSpeech1.Speak函数的消息槽中插入任何文本对象。在这种情况下,join块用来生成被读出的内容。它将几段文字串连在一起,最后生成类似这样的信息:“SMS text received from 111-222-3333. The message is:hello.”
图 4-8 大声读出收到的短信
测试:你需要第二部手机来测试应用。用第二部手机发送文字【必须是英文】到你的测试手机上。你的手机大声读出信息了吗?它是否照常发送自动回复?
像Facebook的Places以及Google的Latitude等类型的应用,都是利用GPS信息来帮助人们跟踪彼此的位置信息。这样的应用最令人担忧的是隐私问题,原因之一是它引发了人们对“老大哥”的恐惧,这里的“老大哥”指的是那些设法跟踪其公民下落的集权政府。但是使用位置信息的应用的确非常有用,试想一个迷路的小孩,或者那些在森林里迷路的徒步旅行者。
在“开车不发短信”的应用中,位置跟踪让回复的短信再多一点内容,而不只是“我正在开车”,回复的信息可以是这样的:“我正在北京东直门内大街209号开车”。对于那些正在等待朋友或家人到来的人来说,这些额外的信息非常有益。
App Inventor提供了LocationSensor(位置传感器)组件,作为手机的GPS (Global Positioning System全球定位系统)功能的接口。除了纬度和经度信息,LocationSensor也可以接入到谷歌地图,为司机提供当前位置的地址信息。
值得注意的是,LocationSensor并不总在读取信息,因此务必要恰当地使用这一组件。具体地讲,应用只对LocationSensor.LocationChanged事件做出响应,而两种情况会触发LocationChanged事件:①当手机的位置传感器第一次收到位置信息时;②随着手机的移动,产生新的位置信息时。使用表4-7中列出的块,具体方法是:当LocationChanged事件触发时,将当前地址信息保存到变量lastKnownLocation中,再将变量值插入到自动回复信息中。
表4-7设置位置传感器的块
块的类型 |
所在抽屉 |
功能 |
---|---|---|
Initialize global lastKnownLocation to |
Variables |
创建一个变量来保存最后读取的地址信息 |
“未知” |
Text |
lastKnownLocation的默认值 |
LocationSensor1.LocationChanged |
LocationSensor1 |
位置传感器第一次读到位置信息,或每次位置信息变化时触发该事件 |
set global lastKnownLocation to |
Variables |
设置变量值,稍后会用到 |
LocationSensor1.CurrentAddress |
LocationSensor1 |
当前地址信息,如:”北京市东城区东直门内大街209号” |
当位置传感器首次读取位置信息时,LocationSensor1.LocationChanged事件被触发,随着设备的移动,还会生成新的位置信息,事件将被再次触发。由于自动回复信息中要包括地址信息,因此,通过调用LocationSensor1.CurrentAddress函数 来获取地址信息,并将其保存在lastKnownLocation变量中,如图4-9所示。在后台,这个函数会调用谷歌地图(通过API 来调用,将在第24章学到),并根据传感器获得的经纬度信息来确定最近的街区地址。
图 4-9 每当传感器收到GPS位置信息时,用变量记录手机的位置
注意,这些块只完成了一半的工作,还要将位置信息插入自动回复信息中,再回复给发件人。
用变量lastKnownLocation中的值对Texting1.MessageReceived事件处理程序加以修改,向自动回复信息中添加位置信息。表4-8列出了所需要的块。
表4-8在自动回复中显示位置信息的块
块的类型 |
所在抽屉 |
功能 |
---|---|---|
join |
Text |
多段文本的连接器 |
ResponseLabel.Text |
ResponseLabel |
不包含位置信息的定制回复信息 |
“我最后的位置在:” |
Text |
原定制信息之后的位置信息提示词 |
global lastKnownLocation |
Variables |
地址信息,如:“北京市东城区东直门内大街209号” |
向回复中添加位置信息需要LocationSensor1.LocationChanged事件与变量lastKnownLocation的协作。如图4-10所示,并非直接发送ResponseLabel.Text中的信息,而是使用join块将若干段信息整合起来:原有的自动回复信息+“ 我的最后位置在:”+变量lastKnownLocation。
图 4-10 在回复信息中加入位置信息
变量lastKnownLocation的默认值是“未知”,所以如果位置传感器尚未产生位置信息,则自动回复的第二部分内容为“我的最后位置在:未知”。如果已经产生了位置信息,则自动回复的第二部分内容有可能是这样:“我的最后位置在:北京市东城区东直门内大街209号”
测试:用第二部手机发送短信到运行应用的手机上,第二个手机是否接收到了带有位置信息的自动回复?如果没有,请确保你已经开启了第一部手机的GPS定位功能。
图4-11中显示了“开车不发短信”最终的块的配置
图 4-11 完整的“开车不发短信”应用(同时显示所有注释)
当应用已经付诸使用,你也许会想到做一些改进,例如:
编写另一个版本,允许用户针对某些特定来信号码定制回复内容。你需要增加一个条件(if)块,来检查这些来信的手机号。有关条件块(if)的更多信息,请参见第18章;
再编写一个版本,根据用户是否在某个纬度/经度范围内,来回复定制信息。这样,如果应用判断你在房间222,它会回复“鲍勃在222房间,现在不能回复短信。”有关LocationSensor以及确定边界的更多信息,请参见第23章;
另一个版本,当短信发送者的手机号码在某个“通知”列表中时,发出提醒铃声。关于如何使用列表,请参阅第19章。
下面是本章涉及到的一些概念:
Texting组件:既可以用来发短信,也可以处理收到的信息。在调用Texting.SendMessage之前,需要为Texting组件设置PhoneNumber及Message属性。为了回复收到的短信,需要为Texting.MessageReceived事件编写处理程序;
TinyDB组件:用于将信息永久存储在手机数据库中,以便每次应用启动时都可以加载保存过的数据。有关TinyDB的更多信息,请参见第22章;
TextToSpeech组件:对于所提供的任何文本对象,都可以大声朗读出来(限英文);
join块:用于将若干片段的文字拼凑(或连接)成单一的文本对象中;
LocationSensor组件:可以报告手机的纬度、经度及当前的街道地址。为了确认它是否收到了位置信息,可以访问LocationSensor.LocationChanged事件处理程序中的相关参数。该事件在第一次收到位置信息时被触发,并在每次位置信息更新后被再次触发。有关LocationSensor的更多信息,请参见第23章。
Text-To-Speech
缩写为TTS,直译为“从文本到语音”,是语音合成技术的一种应用,可以将文字信息实时转换为语音。在本章中使用了Android设备的此项功能,但遗憾的是,目前Google的TTS引擎尚不支持中文的转换,因此学员测试时还需使用英文。
text: 文本[名词]
text: 发短信[动词]
initialize: 初始化
speech: 讲话
location: 位置
sensor: 传感器
tiny: 微小的
DB: database数据库
social: 社交的
storage: 存储
prompt: 提示
response: 响应
receive: 接收
submit: 提交
hint: 提示
variable: 变量
phone: 电话
number: 数字,号码
call: 调用,呼叫
message: 消息
send: 发送
add: 增加
comment: 注释,评论
voice: 声音
store: 存放
value: 值
tag: 标签
global: 全球的,全局的
control: 控制
math: 数学
length: 长度
provide: 提供
code: 编码
GPS: 全球定位系统(Global Positioning System)
change: 改变
last: 最后的
current: 当前的,现在的
本文介绍如何在Scratch中对EV3机器人进行开发。
本文教你一招超简单的方法,只需三步,就能将 sb3 格式作品转换成 H5,实现在移动端轻松玩!
由于Scratch软件界面显示和国内等级考试大纲用词不同,部分名词出现了两种称呼。例如指令模块(积木)、程序区(代码区),其实表达的是一个意思。
用 Scratch 代码编写了成功运行 Linux 内核的模拟器。
MicroBlocks 是受 Scratch 启发, 为「物理计算」而生的图形化编程语言。
App Inventor是一款图形化、模块化的安卓应用开发工具,可以让用户通过积木式的堆叠法来完成您的Android程式,避免复杂的代码细节。