第 2 章 油漆桶

第 2 章 油漆桶

本章介绍画布组件,用它来绘制简单的二维(2D)图形。具体目标是创建一个名为“油漆桶”的应用,让用户可以用不同的颜色在手机屏幕上绘画;然后改进功能,让用户用手机拍摄照片,并在照片上进行绘画。回顾历史,早在 20 世纪 70 年代,“油漆桶”这一类绘画应用是最早运行在个人电脑上的应用之一,目的是为了证明个人电脑的潜力。那时候,开发这样一款简单的绘画应用是一项极其复杂的工作,而且绘画效果也不尽人意。但现在,使用 App Inventor,任何人都可以快速地创建一个有趣的绘画应用,这也正是创建 2D 游戏的起点。

{%}

如图 2-1 所示,“油漆桶”应用能实现下列目标:

  • 用手指在虚拟的油漆桶中蘸取绘画所需的颜色;

  • 用手指在手机屏幕上画线;

  • 用手指触碰手机屏幕画圆;

  • 点击画布下方的按钮来擦净画布;

  • 点击画布下方的按钮来改变画笔的尺寸;

  • 用相机拍摄照片,并在照片上绘画。

图 2-1:“油漆桶”应用

2.1 学习要点

本章涵盖了以下内容:

  • 使用画布组件来绘制图画;

  • 处理设备屏幕上的触摸及拖动事件;

  • 使用布局组件来控制屏幕的外观;

  • 使用带有参数的事件处理程序;

  • 定义变量来保存某些状态,如用户选定的画笔尺寸。

2.2 准备开始

