codecamp

第 14 章 理解应用的结构

本章将从程序员的视角来探讨应用的结构问题。先从一个经典的比喻开始,将应用理解为一份菜谱,然后再从组件的角度,理解其对事件的响应,从而对应用产生新的认识。本章还将探讨应用如何提问、重复、记忆以及与Web交互,所有这些在后面章节均有更详细的叙述。

{%}

大多数人是从用户的角度来描述一个应用,但是在程序员看来,应用要复杂得多。我们必须充分理解应用的内部结构,才能更加有效地创建应用。

通常从两个方面来描述应用的内部结构:组件及行为,这大致与App Inventor的两个主要窗口相对应:组件设计器及块编辑器。前者用来设定应用中的对象(组件),而后者用来编写程序,实现对用户及外部事件的响应(应用程序的行为)。

在图14-1中,对应用的架构给出了总体描述,本章将对这种架构进行深入细致的探讨。

{%}

图 14-1 App Inventor应用的内部结构

组件

应用中的组件分为两大类:可视组件及非可视组件。可视组件是在应用启动后能够看到的组件,如Button、Textbox及Label等,这些通常被视为应用的用户界面。

非可视组件是不可见的,因此它们不是用户界面的组成部分,通常用于访问设备的内置功能,如,Texting组件用于收发短信,LocationSensor组件用于确定设备的位置,而TextToSpeech组件用于朗读文字。非可视组件是设备的技术核心,是服务于应用程序的小精灵。

两类组件都是由一组属性来定义,属性相当于组件信息的存储空间。如可视组件的Width、Height及Alignment属性,它们共同定义了组件的外观。因此,最终用户所看到的如图14-2所示的“Submit”按钮,实际上是在组件设计器中由一组属性所定义的,如表14-1所示。

表14-1 按钮属性

Width Height Alignment Text
50 30 center Submit

{%}

图 14-2 提交按钮

可以将属性理解为上面表格中的内容,在组件设计器中,用它们来定义组件的初始外观。如果将Width属性从50改为70,那么无论是在设计器中,还是在实际应用中,按钮看起来都会变宽。注意,最终用户不会看到70这个数字,他们只能看到按钮变宽。

行为

一般来说,人们很容易了解组件的用途:文本框(Textbox)用于输入信息,按钮(Button)用来点击等等。但是对于应用中的行为,则往往是抽象的和复杂的。行为定义了应用对事件的响应,无论是用户发起的事件(如点击按钮),还是外部事件(如手机收到短信)。定义这些交互行为的难度恰恰是编程的挑战性所在。

幸运的是,App Inventor提供了一种非常适合于定义行为的可视化“块”语言,本节为理解块语言提供了一种模型。

应用如菜谱

人们习惯于把软件与菜谱相对比。像菜谱一样,传统的应用由一系列的顺序排列的指令构成,如图14-3所示,而计算机(厨师)则按顺序执行这些指令。

{%}

图 14-3 传统的软件由一系列顺序执行的指令构成

一项典型的银行业务可能按如下顺序执行:

1. 启动某项业务;

2. 执行某些计算并修改客户账目;

3. 在屏幕上显示新的余额信息。

应用就是一系列的事件处理程序

然而,如今的绝大多数应用,无论是手机的,还是Web或桌面电脑的,都不再适合采用这种菜谱模式了。应用不再是顺序地执行一系列的指令,相反,更为普遍的是对事件的响应,事件的触发者是最终用户。例如,当用户点击按钮时,程序会做出响应,执行某些操作(如发送短信)。对于使用触屏的手机或设备,当你的手指在屏幕上拖动时,将触发另一类事件,在应用中可以利用这类事件,在手指最初的接触点与最终的抬起点之间画一条线,作为对该事件的响应。

这种类型的应用更适合于概括为“对事件做出响应的组件的集合”。这类应用中依然包含了“菜谱”——一些顺序执行的指令,但每个菜谱只限于对某些特定事件做出响应,如图14-4所示的“菜谱”。

{%}

图 14-4 拥有多个“事件菜谱”的应用

因此,当事件发生时,应用通过调用一系列的函数进行回应。这里的函数是指①利用某些组件来完成某些操作,如发送短信;②对某些组件属性进行操作,如在用户界面上修改label的text属性。调用函数意味着让函数运行,让它产生作用。我们把事件,连同对事件进行响应的一系列函数统称为事件处理程序(Event Handler)。

许多事件由最终用户触发,但还有些不是。应用可以对手机内部的事件进行响应,如方向传感器的变化以及时钟的行走(即时间的流逝),也可以对手机以外的事件做出响应,如来自于其他手机的事件,或收到来自web的数据,等等。如图14-5所示。

{%}

图 14-5 应用可以兼顾对内部及外部事件的响应

之所以称App Inventor编程为“直观”编程,是因为这种编程完全基于一种事件响应模式,而“事件处理程序”则是该语言中最重要的词汇(在其他语言中情况未必如此)。想要定义某个行为,首先要拖出一个事件块,事件块在形式上是这样的:“When do”。假设有这样一个“朗读”应用,当用户点击按钮时,应用大声读出用户输入的文字,这个应用只需要一个事件处理程序,如图14-6所示。

{%}

图 14-6 “朗读”应用中的事件处理程序

这些块的作用是,当用户点击“SpeakItButton”按钮时,TextToSpeech组件将朗读用户在输入框TextBox1中输入的文字。在这里,事件是SpeakItButton.Click,对事件的响应是调用TextToSpeech1.Speak函数,事件处理程序中包括了图14-6中的所有块。

在App Inventor中,所有活动都发生在对事件的响应之中,应用中不可能存在事件块“when-do”之外的块,如图14-7这样的单摆浮搁的块是毫无意义的。

{%}

图 14-7 事件处理程序之外的散在的块毫无用处

事件类型

可以引发活动的事件被分类列在表14-2中。

表14-2 能够引发活动的事件

事件类型 举例
用户发起的事件 当用户点击Button1时,执行...
初始化事件 当应用启动时,执行...
计时器事件 当20毫秒过去时,执行...
动画事件 当两个物体碰撞时,执行...
外部事件 当电话收到短信时,执行...

用户引发的事件

用户引发的事件是一种最常见的事件类型,在输入表单中,通常点击按钮事件会引发应用的响应。图形化的应用更多的是对触摸及拖拽事件做出响应。

初始化事件

有时需要在应用启动时实现某些功能,这既不同于响应最终用户引发的事件,也不是对其他类型事件的响应,那么如何让这种情况也适合于事件处理模式呢?

在App Inventor这种基于事件处理的语言中,应用的启动也被视为一种事件。如果你想在应用打开的同时实现某些功能,可以拖出Screen1.Initialize事件块,并将某些函数调用块放在其中。

例如,在第三章打地鼠游戏中,在应用启动的同时,通过调用MoveMole过程,将地鼠放在一个随机的位置,如图14-8所示。

图 14-9 一旦Clock1.Timer开始运行(计时),使用计时器事件块来移动球

动画事件

在canvas范围内的图形对象(sprites精灵),它们的活动将触发动画事件,具体地说,当两个sprites发生碰撞,或一个sprites到达canvas的边界时,将触发动画事件。因此可以编写游戏或其他交互式动画程序,利用动画事件来定义游戏或动画的情节。更多信息请参见第17章。

外部事件

当手机从接收到来自GPS卫星的位置信息时,将触发一个外部事件;同样,当手机收到短信时,也会触发此类事件(图14-10)。

第 13 章 亚马逊掌上书店
第 15 章 软件工程与应用调试
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }