codecamp

第 2 章 油漆桶

本章介绍Canvas组件,用它来生成简单的二维(2D)图形,目标是创建一个PaintPot(油漆桶)应用,让用户在手机屏幕上绘制图画,并让用户用手机给自己拍照,然后在自己的照片上绘图。回顾历史,早在20世纪70年代,PaintPot是最早运行在个人电脑上的应用之一,目的是为了证明个人电脑的潜力。那时候,开发这样一款简单的绘图应用是一项极其复杂的工作,而且绘图效果也略显粗糙。但现在,使用App Inventor,任何人都可以快速地创建一个有趣的绘图应用,这也是创建2D游戏的起点。

如图2-1,油漆桶应用将实现下列目标:

  • 用手指点取颜色并绘图;

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

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

  • 点击按钮来擦净屏幕;

  • 点击按钮来改变绘制圆点的大小;

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

{%}

{%}

图 2-1 油漆桶应用

学习内容

本章涵盖了以下内容:

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

  • 处理屏幕上的触摸及拖拽事件;

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

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

  • 定义变量,来保存某些状态,如用户绘制的圆点的大小。

准备开始

首先检查测试用的Android设备是否已经为使用App Inventor做好了准备:

  • Android设备中已经安装了“AI伴侣”;

  • 手机的WiFi连接已经打开;

再访问App Inventor网站。新建项目“PaintPot”,点击“Connect->AICompanion”,并按照提示操作,连接测试设备。

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

这样做是否会混淆了项目名称与屏幕标题呢(在英文版书中,将Title改为“PaintPot”,与项目同名,因此才有此疑问,对中文读者来说不存在这个疑问。——译者注)?别担心!在App Inventor中有三个非常重要名称:

  • 项目名称:同时也是应用发布时所使用的名称。提示:想修改项目名称,可以点击Project->Save project as,可以将原有项目赋予新的名称,同时原有项目依然得以保留;

  • 组件名称:一般的组件名称都可以修改,但Screen1例外,在当前版本中不能修改它的名称;

  • 屏幕标题:出现在设备的标题栏中,是Screen组件的Title属性,默认值是Screen1,如第一章HelloPurr中所见,可以随意修改它,如我们刚才将其改为“油漆桶”。

设计组件

创建“油漆桶”应用需要以下组件:

  • 三个Button组件:用来选择画笔颜色:红、蓝或绿,放在HorizontalArrangement组件中;

  • 一个Button组件用来充当橡皮;

  • 另外两个Button组件用来改变画笔的大小;

  • 一个Canvas组件,充当画布。Canvas具有BackgroundImage属性,我们将其设置为第一章HelloPurr中的kitty.png,稍后还可以将背景图片设置为用户拍摄的照片。

创建颜色按钮

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

1. 拖一个Button组件到预览窗口,设置其Text属性为“红”,BackgroundColor属性设为红色;

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

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

{%}

图 2-2 创建了3个按钮的预览窗口

注意:在项目中,建议为组建起一个有意义的名称,而不是像第一章那样采用默认名称。有意义的名称增加了程序的可读性,尤其是在切换到块编辑器时,将有助于区分不同的组件。本书中,采用惯用的骆驼命名法(如RedButton),即多单词无空格的首字母大写命名方式。

 测试:如果你还没有点击“Connect”来连接测试设备,那么做好连接,然后检查一下应用在设备(如果已经连接)上的表现。

使用Arrangement组件改善布局

现在三个按钮排成一列纵队,我们希望它们能排成一行,如图2-3所示,使用HorizontalArrangement组件来实现组件的水平排列:

1. 在组件面板的Layout类中拖出HorizontalArrangement组件,放在按钮下方;

2. 在属性面板中,设置HorizontalArrangement的width属性为“Fill Parent”(充满父容器),以便在水平方向上占满整个屏幕;

3. 将三个按钮移动到HorizontalArrangement中。注意,当你拖拽按钮时,会看到一条蓝色竖线,提示按钮将会被放置在什么地方。

{%}

图 2-3 在水平布局组件内的三个按钮

此时查看组件列表,你会发现三个按钮缩进排列在HorizontalArrangement项下,以显示它们现在是次一级的组件。同时注意到所有组件都缩进排在Screen1项下。

 测试:在测试设备的屏幕上,你会看到三个按钮排列成一行,尽管看起来与预览窗口中略有不同。如,在预览窗口中可见的HorizontalArrangement周围的轮廓线,在测试设备上则不可见。

