本章將從程序員的視角來探討應用的結構問題。先從一個經典的比喻開始,將應用理解為一份菜譜,然后再從組件的角度,理解其對事件的響應,從而對應用產生新的認識。本章還將探討應用如何提問、重復、記憶以及與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-8 應用啟動時,使用Screen1.Initialize事件塊來放置地鼠**
#### **計時器事件**
應用中的某些活動是由時間的流逝而觸發的,比如動畫,可以理解為計時器事件觸發了角色的移動。App Inventor有一個Clock組件,用于觸發計時器事件。例如,如果想讓一個球在一定時間間隔內,在屏幕上水平移動10個像素,就可以像圖14-9那樣來設置塊。

**圖 14-9 一旦Clock1.Timer開始運行(計時),使用計時器事件塊來移動球**
#### **動畫事件**
在canvas范圍內的圖形對象(sprites精靈),它們的活動將觸發動畫事件,具體地說,當兩個sprites發生碰撞,或一個sprites到達canvas的邊界時,將觸發動畫事件。因此可以編寫游戲或其他交互式動畫程序,利用動畫事件來定義游戲或動畫的情節。更多信息請參見第17章。
#### **外部事件**
當手機從接收到來自GPS衛星的位置信息時,將觸發一個外部事件;同樣,當手機收到短信時,也會觸發此類事件(圖14-10)。

**圖 14-10 當手機收到短信時,觸發Texting1.MessageReceived事件**
這類向設備輸入外來信息的行為都被視為外部事件,用戶點擊按鈕也屬于此類事件。
因此你所創建的應用,從本質上講是一系列的事件處理程序:一個是對應用的初始化,有些是響應最終用戶的輸入,有些由事件觸發,有些則有外部事件觸發。你的任務是以事件處理的方式構思應用,然后設計對每個事件的響應方式。
### **事件處理程序可以提問**
對事件的響應不總是單線條的菜譜,程序可以提問,也可以重復某些操作。“提問”意味著就應用中的數據進行提問,并根據答案決定下一步的行進方向(分支)。我們把應用中的提問稱為“條件分支”,如圖14-11所示。

**圖 14-11 事件處理程序可以根據對條件問句的回答來執行不同的分支**
測試條件會如此設計:“分數到達100了嗎?”或者“我剛收到的短信來自小明嗎?”
測試也可以是的更為復雜的規則,包含多種關系操作符(小于、大于、等于)及邏輯運算符(and、or、not)。
在App Inventor中可以使用if塊、ifelse塊來設定條件行為,例如,在圖14-12中,當玩家的分數為100點時,程序將顯示“你贏了!”。

**圖 14-12 一旦玩家成績達到100點,用if塊來報告獲勝**
本書第18章中將詳細討論條件塊。
### **事件處理程序可以重復執行某些塊**
應用不但可以提問并依據答案執行不同分支,還可以多次重復執行某些操作。App Inventor提供了兩種用于重復執行的塊:foreach及while do。兩種塊中都會包含其它塊。對于列表中的每一項,都會執行一次foreach塊中的所有塊,例如,如果你想給電話號碼列表中的每個人都發送同一條短信,你可以利用圖14-13中的塊來實現。

**圖 14-13 foreach塊中的塊將對列表中的每一項都執行一次操作**
foreach塊中的塊會重復多次,例如,3次,因為PhoneNumbers列表中只有三項。因此“想你...”這樣的短信將發往這三個號碼。本書第20章將詳細討論重復塊。
### **事件處理程序可以實現存儲功能**
在事件處理程序運行過程中,通常會記錄某些信息,有些信息可以保存在內存中,被稱作變量。變量在塊編輯器中定義,與組件的屬性類似,但與任何組件無關。例如,在游戲類應用中,可以定義一個叫做“score(分數)”的變量,當用戶執行了某些操作時,事件處理程序會相應地修改score的值。變量用于在應用運行過程中臨時保存數據,一旦退出應用,數據將不復存在。
有時不僅需要在運行中保存某些數據,而且要求當退出又重新打開應用時,數據依然存在。例如,你想保存一個游戲的歷史最高得分,就需要長期保存數據,以便下次有人再玩游戲時,可以看到這個分數。在應用關閉后依然保存下來的數據成為永久性數據,這些數據被保存在某種類型的數據庫中。
在本書的第15章及第22章,我們將分別討論臨時存儲(變量)及永久存儲(數據庫)的使用問題。
### **事件處理程序可以與Web對話**
有些應用只能使用電話或設備的內部信息,但有些則能通過向web service API發送請求的方式與網絡進行通信,這類應用被稱為“網絡應用”。
Twitter是一個很好的例子,它提供web service供App Inventor的應用訪問。你可以編寫一個應用,來請求并顯示朋友們剛剛發布的“推文”,也可以隨時更新自己的Twitter狀態。能夠與多個web service進行對話的應用被稱為聚合類應用,我們將在第24章進行探討。
## **小結**
應用開發者必須以兩種視角來觀察一個應用,一個是最終用戶的視角,另一個則是自內向外的程序員的視角。利用App Inventor來開發應用,首先要設計應用的外觀,然后設計應用的行為——一套事件處理程序,讓應用按照你的意圖去運行。首先,通過在事件處理程序中拼裝配置某些塊,來實現對事件的響應,這些塊可能是函數、條件分支、循環操作、web調用、數據庫操作,等等;然后在手機中運行應用,來測試你的程序。當你編寫了若干個程序之后,應用的內部結構與它的物理外觀之間的關系將逐漸清晰,此時,你將成了一名真正的程序員。
- 簡介
- 序言
- 前言
- 第 1 章 Hello 貓咪
- 第 2 章 油漆桶
- 第 3 章 打地鼠
- 第 4 章 開車不發短信
- 第 5 章 瓢蟲快跑
- 第 6 章 巴黎地圖旅游
- 第 7 章 安卓,我的車在哪?
- 第 8 章 總統測驗
- 第 9 章 木琴
- 第 10 章 出題及答題
- 第 11 章 廣播中心
- 第 12 章 遙控機器人
- 第 13 章 亞馬遜掌上書店
- 第 14 章 理解應用的結構
- 第 15 章 軟件工程與應用調試
- 第 16 章 應用中的存儲
- 第 17 章 創建動畫應用
- 第 18 章 程序中的決策:條件塊
- 第 19 章 數據列表編程
- 第 20 章 循環
- 第 21 章 定義過程
- 第 22 章 數據庫
- 第 23 章 傳感器
- 第 24 章 與Web API通信