登录 App Inventor 网站(http://ai2.appinventor.mit.edu/),创建一个名为“油漆桶”的新项目。连接测试设备或模拟器,为实时测试做好准备。相关的帮助信息请参见 http://appinventor.mit.edu/explore/ai2/setup

在正式开始之前,在设计视图右侧的属性面板中,将“Screen1”的标题属性修改为“油漆桶”。在测试设备上可以立即看到这一改变:应用的标题栏将显示“油漆桶”。

这样做是否会导致项目名称与屏幕标题混淆呢?别担心!在 App Inventor 中有三个非常重要的名称。

  • 项目名称:同时也是应用发布时所使用的名称。提示:要想修改项目名称,可以点击菜单“项目→另存项目”,为原有项目赋予新的名称,同时原有项目依然得以保留。

  • 组件名称:一般的组件名称都可以修改,但 Screen1 例外。在当前版本中不能修改初始屏幕的名称。

  • 屏幕标题:出现在设备的标题栏中,是屏幕的标题属性。默认值是 Screen1,如在第 1 章“你好猫咪”应用中所见到的,可以随意修改它,例如我们刚刚将它改为“油漆桶”。

2.3 设计组件

“油漆桶”应用中将会用到以下组件。

  • 三个按钮组件用来选择画笔颜色:红、蓝、绿。用一个水平布局组件来放置这三个按钮。

  • 一个按钮组件用来充当橡皮擦,另外两个按钮组件用来改变画笔的大小,还有一个用来打开相机拍照。

  • 一个画布组件。顾名思义,用户可以在上面绘画。画布组件具有背景图片属性,将其设置为第 1 章“你好猫咪”应用中的 kitty.png。稍后还可以将背景图片设置为用户拍摄的照片。

2.3.1 创建颜色按钮

首先,按照以下提示创建三个颜色按钮。

(1) 拖一个按钮组件到工作区域的预览窗口中,设置其显示文本属性为“红”,设置背景颜色属性为红色。

(2) 在组件列表中选中按钮 1(可能已经被选中),点击重命名按钮,将组件名称改为 RedButton。注意组件名称中不允许有空格,因此通常将组件名称中每个单词的首字母大写 1

1在本书的后续章节中,组件及变量的命名都将尽可能采用中文,以便初学者可以用母语阅读程序、理解其中的逻辑,并最终养成自己的逻辑思维能力。——译者注

(3) 同样,创建另外两个按钮,分别命名为 BlueButton 和 GreenButton,将它们放在 RedButton 下方。对照图 2-2,检查一下你的操作结果。

{%}

图 2-2:工作区域的预览窗口中显示了创建的三个按钮

注意:在项目中,建议为组件起一个有意义的名称,而不是像“你好猫咪”应用中那样采用默认的名称。有意义的名称增强了程序的可读性,尤其是在切换到编程视图时,将依据名称来区分不同的组件。本书中约定组件的命名方法:组件名称必须以组件类型结尾,如 RedButton。2

2在编程技术中,这种为变量或组件命名的方法被称为骆驼命名法。首字母就像驼峰一样突起,以便于阅读代码。采用汉字为变量或组件命名时,没有这样的效果。——译者注

 测试:如果你还没有连接测试设备,那么现在做好连接,然后查看一下应用在设备上的表现。

2.3.2 使用布局组件改善布局

现在三个按钮排成一列,但我们希望它们能排成一行,如图 2-3 所示。为此,使用水平布局组件来实现组件的水平排列。

(1) 在组件面板的组件布局类中,拖出水平布局组件,放在按钮下方。

(2) 在属性面板中,设置水平布局组件的宽度为“充满”,以便组件在水平方向上占满整个屏幕。

(3) 依次将三个按钮移动到水平布局组件中。注意,当你拖曳按钮时,会看到一条蓝色竖线,提示按钮将会被放置在什么地方。

{%}

图 2-3:水平布局内的三个按钮

此时查看组件列表,你会发现三个按钮缩进排列在水平布局组件之下。这表明三个按钮组件是水平布局组件的次一级组件。同时注意,所有的组件都缩进排在 Screen1 之下。

你也可以让三个按钮在屏幕上居中,只要设定水平布局组件的水平对齐属性为“居中”即可。3

3建议将三个颜色按钮的字号属性改为 20,勾选粗体属性,并将红、蓝两个按钮的文本颜色属性改为白色,以便于查看。——译者注

 测试:在测试设备的屏幕上方,你会看到三个按钮排列成一行,不过看起来与预览窗口中的样子略有不同。例如,在预览窗口中可见的水平布局组件的轮廓线,在测试设备上则不可见。

通常,可以利用布局组件来创建简单的垂直、水平或表格布局。不过,也可以通过逐层嵌套布局组 件的方式来创建更加复杂的布局。

2.3.3 添加画布

下面对画布组件进行设置,具体步骤如下。

(1) 打开组件面板中的“绘图动画”类组件,将其中的画布组件拖放到预览窗口中,改名为“画布”。宽度设为“充满”,以便画布在水平方向上可以充满整个屏幕。高度设为 300 像素,以便在画布下方还能够容纳两行按钮。

(2) 如果你已经完成了第 1 章的课程,则已经下载了 kitty.png 文件;如果尚未下载该文件,可以从这里下载:http://appinventor.org/bookFiles/HelloPurr/kitty.png

(3) 将画布的背景图片设置为 kitty.png:在设计视图的属性面板中,背景图片的默认设置为“无”。点击“无”及“上传文件”按钮来添加 kitty.png 文件。

(4) 将画布的画笔颜色属性设置为红色,以便当用户刚启动应用但尚未点击颜色按钮时,画笔的默认颜色为红色。对照图 2-4 检查一下你的操作。

{%}

图 2-4:将画布组件的背景图片设置为猫咪的图片

2.3.4 添加底部按钮及照相机组件

(1) 从组件面板中拖出另一个水平布局组件,放在画布下方。再拖两个按钮组件放在这个水平布局组件中。将第一个按钮改名为“拍照按钮”,显示文本属性设置为“拍照”;第二个按钮改名为“擦除按钮”,显示文本属性设置为“擦除”。

(2) 再从组件面板中拖两个按钮组件到水平布局组件中,放在擦除按钮后面。

(3) 两个按钮分别命名为“大圆按钮”“小圆按钮”,显示文本属性也分别设为“大圆”“小圆”。

(4) 从组件面板的多媒体类中拖出一个照相机组件放在预览窗口中,它将落在非可视组件区。

到此为止,应用外观已经设置完成,如图 2-5 所示。

{%}

图 2-5:“油漆桶”应用的完整用户界面

 测试:在设备上检查一下应用。猫的图片上方是否有一行按钮?底部的按钮是否正常显示?

2.4 为组件添加行为

下一步将定义组件的行为。编写一个绘画程序的难度似乎是难以想象的,但 App Inventor 已经承担了大部分繁重的工作:借助简单易用的块语言,不仅可以处理用户的触摸及拖动事件,也可以实现绘画及拍照功能。

在设计视图中,已经添加了叫作“画布”的画布组件。画布组件可以侦测到触摸及拖动事件。对触摸事件编程,实现画圆功能,这样当用户的手指触摸到画布时,将在被触摸的位置画出一个圆形;对拖动事件编程,可以实现画线功能,即当用户的手指在画布上划过时,沿途会画出一条线。还可以通过对按钮的点击事件编程,以改变画笔的颜色,清除画布,将画布的背景图片修改为相机拍摄到的照片。

2.4.1 在触摸事件中画圆

首先处理触摸事件:当用户触摸画布时,在接触点绘制一个圆形。

(1) 在编程视图中,打开画布的代码块抽屉,拖出“当画布被触摸时”块。该代码块有三个参数:x 坐标、y 坐标及碰到任意精灵,如图 2-6 所示。其中的 xy 坐标提供了接触点的位置信息。

{%}

图 2-6:事件中携带了触碰点的位置信息

 提示:在第 1 章“你好猫咪”应用中,我们已经熟悉了按钮点击事件,但对于画布组件的事件还很陌生。按钮点击事件很简单,不附带任何其他信息;但有些事件则不然,它们附带了与事件有关的“参数”信息。在画布的触摸事件中,提供了触摸点在画布上的 xy 坐标,以及是否碰到了位于画布中的对象(在 App Inventor 中被称作“精灵”),但是在第 3 章之前我们还不需要使用碰到任意精灵参数。本章我们只用到了触碰点的坐标,利用坐标来绘制圆形。

(2) 从画布的代码块抽屉中拖出“让画布画圆”块,放在画布的触摸事件处理程序中,如图 2-7 所示。

{%}

图 2-7:当用户点击画布时画一个圆

在“让画布画圆”块的右侧有四个插槽,前三个插槽需要填入参数:圆心 x 坐标、圆心 y 坐标、半径,其中圆心 xy 坐标用于指定绘制圆形的位置,半径用于指定圆的大小。图中的代码有些令人困惑,有两组 xy 坐标,这里要区分清楚:触摸事件中的 xy 坐标表示接触点的位置(已知);而画圆命令块中的 xy 坐标插槽,用于设定绘制圆形的圆心位置(待定)。我们恰好要以用户的触摸点为圆心绘制圆形,因此可以从触摸事件中取得 xy 坐标的值,作为画圆命令的参数,填充到代码块的插槽中。4

4“让画布画圆”块中的第四个插槽是最近的版本中新增加的参数——实心。它的默认值是“真”,表示将绘制实心圆。如果设为假,将绘制空心圆。——译者注

 提示:可以从触摸事件块中提取事件的参数,方法是将鼠标悬停在参数上,如图2-8所示。

{%}

图 2-8:鼠标悬停在事件参数上,可以取得事件的参数值

(3) 从事件中拖出“x 坐标”“y 坐标”块,并将它们填充到画圆命令块的插槽中,如图 2-9 所示。

{%}

图 2-9:圆心的位置已经确定,但还需要设定半径的大小

(4) 现在需要设定圆的半径。半径的单位为像素,像素是显示屏幕上所能绘制的最小的点。此时设半径为 5 像素:在编程视图工作区的空白处输入数字 5,按回车,将会自动创建一个数字块,然后将数字块 5 拖入半径插槽。当数字块 5 填入到半径插槽后,屏幕左下角的黄色三角形处的数字变为 0,因为此时所有的插槽都已经被填满。图 2-10 就是完整的触摸事件处理程序。

{%}

图 2-10:当用户触摸屏幕时,将以触摸点为圆心绘制一个半径为 5 的实心圆

 提示:在编程视图的工作区中输入 5,然后回车,这种操作叫作输入块(typeblocking)。系统会根据你输入的字符,显示与该字符相匹配的一系列块;如果输入的是数字,那么将创建一个数字块。

 

 看看测试设备上都有什么。触碰画布,手指碰过的地方会留下一个圆点。如果在设计视图中将画布的画笔颜色属性设置为红色,那么圆点也是红色(否则应该是默认的黑色)。

2.4.2 在拖动事件中画线

下面添加拖动事件处理程序。先看一下触摸事件与拖动事件之间的区别。

  • 触摸事件:手指在画布上放下再抬起,其间手指没有移动。

  • 拖动事件:手指在画布上放下,手指与屏幕保持接触并移动。

在绘画程序中,手指在屏幕上拖动,将沿着手指移动的路径绘制出一条线。这条线实际上是由无数个微小的直线(线段)构成的:手指每次微小的移动,都将从手指所在的最后一个位置开始,到手指的当前位置为止,绘制一个微小的线段。

(1) 从画布的代码块抽屉中拖出拖动事件处理程序块,如图 2-11 所示。拖动事件携带了以下参数。

  • 起点 X 坐标、起点 Y 坐标:手指开始拖动时所在的位置(整条线的起点)。

  • 邻点 X 坐标、邻点 Y 坐标:手指的上一个位置(微小线段的起点)。

  • 当前 X 坐标、当前 Y 坐标:手指的当前位置(微小线段的终点)。

  • 拖到任意精灵:布尔值,如果用户拖动过程中碰到过精灵,则其值为真。本章不会用到这个参数。

{%}

图 2-11:比起触摸事件,拖动事件携带了更多的参数

(2) 从画布的代码块抽屉中拖出“让画布画线”块,填充到拖动事件块中,如图 2-12 所示。

{%}

图 2-12:添加画线功能

画线命令块有四个参数,分别确定微小线段的起点及终点坐标,其中起点为 ( 第一点 x 坐标,第一点 y 坐标 ),终点为 ( 第二点 x 坐标,第二点 y 坐标 )。你能确定每个参数中需要填入什么值吗?记住,当手指在画布上拖动时,拖动事件将被调用很多次:在应用中,手指的每次微小的移动都会绘制出一个微小线段,从 ( 邻点 X 坐标,邻点 Y 坐标 ) 到 ( 当前 X 坐标,当前 Y 坐标 )。

(3) 从拖动事件中拖出你需要的参数。将邻点 X 坐标、邻点 Y 坐标分别填充到第一点 x 坐标、第一点 y 坐标中,然后,将当前 X 坐标、当前 Y 坐标分别填充到第二点 x 坐标、第二点 y 坐标中,如图 2-13 所示。

{%}

图 2-13:当用户在屏幕上拖动时,从上一点到当前点绘制线段

 测试:在设备上测试一下刚刚设定的行为。在屏幕上随意拖动手指,画出直线及曲线。触碰屏幕画圆。

2.4.3 改变颜色

应用已经实现了画线功能,但现在只能画红线。下面编写颜色按钮的事件处理程序,以便用户可以改变画笔的颜色。同样设置擦除按钮程序,以便用户可以清除画布并重新开始。

在编程视图中完成以下操作。

(1) 打开 RedButton 的代码块抽屉,拖出“当 RedButton 被点击时”块。

(2) 打开画布的代码块抽屉。拖出“设画布的画笔颜色”块(可能需要滚动代码块列表以便在列表的后面找到它),并把它放在点击事件块中“执行”的位置。

(3) 打开内置块分组中的颜色抽屉,拖出红色块,将其填入到设置画笔颜色块的插槽中。

(4) 重复步骤 1~3,设置蓝色及绿色按钮。

(5) 最后设置擦除按钮。从擦除按钮的抽屉中拖出“当擦除按钮被点击时”块。再从画布抽屉里拖出“清除画布”块,并将其放在擦除按钮点击事件块中。确认所有块显示如图 2-14 所示。

{%}

图 2-14:点击颜色按钮改变画笔颜色,单击擦除按钮清空画布

 测试:点击每个颜色按钮,看看是否能够画出不同颜色的圆点;点击擦除按钮,看看画布是否被清空。

2.4.4 让用户拍照片

App Inventor 应用可以调用安卓设备的强大功能,包括相机功能。为了增加应用的趣味性,用户可以打开相机拍摄照片,并将照片设置为画布的背景。

照相机组件有两个关键的代码块:“让照相机拍摄照片”块用来启动设备上的拍照程序;拍照完成时 将触发“照相机完成拍摄”事件。在完成拍摄事件处理程序中,可以将刚刚拍摄的照片设置为画布 的背景图片。

(1) 打开拍照按钮的代码块抽屉并拖出按钮点击事件块。

(2) 从照相机 1 的抽屉中拖出“让照相机 1 拍摄照片”块,放在拍照按钮的点击事件处理程序中。

(3) 从照相机 1 的抽屉中拖出完成拍摄事件块。

(4) 从画布抽屉中拖出“设画布的背景图片”块,放在照相机 1 拍摄完成事件处理程序中。

(5) 照相机 1 拍摄完成事件中有一个名为“图片地址”的参数,代表刚刚拍摄完成的照片。从事件块中取出“图片地址”块,并填充到“设画布的背景图片”块的插槽中。

所有的代码块如图 2-15 所示 。

{%}

图 2-15:将拍好的照片设置为画布的背景图片

 测试:在设备上点击拍照按钮并拍摄照片,猫的图片会变成你拍的照片。你可以在照片上进行绘画。(在 Wolber 教授的照片上绘画是学生们的一大乐事,如图 2-16 所示。)

{%}

图 2-16:带有 Wolber 教授涂鸦照片的“油漆桶”应用

2.4.5 改变画笔的大小

在画布上画圆时,圆的大小取决于画圆命令中的半径参数;现在半径的值是 5。如果想改变圆的大小,需要为半径设置不同的值。试试看将此前的 5 改为 10,然后在测试设备上观察结果。

但问题是,应用的用户只能用开发者设置的半径参数画圆。如何让用户来改变圆的大小呢?为此我们来修改程序,这样不只是程序员,用户也能改变圆的大小:当用户点击大圆按钮时,圆的半径设为 8;当点击小圆按钮时,半径设为 2。

我们要用不同的半径画圆,但应用如何知道我们要用哪个值呢?必须通知应用我们选定的值,而应用必须以某种方式记住或保存这个值,这样才能在需要的时候使用这个值。之前我们所使用的值,要么设定为组件的属性(如画布的画笔颜色),要么用固定的数字块(如数字 5),而现在应用需要记住一些属性之外的、不是固定不变的东西,这就需要定义一个变量。变量是一个存储单元,可以把它想象成一个容器,里面存储着可变的数据,如画笔的大小(有关变量的详细信息,请参见第 16 章)。

让我们先来定义“画笔宽度”这个变量。

(1) 在编程视图的内置块分组中,从变量抽屉中拖出一个“声明全局变量”块。将“我的变量”改为“画笔宽度”。

(2) 注意,这个“声明全局变量画笔宽度”块有一个开放的插槽,可以在这里设定变量的初始值。初始值指的是应用启动时变量的默认值(编程术语也叫“变量初始化”)。在本应用中,用数字块 2 来初始化变量画笔宽度(创建块“2”的方法有两种:在工作区直接输入“2”,然后回车;或从数学抽屉中拖出“0”块,将 0 改为 2),将其填充到“声明全局变量”块的插槽中,如图 2-17 所示。

{%}

图 2-17:将全局变量画笔宽度的初始值设为 2

2.4.6 使用变量

下一步,我们要修改画布的触摸事件处理程序,将其中画圆命令中的半径参数由原来的固定值替换为变量画笔宽度。(乍一看我们使用的半径参数还是预先设定的固定值 2,并非一个可变值,不过别急,稍后我们将改变画笔宽度的值,因此也就改变了所画圆的大小。)

(1) 从“声明全局变量画笔宽度”块中拖出一个“画笔宽度”块,它提供了变量的值。

(2) 来看“让画布画圆”块,将数字块“5”拖出插槽并扔进垃圾桶,用“画笔宽度”块来替换(见图 2-18)。当用户触摸画布时,应用将根据画笔宽度的大小来确定圆的半径。

{%}

图 2-18:圆的半径取决于变量“画笔宽度”中保存的值

2.4.7 修改变量的值

现在,应用将根据画笔宽度的值来决定绘制圆的大小。不过,画笔宽度既然是变量,就要依据用户的选择进行改变。通过编写小圆按钮及大圆按钮的点击事件处理程序来实现此功能。

(1) 从小圆按钮的抽屉中拖出点击事件处理程序。再从变量抽屉中拖出第三个块——“设……为”块,点击小倒三角形周围区域打开下拉列表(此时只有一项),选择“global 画笔宽度”,并将其填充到小圆按钮点击事件中。最后,创建一个数字块“2”,并将其填充到“设 global 画笔宽度”块中。

(2) 创建另一个类似的大圆按钮点击事件处理程序,设置画笔宽度为 8。这两个事件处理程序显示在编程视图中,如图 2-19 所示。

{%}

图 2-19:点击按钮改变画笔宽度,画圆的大小也将随之改变

 提示:“设 global 画笔宽度”中的“global”(全局)指的是该变量适用于程序中所有的事件处理程序,因此被称为全局变量。与全局变量相对应的是局部变量,适用于程序的特定部分(详见第 21 章)。

 

 测试:尝试单击大圆按钮、小圆按钮,然后在画布上触碰,所绘圆点的大小是否不同?画线呢?线没有变化,因为只有画圆命令块使用了变量画笔宽度。在此基础上,能否考虑修改代码块,以使画笔宽度对画线也同样有效?(提示:画布有一个画笔线宽属性。)

2.5 完整的“油漆桶”应用

图 2-20 中列出了应用中的全部代码块。

{%}

图 2-20:完整的“油漆桶”应用

2.6 改进

可以考虑做以下改进。

  • 用户界面中没有提供更多的当前状态信息(如画笔大小或颜色,只能通过画图来了解这些信息)。修改应用,向用户显示当前的状态信息。

  • 在设置画笔线宽时,让用户有更多的选择,而不仅限于 2 和 8。使用数字滑动条组件可以实现这一功能。

2.7 小结

本章涵盖了如下内容。

  • 画布组件:用于在其中绘画,也可以感知触摸及拖动事件,可以利用这些事件来实现绘画功能。

  • 使用布局组件来安置多个组件,而不是将它们摞在一起。

  • 有些事件处理程序携带了与事件有关的信息,例如触摸事件中携带了触摸点的坐标,这些信息用参数来表示。在使用带参数的事件处理程序时,App Inventor 在事件块中为每个参数派生出两个块,分别用来读取和设置这些参数。

  • 创建变量可以使用变量抽屉中的“声明全局变量(我的变量)”块。变量可以让应用记住那些没有被保存为组件属性值的信息,如画笔的大小。

  • 对于程序中声明的每一个全局变量,App Inventor 会自动派生出两个块:“global+ 变量名”块用来获取变量的值(读操作),而“设 global+ 变量名”块用来设置或修改变量的值(写操作)。将鼠标悬停在声明变量块的变量名称上,就可以获得这两个块。

本章介绍了如何利用画布组件来实现一个绘画程序。你也可以用它来编写某些 2D 游戏中的动画。更多信息请参见第 3 章的打地鼠游戏、第 5 章的瓢虫快跑游戏以及第 17 章中关于动画的讨论。

目录

  • 版权声明
  • O'Reilly Media, Inc. 介绍
  • 前言
  • 第一部分 App Inventor 2 教程
  • 第 1 章 你好猫咪
  • 第 2 章 油漆桶
  • 第 3 章 打地鼠
  • 第 4 章 开车不发短信
  • 第 5 章 瓢虫快跑
  • 第 6 章 巴黎地图旅游
  • 第 7 章 安卓,我的车在哪儿
  • 第 8 章 总统问答
  • 第 9 章 木琴
  • 第 10 章 出题与答题
  • 第 11 章 广播中心
  • 第 12 章 遥控机器人
  • 第 13 章 亚马逊掌上书店
  • 第二部分 Inventor 指南
  • 第 14 章 理解应用的结构
  • 第 15 章 软件工程与应用测试
  • 第 16 章 应用的存储
  • 第 17 章 创建动画应用
  • 第 18 章 程序中的决策
  • 第 19 章 数据列表编程
  • 第 20 章 循环
  • 第 21 章 定义过程与代码复用
  • 第 22 章 数据库
  • 第 23 章 传感器
  • 第 24 章 网络通信
  • 关于作者