通常采用布局组件来创建简单的垂直、水平或表格布局,也可以通过逐级插入(或嵌套)布局组件来创建更加复杂的布局。

添加Canvas(画布)

Canvas像一块画布,用户可以在上面绘画(画圆、画等)。添加一个Canvas,并用第一章中的kitty.png作它的背景图片(设置BackgroundImage属性),具体步骤如下:

1. 打开组件面板中的Drawing and Amination(绘画与动画)类,将Canvas组件拖到预览窗口中,改名为DrawingCanvas,Width设为“Fill parent”,Height设为300pixels;

2. 如果你已经完成了第一章的课程,那么文件kitty.png已经下载;如果没有,请在这里下载kitty.png。

3. 将DrawingCanvas的BackgroundImage设置为kitty.png:在设计器的属性面板中,BackgroundImage的默认值为None,点击None及Upload File来添加kitty.png文件;

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


图 2-7 用户触摸画布时,应用绘制一个圆点

在DrawingCanvas.DrawCircle块的右侧有三个插槽,需要填入三个参数:x、y、r。其中x、y用于指定绘制圆形的位置,r用于指定圆的半径。在屏幕左下角带感叹号的黄色警告显示数字“1”,表示需要填满这些插槽。从图中看到,有两组xy,这里要区分清楚:DrawingCanvas.Touched事件中的xy表示接触点位置(已知);而DrawingCanvas.DrawCircle命令块的xy插槽,用于设定绘制圆形的位置(待定)。我们恰好要在用户的接触点绘制圆形,因此DrawingCanvas.Touched事件中的xy值,可以作为DrawingCanvas.DrawCircle的x、y参数,插入到插槽中。

 提示:可以从“when”块中提取事件的参数值,将鼠标悬停在参数上,将呼出“get”及“set”块。可以将“get x”块拖出并插到x插槽中,作为DrawCircle命令的x值。如图2-8所示。


图 2-12 添加画线功能

DrawingCanvas.DrawLine块有四个参数,两点确定一线:设(X1,Y1)为起点,(X2,Y2)为终点。你能确定每个参数中需要插入什么值吗?记住,当手指在DrawingCanvas上拖动时,拖动事件将被调用很多次:在应用中,手指的每次移动都会绘制出一个微小线段,从(Prevx, prevy)到(currentX, currentY)。现在把它们填入DrawingCanvas.DrawLine块。

3. 拖出“get”块来充当画线的参数。将get prevX与get prevY分别插入到x1和y1插槽;而get currentX与get currentY插入到x2和y2插槽,如图2-13所示。

{%}

图 2-13 用户在屏幕上拖动手指,应用就在前一位置与当前位置之间画一条微小线段

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

添加按钮事件处理程序

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

在块编辑器中:

1. 展开左侧块的(Blocks)列表;

2. 打开RedButton抽屉,拖出RedButton.Click块;

3. 打开DrawingCanvas抽屉。拖出set DrawingCanvas.PaintColor块(可能需要滚动块的列表以便在抽屉里找到它),并把它放在RedButton.Click块“do”的位置;

4. 打开Colors抽屉,拖出红色块,将其插入set DrawingCanvas.PaintColor块的插槽;

5. 重复步骤2-4,设置蓝色和绿色按钮;

6. 最后设置WipeButton按钮。从WipeButton抽屉中拖出WipeButton.Click块。再从DrawingCanvas抽屉里拖出DrawingCanvas.Clear块,并将其放在WipeButton.Click块中。确认所有块显示如图2-14所示。

{%}

图 2-14 单击颜色按钮改变DrawingCanvas的画笔颜色;单击清除按钮清空屏幕

让用户拍照片

App Inventor应用可以与Android设备的强大功能进行交互,包括相机功能。为了增加应用的趣味性,用户可以将绘图背景设置为他们用相机拍摄的照片。

1. Camera组件有两个关键的块:Camera.TakePicture块用来启动设备上的拍照程序;拍照完成将触发Camera.AfterPicture事件。在Camera.AfterPicture事件处理程序中,可以将刚刚拍摄的照片设置为DrawingCanvas.BackgroundImage。打开TakePictureButton抽屉并拖出TakePictureButton.Click事件处理程序;

