第二章 油漆桶
本章介绍画布组件,用它来绘制简单的二维(2D)图形。具体目标是创建一个名为油漆桶的应用,用户可以用不同的颜色在手机屏幕上绘画;然后改进功能,让用户用手机拍摄照片,并在照片上进行绘画。回顾历史,早在20世纪70年代,油漆桶这一类的绘画应用是最早运行在个人电脑上的应用之一,目的是为了证明个人电脑的潜力。那时候,开发这样一款简单的绘画应用是一项极其复杂的工作,而且绘画效果也差强人意。但现在,使用App Inventor,任何人都可以快速地创建一个有趣的绘画应用,这也正是创建2D游戏的起点。
如图2-1,油漆桶应用将实现下列目标:
- 用手指在虚拟的油漆桶中蘸取绘画所需的颜色;
- 用手指在手机屏幕上画线;
- 用手指触碰手机屏幕画圆;
- 点击画布下方的按钮来擦净画布;
- 点击画布下方的按钮来改变画笔的尺寸;
- 用相机拍摄照片,并在照片上绘画。 学习内容
本章涵盖了以下内容:
- 使用画布组件来绘制图画;
- 处理设备屏幕上的触摸及拖动事件;
- 使用布局组件来控制屏幕的外观;
- 使用带有参数的事件处理程序;
- 定义变量,来保存某些状态,如用户选定的画笔尺寸。
准备开始
登陆App Inventor网站,创建一个名为“油漆桶”的新项目,连接测试设备(或模拟器),为实时测试做好准备(相关的帮助信息请参见http://appinventor.mit.edu/explore/ai2/setup)。 在正式开始之前,在设计视图右侧的属性面板中,将“Screen1”的标题属性修改为“油漆桶”。在测试设备上可以立即看到这一改变:应用的标题栏将显示“油漆桶”。 这样做是否会导致项目名称与屏幕标题的混淆呢?别担心!在App Inventor中有三个非常重要名称:
- 项目名称:同时也是应用发布时所使用的名称。提示:想修改项目名称,可以点击菜单“项目→另存项目”,就可以为原有项目赋予新的名称,同时原有项目依然得以保留;
- 组件名称:一般的组件名称都可以修改,但Screen1例外,在当前版本中不能修改初始屏幕的名称;
- 屏幕标题:出现在设备的标题栏中,是屏幕的标题属性,默认值是Screen1,如第一章你好猫咪中所见到的,可以随意修改它,例如我们刚刚将它改为“油漆桶”。
设计组件
油漆桶应用中将会用到以下组件:
- 三个按钮组件用来选择画笔颜色:红、蓝、绿,用一个水平布局组件来放置这三个按钮;
- 一个按钮组件用来充当橡皮擦,另外两个按钮组件用来改变画笔的大小,还有一个用来打开照相机拍照;
- 一个画布组件,顾名思义,用户可以在上面绘画,画布组件具有背景图片属性,将其设置为第一章你好猫咪中的kitty.png,稍后还可以将背景图片设置为用户拍摄的照片。
创建颜色按钮
首先按照以下提示创建三个颜色按钮:
- 拖一个按钮组件到工作区域的预览窗口中,设置其显示文本属性为“红”,背景颜色属性设为红色;
- 在组件列表中选中按钮1(可能已经被选中),点击重命名按钮,将组件名称改为RedButton。注意组件名称中不允许有空格,因此通常将组件名称中每个单词的首字母大写(在本书的后续章节中,组件及变量的命名都将尽可能采用中文,以便初学者可以用母语阅读程序、理解其中的逻辑,并最终养成自己的逻辑思维能力。——译者注)。
- 同样,创建另外两个按钮,分别命名为BlueButton和GreenButton,将它们放在RedButton下方。对照图2-2,检查一下你的操作结果。
注意:在项目中,建议为组件起一个有意义的名称,而不是像你好猫咪中那样采用默认的名称。有意义的名称增加了程序的可读性,尤其是在切换到编程视图时,将依据名称来区分不同的组件。本书中约定组件的命名方法:组件名称必须以组件类型结尾(如RedButton) 。
测试:如果你还没有连接测试设备,那么做好连接,然后来查看一下应用在设备(如果已经连接)上的表现。
使用布局组件改善布局
现在三个按钮排成一列纵队,我们希望它们能排成一行,如图2-3所示,使用水平布局组件来实现组件的水平排列:
- 在组件面板的组件布局类中拖出水平布局组件,放在按钮下方;
- 在属性面板中,设置水平布局组件的宽度为“充满”,以便组件在水平方向上占满整个屏幕;
- 依次将三个按钮移动到水平布局组件中。注意,当你拖拽按钮时,会看到一条蓝色竖线,提示按钮将会被放置在什么地方。
此时查看组件列表,你会发现三个按钮缩进排列在水平布局组件之下,这表明三个按钮组件是水平布局组件的次一级组件。同时注意到所有的组件都缩进排在Screen1之下。
你也可以让三个按钮在屏幕上居中,只要设定水平布局组件的水平对齐属性为“居中”即可。(建议将三个颜色按钮的字号属性改为20,勾选粗体属性,并将红、蓝两个按钮的文本颜色属性改为白色,以便于查看。——译者注)
测试:在测试设备上,你会看到三个按钮排列成一行,看起来与预览窗口中的样子略有不同。如,在预览窗口中可见的水平布局组件的轮廓线,在测试设备上则不可见。
通常利用布局组件来创建简单的垂直、水平或表格布局,不过,也可以通过逐层嵌套布局组件的方式来创建更加复杂的布局。
添加画布
下面对画布组件进行设置,具体步骤如下:
- 打开组件面板中的“绘图动画”类组件,将其中的画布组件拖放到预览窗口中,改名为“画布”,宽度设为“充满”,以便画布在水平方向上可以充满整个屏幕;高度设为300像素,以便在画布下方还能够容纳2行按钮。
- 假设你已经完成了第一章的课程,已经下载了kitty.png;如果尚未下载该文件,可以从这里下载:http://appinventor.org/bookFiles/HelloPurr/kitty.png。
- 将画布的背景图片设置为kitty.png:在设计视图的属性面板中,背景图片的默认设置为“无”,点击“无”及“上传文件”按钮来添加kitty.png文件;
- 将画布的画笔颜色属性设置为红色,以便当用户刚启动应用但尚未点击颜色按钮时,画笔为红色。对照图2-4检查一下你的操作。
添加底部按钮及照相机组件
- 从组件面板中拖出第二个水平布局组件,放在画布下方,设置其宽度属性为充满,水平对齐属性为“居中”;再拖两个按钮放在第二个水平布局组件中;将第一个按钮改名为“拍照按钮”,显示文本属性也设置为“拍照”;第二个按钮改名为“擦除按钮”,显示文本属性也设置为“擦除”;
- 再拖两个按钮组件到第二个水平布局组件中,放在擦除按钮后面;
- 两个按钮分别命名为“大圆按钮”、“小圆按钮”,显示文本属性也分别设为“大圆”、“小圆”;
- 从组件面板的多媒体类中拖出一个照相机组件放在预览窗口中,它将落在非可视组件区。
到此为止,应用外观已经设置完成,如图2-5所示。
测试:在设备上检查一下应用,猫的图片上方是否有一行按钮?底部的按钮是否正常显示?
为组件添加行为
下一步将定义组件的行为。编写一个绘画程序的难度似乎是难以想象的,但无疑App Inventor已经承担了大部分繁重的工作:借助于简单易用的块语言, 不仅可以处理用户的触摸及拖动事件,也可以实现绘画及拍照功能。
在设计视图中,已经添加了叫做“画布”的画布组件,画布组件可以侦测到触摸及拖动事件,对触摸事件编程,来实现画圆功能,当用户的手指触摸到画布时,将在被触摸的位置画上一个圆形;对拖动事件编程,可以实现画线功能,即,当用户的手指在画布上划过时,沿途会画出一条线。还可以通过对按钮的点击事件编程,来改变画笔的颜色、大小、清除画布以将画布的背景图片修改为相机拍摄到的照片。
在触摸事件中画圆
首先处理触摸事件:当用户触摸画布时,在接触点绘制一个圆形:
(1) 在编程视图中,打开画布的代码块抽屉,拖出“当画布被触摸时”块,该代码块有三个参数:x坐标、y坐标及碰到任意精灵,如图2-6所示。其中的x、y坐标提供了接触点的位置信息。
提示:在第一章你好猫咪的应用中,我们已经熟悉了按钮点击事件,但对于画布组件的事件还很陌生。按钮点击事件的很简单,不附带任何其他信息;但有些事件则不然,它们附带了与事件有关的“参数”信息。在画布的触摸事件中,提供了触摸点在画布上的x、y坐标,以及是否碰到了位于画布中的对象(在App Inventor中被称作“精灵”),但是在第三章之前我们还不需要使用碰到任意精灵参数。本章我们只用到了触碰点的坐标,利用坐标来绘制圆形。
(2) 从画布的代码块抽屉中拖出“让画布画圆”块,放在画布的触摸事件处理程序中,如图2-7所示;
在“让画布画圆”块的右侧有四个插槽,其中的前三个插槽需要填入参数:圆心x坐标、圆心y坐标、半径,其中圆心x、y坐标用于指定绘制圆形的位置,半径用于指定圆的大小。图中的代码有些令人困惑,有两组x、y坐标,这里要区分清楚:触摸事件中的x、y坐标表示接触点的位置(已知);而画圆命令块中的x、y坐标插槽,用于设定绘制圆形的圆心位置(待定)。我们恰好要以用户的触摸点为圆心绘制圆形,因此可以从触摸事件中取得x、y坐标的值,作为画圆命令的参数,填充到代码块的插槽中。(“让画布画圆”块中的第四个插槽是最近的版本中新增加的参数——实心,它的默认值是“真”,表示将绘制实心圆,如果设为假,将绘制空心圆。——译者注)
提示:可以从触摸事件块中提取事件的参数,方法是将鼠标悬停在参数上。如图2-8所示。
(3) 从事件中拖出“x坐标”、“y坐标”块,并将它们填充到画圆命令块的插槽中,如图2-9所示。
(4) 现在需要设定圆的半径,半径的单位为像素,像素是显示屏幕上所能绘制的最小的点,此时设半径为5个像素:在编程视图工作区的空白处输入数字5,再按回车,将会自动创建一个数字块,将数字块5拖入半径插槽。当数字块5填入到半径插槽后,屏幕左下角的黄色三角形处的数字变为0,因为此时,所有的插槽都已经被填满。如图2-10就是完整的触摸事件处理程序。
提示:在编程视图的工作区中输入5然后回车,这种操作叫做输入块(typeblocking),系统会根据你输入的字符,显示与该字符相匹配的一系列块;如果输入的是数字,那么将创建一个数字块。
测试:看看测试设备上都有什么。触碰画布,手指碰过的地方会留下一个圆点。如果在设计视图中将画布的画笔颜色属性设置为红色,那么圆点也是红色(否则应该是默认的黑色)。
在拖动事件中画线
下面添加拖动事件处理程序,先看一下触摸事件与拖动事件之间的区别:
- 触摸事件:手指在画布上放下再抬起,其间手指没有移动。
- 拖动事件:手指在画布上放下,手指与屏幕保持接触并移动。
在绘画程序中,手指在屏幕上拖动,沿着手指移动的路径,将绘制出一条线,而这条线实际上是由无数个微小的直线(线段)构成:手指每次微小的移动,都将从手指所在的最后一个位置开始,到手指的当前位置为止,绘制一个微小的线段。
(1) 从画布的代码块抽屉中拖出拖动事件处理程序块,如图2-11所示;拖动事件携带了以下参数:
- 起点X坐标、起点Y坐标:手指开始拖动时所在的位置(整条线的起点);
- 邻点X坐标、邻点Y坐标:手指的上一个位置(微小线段的起点);
- 当前X坐标、当前Y坐标:手指的当前位置(微小线段的终点);
- 拖到任意精灵:布尔值,如果用户拖动过程中碰到过精灵,则其值为真。本章不会用到这个参数。
(2) 从画布的代码块抽屉中拖出“让画布画线”块,填充到拖动事件块中,如图2-12所示。
(3) 画线命令块有四个参数,分别确定微小线段的起点及终点坐标,其中起点为(第一点X坐标,第一点Y坐标),终点为(第二点X坐标,第二点Y坐标)。你能确定每个参数中需要填入什么值吗?记住,当手指在画布上拖动时,拖动事件将被调用了很多次:在应用中,手指的每次微小的移动都会绘制出一个微小线段,从(邻点X坐标, 邻点Y坐标)到(当前X坐标, 当前Y坐标)。
(4) 从拖动事件中拖出你需要的参数,将邻点X坐标、邻点Y坐标分别填充到第一点x坐标、第一点y坐标中,然后,将当前X坐标、当前Y坐标分别填充到第二点x坐标、第二点y坐标中,如图2-13所示。
测试:在设备上测试一下刚刚设定的行为:在屏幕上随意拖动手指,画出直线及曲线;触碰屏幕画一个圆。
改变颜色
应用已经实现了画线功能,但现在只能画红线。下面编写颜色按钮的事件处理程序,以便用户可以改变画笔的颜色;同样设置擦除按钮程序,以便用户可以清除画布并重新开始。 在编程视图中完成以下操作:
- 打开RedButton的代码块抽屉,拖出“当RedButton被点击时”块;
- 打开画布的代码块抽屉。拖出“设画布的画笔颜色”块(可能需要滚动代码块列表以便在列表的后面找到它),并把它放在点击事件块中“执行”的位置;
- 打开内置块分组中的颜色抽屉,拖出红色块,将其填入到设置画笔颜色块的插槽中;
- 重复步骤1-3,设置蓝色及绿色按钮;
- 最后设置擦除按钮。从擦除按钮的抽屉中拖出“当擦除按钮被点击时”块。再从画布抽屉里拖出“清除画布”块,并将其放在擦除按钮点击事件块中。确认所有块显示如图2-14所示。
图2-14 点击颜色按钮改变画笔颜色,单击擦除按钮清空画布
测试:点击每个颜色按钮,看看是否能够画出不同颜色的圆点;点击擦除按钮,看看画布是否被清空。
让用户拍照片
App Inventor应用可以调用安卓设备的强大功能,包括相机功能。为了增加应用的趣味性,用户可以打开相机拍摄照片,并将照片设置为画布的背景。
照相机组件有两个关键的代码块:“让照相机拍摄照片”块用来启动设备上的拍照程序;拍照完成时将触发“照相机完成拍摄”事件。在完成拍摄事件处理程序中,可以将刚刚拍摄的照片设置为画布的背景图片。1. 打开拍照按钮的代码块抽屉并拖出按钮点击事件块;2. 从照相机1的抽屉中拖出“让照相机1拍摄照片”块,放在拍照按钮的点击事件处理程序中;3. 从照相机1的抽屉中拖出完成拍摄事件块;4. 从画布抽屉中拖出“设画布的背景图片”块,放在照相机1拍摄完成事件处理程序中;5. 照相机1拍摄完成事件中有一个名为“图片地址”的参数,代表刚刚拍摄完成的照片,从事件块中取出“图片地址”块,并填充到“设画布的背景图片”块的插槽中。所有的代码块如图2-15所示 。
测试:在设备上点击拍照按钮并拍摄照片,猫的图片变成了你拍的照片。你可以在照片上进行绘画。(在Wolber教授的照片上绘画是学生们的一大乐事,如图2-16。)(Wolber教授是本书的作者之一。)
改变画笔的大小
在画布上画圆时,圆的大小取决于画圆命令中的半径参数,现在半径的值是5,如果想改变圆的大小,需要为半径设置不同的值。试试看将此前的5改为10,然后在测试设备上观察结果。
但问题是,无论开发者如何设置半径参数,应用的用户都只能用某个固定的尺寸画圆。如何让用户来改变圆的大小呢?为此我们来修改程序:当用户点击大圆按钮时,圆的半径设为8,当点击小圆按钮时半径设为2。
我们要用不同的半径画圆,但应用如何知道我们要用哪个值呢?必须通知应用我们选定的值,而应用必须以某种方式记住(或保存)这个值,这样才能在需要的时候使用这个值。之前我们所使用的值,要么设定为组件的属性(如画布的画笔颜色),要么用固定的数字块(如数字5),现在应用需要记住一些属性之外的、不是固定不变的东西,这就需要定义一个变量。变量是一个存储单元,可以把它想象成一个容器,里面存储着可变的数据,如画笔的大小(有关变量的详细信息,请参见本书第16章)。
让我们先来定义一个变量“画笔宽度”:
- 在编程视图的内置块分组中,从变量抽屉中拖出一个“声明全局变量”块。将“我的变量”改为“画笔宽度”;
- 注意,这个“声明全局变量画笔宽度”块有一个开放的插槽,可以在这里设定变量的初始值,初始值指的是应用启动时变量的默认值(编程术语也叫“变量初始化”)。在本应用中,用数字块2来初始化变量画笔宽度,(创建块“2”的方法有两种:在工作区直接输入“2”然后回车;或从数学抽屉中拖出“0”块,将0改为2。)将其填充到“声明全局变量”块的插槽中,如图2-17所示。
使用变量
下一步,我们要修改画布的触摸事件处理程序,将其中画圆命令中的半径参数由原来的固定值替换为变量画笔宽度。(乍一看我们使用的半径参数还是预先设定的固定值2,并非一个可变值,不过别急,稍后我们将改变画笔宽度的值,因此也就改变了所画圆的大小。)
- 从“声明全局变量画笔宽度”块中拖出一个“画笔宽度”块,它提供了变量的值;
- 来看“让画布画圆”块,将数字块“5”拖出插槽并扔进垃圾桶,用“画笔宽度”块来替换(见图2-18)。当用户触摸到画布时,应用将根据画笔宽度的大小来确定圆的半径。
修改变量的值
现在,应用将根据画笔宽度的值,来决定绘制圆的大小,不过,画笔宽度既然是变量,就要依据用户的选择进行改变。 通过编写小圆按钮及大圆按钮的点击事件处理程序来实现此功能:
(1) 从小圆按钮的抽屉中拖出点击事件处理程序;再从变量抽屉中拖出第三个块——“设()为”块,点击小倒三角形周围区域打开下拉列表(此时只有一项),选择“global 画笔宽度”,并将其填充到小圆按钮点击事件中;最后,创建一个数字块“2”,并将其填充到“设 global 画笔宽度”块中。
(2) 创建另一个类似的大圆按钮点击事件处理程序,设置画笔宽度为8。这两个事件处理程序显示在编程视图中,如图2-19所示。
提示: “设global 画笔宽度”中的“global”(全局)指的是该变量适用于程序中所有的事件处理程序,因此被称为全局变量,与全局变量相对应的是局部变量,适用于程序的特定部分(详见第21章)。
测试:尝试单击大圆按钮、小圆按钮,然后在画布上触碰,所绘圆点的大小是否不同?画线呢?线没有变化,因为只有画圆命令块使用了变量画笔宽度。在此基础上,能否考虑修改代码块,以使画笔宽度对画线也同样有效?(提示:画布有一个画笔线宽属性。)
完整的应用:油漆桶
图2-20中列出了应用中的全部代码。
改进
可以考虑做以下改进:
- 用户界面中没有提供更多的当前状态信息(如画笔大小或颜色),只能通过画图来了解这些信息。修改应用,向用户显示当前的状态信息;
- 在设置画笔线宽时,让用户有更多的选择,而不仅限于2和8。使用数字滑动条组件可以实现这一功能。
小结
本章涵盖了如下内容:
- 画布组件:用于在其中绘画,也可以感知触摸及拖动事件,可以利用这些事件来实现绘画功能;
- 使用布局组件来安置多个组件,而不是将它们摞在一起;
- 有些事件处理程序携带了与事件有关的信息,例如触摸事件中携带了触摸点的坐标,这些信息用参数来表示。在使用带参数的事件处理程序时,App Inventor在事件块中,为每个参数派生出两个块,分别用来读取和设置这些参数;
- 创建变量可以使用变量抽屉中的“声明全局变量(我的变量)”块,变量可以让应用记住那些没有被保存为组件属性值的信息,如画笔的大小;
- 对于程序中声明的每一个全局变量,App Inventor会自动派生出两个块:“global+变量名”块用来获取变量的值(读操作),而“设 global+变量名”块用来设置或修改变量的值(写操作)。将鼠标悬停在声明变量块的变量名称上,就可以获得这两个块。 本章介绍了如何利用画布组件来实现一个绘画程序。你也可以用它来编写某些2D游戏中的动画,更多信息请参见第3章的打地鼠游戏、第5章的“瓢虫快跑”游戏,以及第17章中关于动画的讨论。