2. 从Camera1抽屉拖出Camera1.TakePicture放在TakePictureButton.Click事件处理程序中;

3. 从Camera1的抽屉中拖出Camera1.AfterPicture事件处理程序;

4. 从DrawingCanvas抽屉拖出set DrawingCanvas.BackgroundImage块放在Camera1.AfterPicture事件处理程序中;

5. Camera1.AfterPicture事件有一个名为image的参数,代表刚刚拍摄的照片,将从Camera1.AfterPicture块中得到的get image块插入DrawingCanvas.BackgroundImage块。

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

{%}

图 2-15 拍完的照片被设置为DrawingCanvas的背景图片

 测试:在设备上点击“拍照”按钮并拍摄照片,猫的图片变成了你拍的照片。你可以在自己的照片上进行绘画。(用Wolber教授的照片绘画是学生们的一大乐事,如图2-16。)(Wolber教授是本书的作者之一。)

改变画笔大小

{%}

图 2-16 带有Wolber教授涂鸦照片的PaintPot应用

在DrawingCanvas上画圆点,其大小由DrawingCanvas.DrawCircle块中参数r决定。改变r值可以改变圆点的大小。试试看将5改为10,然后在测试设备上查看结果。

另一个问题是,无论开发者如何设置参数r,用户都只能用这个固定的尺寸。如何让用户来改变圆点的大小呢?为此我们来修改程序:当用户点击“大圆”按钮时,圆点半径设为8,当点击“小圆”时半径设为2。

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

让我们先来定义一个变量dotSize:

1. 在块编辑器中,从Variables(变量)抽屉中拖出一个initialize global name to块。将“name”改为“dotSize”;

2. 请注意,initialize global dotSize to块有一个开放的插槽,可以在这里设定变量的初始值,或者说是应用启动时的默认值(编程术语称为“初始化变量”)。在本应用中,用数字块2来初始化变量dotSize,(创建块“2”的方法有两种:在空白区直接输入“2”然后回车;或从Math抽屉中拖出“0”块,将0改为2。)将其插到initialize global dotSize to块的插槽中,如图2-17所示。

{%}

图 2-17 将dotSize变量的初始值设为2

使用变量

下一步,我们要修改DrawingCanvas.Touched事件处理程序,将其中DrawingCanvas.DrawCircle块的参数r的固定值用变量dotSize来代替。(我们先将dotSize的初始值设定为“固定”的2,但稍后我们将改变dotSize的值,并同时改变画笔的大小。)

1. 从initialize global dotSize to块中拖出一个get global dotSize块,用它来提供变量的值;

2. 转到DrawingCanvas.Touched事件处理程序,将数字块“5”拖出插槽并扔进垃圾桶,用get global dotSize块来替换(见图2-18)。当用户触摸到DrawingCanvas时,应用将根据dotSize的大小来确定圆点的半径。

{%}

图 2-18 画笔的大小取决于变量dotSize中保存的值

修改变量值

现在变量魔法登场,变量dotSize允许用户选择画笔的大小,而事件处理程序也将以dotSize为半径来画圆。通过设计SmallButton.Click和BigButton.Click的事件处理程序来实现此功能:

1. 从SmallButton抽屉中拖出SmallButton.Click事件处理程序;再从Variables抽屉中拖出一个“set”块,下拉选择global dotSize,并将其插入SmallButton.Click块;最后,创建一个数字块“2”,并将其插入set global dotSize块。

2. 创建另一个类似的BigButton.Click事件处理程序,设置画笔大小为8。这两个事件处理程序显示在块编辑器中,如图2-19所示。

{%}

图 2-19 点击SmallButton及BigButton按钮改变画笔大小,之后将以该尺寸绘制图形

 提示: get/set global dotSize 之中的“global”(全局)指的是该变量适用于程序中所有的事件处理程序(全局)。与global相对的是“local”(局部)变量,适用于程序的特定部分;App Inventor 2中添加了此项功能,第12章首次使用。

{%} App inventor网站,可以尝试译者提供的替代版本,或点击页面右上角的“开发体验”按钮。


第 1 章 Hello 猫咪
第 3 章 打地鼠
温馨提示
下载编程狮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; }