如你所見,應用就是處理事件以及作出決策,這一過程是計算機程序的基礎,而同樣構成程序基礎的就是數據——程序所要處理的信息。程序中很少只用到像游戲中的成績這樣的單個數據,更普遍的是使用復雜數據——一些相互關聯的數據項,必須像設計應用的功能一樣,非常細心地組織這些數據。

本章將探討App Inventor中處理數據的方式,并學習兩種數據類型的基本編程方法,兩種數據類型為靜態數據(數據的值保持不變)及動態數據(數據由用戶生成),然后將學習如何處理更為復雜的包含數據的數據,即數據項本身也是一組數據。
許多應用中都存在這樣復雜的數據,如facebook中的好友列表,測試應用中的問題及答案列表等等,游戲中也會有角色的列表以及當前最高成績的列表。
列表變量的使用如同普通的文本及數字變量一樣,只是它們不僅僅代表單一的有名稱的存儲單元,而是表示一組相互關聯的存儲單元,例如,考慮表19-1中的電話號碼列表。
**表19-1 列表變量表示一系列的存儲單元**

使用索引值(index)來訪問列表元素,因此在列表19-1中,index為1時表示第一項111-2222,index為2時表示第二項333-4444,而index為3時表示555-6666。
App Inventor提供了操作這些數據的塊,包括數據的創建、為數據添加元素、從列表中選擇指定的項以及對整個列表的操作,讓我們從創建列表開始。
## **創建列表變量**
在塊編輯器中,使用“initialize global (name) to”塊以及“make a list”塊來創建列表變量。例如,假設你正在寫一個“一鍵發送短信”的應用,通過點擊一個按鍵向電話號碼列表中的所有成員發送短信。用如下方式創建一個電話號碼列表:
1\. 從塊編輯器的Variables抽屜中拖出一個“initialize global (name)to”塊到應用中,如圖19-1。

**圖 19-1 初始化變量的塊**
2\. 點擊文本“name”,將其改為phoneNumbers,如圖19-2所示。

**圖 19-2 將變量重命名為phoneNumber**
3\. 從Lists抽屜中拖出“make a list”塊插入初始化變量塊,如圖19-3所示。這是告訴應用,要存儲的變量是一個列表,而非單個值。通過點擊“make a list”塊上的藍色增項圖標來指定所需存儲槽的數量,來增加數據項,如圖19-3所示。

**圖 19-3 使用“make a list”塊來定義列表變量phoneNumber**
4\. 最后,從Text抽屜中拖出文本塊,輸入需要的電話號碼,插入到“make a list”塊的數據項插槽中。

**圖 19-4 當列表中添加了全部數據項,相當于開辟了一個新的存儲空間**
數據項的存儲中可以插入任何類型的數據,但在本例中,這些數據項是文本類型的對象,而不是數字,因為電話號碼中含有一個破折號“-”,這種非數字類型的符號無法輸入到數字塊中,也無法與數字進行任何運算(而數字運算必須用到數字塊)。
圖19-4中所示的塊定義了一個名為phoneNumber的變量,在應用啟動時,你定義的任何變量就在此時被創建,而像表19-1中的存儲槽也被同時創建并被填寫上初始值。一旦有了這個列表變量,就可以使用列表中的數據開始編程了。
## **選擇列表項**
應用中可以使用“select list item”塊,并指定索引值(index)來訪問列表中指定的數據項。index代表了該數據項在列表中的位置。因此,如果列表中有三個數據項,就可以用索引值1、2、3來訪問這些項。圖19-5中顯示了選中列表第二項的塊。

**圖 19-5 選擇列表中的第二項**
使用選擇塊“select list item”需要提供兩項參數,首先是要查詢的列表,將其插入選擇塊的第一個插槽中,其次是索引值index,將其插入選擇塊的第二個插槽中。圖19-5中的塊是告訴應用從列表phoneNumber中選出第二個元素。如果列表的定義如表19-1的話,俺么選擇的結果就是“333-4444”。
從列表中選擇數據項,這僅僅是第一步,通過選擇可以實現各種操作,下面將舉例說明。
### **使用Index遍歷列表**
許多應用中,定義列表的目的是讓用戶可以遍歷(逐個查看)它。第8章總統測驗就是一個很好的例子:用戶點擊“下一題”按鈕,程序從問題劣幣哦啊中選擇下一道題并顯示出來。
但如何實現對下一項的選擇呢?圖19-5中選擇的是列表phoneNumber中的第二項,而遍歷列表時,每次選擇的項目序號是不同的,會根據當前選中項在列表中的位置來確定。因此就需要定義一個變量來表示這個位置,通常用index來作為變量名,初始值通常設為1(列表中的第一個位置),如圖19-6所示。

**圖 19-6 變量index的初始值為1**
當用戶設法移動到下一項時,可以在當前的index值上加1來實現變量的遞增,并使用遞增后的值在列表中做選擇。圖19-7中顯示了實現這一點使用的塊。

**圖 19-7 使index值遞增,并用遞增后的值選擇列表項**
### **舉例:遍歷畫筆顏色列表**
來看一個例子,用戶可以通過點擊按鈕來為他的房子選擇一種可能的粉刷顏色,每次點擊,按鈕的顏色都會變化。當用戶查閱完全部顏色時,再重新回到第一種顏色。
例子中,可以使用基本色,也可以替換成任何一組顏色,關于顏色的更多信息,可以參見App Inventor文檔([http://appinventor.googlelabs.com/learn/reference/blocks/colors.html](http://appinventor.googlelabs.com/learn/reference/blocks/colors.html))。
第一步是定義一個顏色列表變量,并以顏色為列表項來初始化列表,如圖19-8中所示。

**圖 19-8 用一組顏色為colors列表做初始化**
接下來,定義變量index來跟蹤列表的當前項位置,初始值為1。可以給變量一個更有意義的名字,如currentColorIndex,但如果你的應用中不需要處理其他更多的列表,用index就好。如圖19-9所示。

**圖 19-9 使用index變量來跟蹤列表當前項的位置,初始值為1**
用戶通過點擊ColorButton從列表中瀏覽到下一項(顏色),此時,index值遞增,而按鈕的BackgroundColor變為當前選中的顏色,如圖19-10所示。

**圖 19-10 用戶通過點擊按鈕瀏覽顏色列表——每次點擊都會改變按鈕顏色**
先假設我們已經在組件設計器中將按鈕的背景顏色設為紅色(Red),第一次點擊按鈕時,index值從初始的1變為2,按鈕顏色變為列表中的第二項——綠色;第二次點擊按鈕時,索引值從2變為3,按鈕變為藍色。
想象一下,下一次的點擊會出現什么情況?
如果你說會出錯,那么你對了!index值將變成4,程序將試圖在列表中選擇第4項,但列表中只有3項,因此程序強行關閉,或退出,用戶將看到一條如圖19-11中所示的錯誤信息。

**圖 19-11 當試圖從一個只有三項的列表中選擇第四項時,程序將提示錯誤信息**
顯然,你不想讓用戶看到這樣的信息,為了避免出現這樣的問題,需要添加一個if塊來檢查是否到達了列表中的最后一項。如果是,將index值設回1,來顯示第一種顏色,如圖19-12所示。

**圖 19-12 使用if塊檢查索引值index是否大于列表的長度,如果是,將index值重新設為1**
用戶點擊按鈕時,index值遞增,然后檢查它的值是否過大。與index值進行比較的是“length of list”,而不是3,因此,即便是列表中添加了更多的項,程序也能正常運行。通過檢查index是否大于列表長度(而不是與固定的數字3進行比較),可以消除程序中的代碼相關性。所謂“代碼相關性”是一個編程術語,舉例來說,如果你的應用中某些方面的程序寫得過于具體,那么當你想對某處做出修改時(如,列表的數據項),你不得不找到應用中所有使用過這個list的地方,并對程序塊進行逐一修改。
正如你所想象得,這種相關性會讓程序在短時間內變得混亂不堪,也會產生更多的錯誤等待你去排查。事實上,在“粉刷彩色房屋”應用的設計中,就在我們剛剛完成的程序中,還存在另一個代碼相關性問題,你能找出是什么嗎?
如果將顏色列表中的第一項由紅色改為其他顏色,應用的運行結果就不再正確,除非你能記得在組件設計器中修改ColorButton.BackgroundColor屬性的初始設定。消除這種代碼相關性的方法是,將ColorButton.BackgroundColor初始值設定為顏色列表中的第一項,而不是某個特定的顏色。由于這一修改涉及到程序啟動時的行為,因此需要在應用啟動時調用的Screen.Initialize事件處理程序中進行這一設定。如圖19-13所示。

**圖 19-13 應用啟動時,將按鈕的背景色設置為顏色列表中的第一項**
## **創建輸入表單及動態數據**
前面的“粉刷彩色房屋”應用涉及到一個靜態列表:程序員(也就是你)定義了列表中的元素,除非你親自動手,沒有人能修改這些列表項。不過,多數情況下,應用中要處理動態列表:最終用戶輸入新的數據項而導致數據的變化,或者從數據庫或web信息源加載新數據。本節將討論一個“隨手記”應用:用戶在應用中,通過表單輸入筆記,并可預覽之前所有輸入過的內容。
### **定義動態列表**
如果希望創建一個空列表,可以使用“create empty list”塊來定義,例如,在“隨手記”應用中,允許用戶輸入筆記,但在定義列表時,不應該有預定義的數據項,具體的定義方法見圖19-14。

**圖 19-14 動態列表的定義中不應該含有任何預定義數據項**
### **添加數據項**
當第一次啟動應用時,notes列表是空的,當用戶在表單中輸入數據并點擊“保存”按鈕時,新的筆記內容將被添加到列表中。表單的設置非常簡單,如圖19-15所示。

**圖 19-15 用輸入表單想筆記列表中添加新項**
當用戶輸入一段筆記并點擊“保存”按鈕,應用將調用“add items to list”函數將新輸入的內容添加到列表中,如圖19-16所示。

**圖 19-16 用戶點擊“保存”按鈕時,調用“add items to list”向列表中添加新內容**
“add items to list”塊將新的數據追加到列表的結尾,用戶每次點擊“保存”,就添加一條新筆記,在Lists抽屜中可以找到這個塊。特別注意:還有另一個塊“append to list”,它的功能是向一個列表中追加另一個列表,很少會用到這個塊。
### **顯示列表**
對用戶來說,列表變量notes的內容是不可見的,還記得之前講過,應用中的變量是用來保存那些不需要被用戶看到的信息。圖19-16中的塊實現了一點擊按鈕就添加新項的功能,但用戶看不到任何反饋,除非你在程序中添加顯示列表內容的功能。
在應用的用戶界面中顯示列表內容最簡單的方法就是現實數字和文本的方法:將列表內寫入Label組件的Text屬性,如圖19-17所示。

**圖 19-17 用NotesListLabel的Text屬性顯示筆記列表**
可惜這種簡單的顯示方法看起來不夠美觀,列表中所有的項被放置在一對小括號內,沒有分行,項之間用空格分隔。如圖19-18所示,用戶輸入了第一條筆記“忘記了讓筆記顯示出來”,然后又輸入第二條“顯示結果被一對括號包圍著!”。

**圖 19-18 列表內容的簡單顯示方法**
如果學習過第13章的“亞馬遜掌上書店”,你對這個問題應該熟悉。在第20章中,將學習如何用更加復雜的方式來顯示列表內容。
### **刪除列表項**
使用“remove list item”塊可以從列表中刪除某一項。如圖19-19所示。

**圖 19-19 刪除列表項**
圖19-19從列表notes中刪除了第2項,但通常我們不希望只刪除某個固定的項(如第2項),而是讓用戶來選擇需要刪除的項。
ListPicker是一個可以用于刪除列表項的用戶界面組件,它與一個按鈕關聯,當點擊按鈕時,ListPicker會顯示列表項,并允許用戶選擇其中的一項。當用戶選中后,應用就可以將其刪除。
ListPicker有兩個關鍵事件BeforePicking及AfterPicking,而且每個事件都有兩個重要屬性:Elements及Selection,如表19-2所示,只要理解了這兩個事件及其屬性,ListPicker組件的編程就很容易了。
**表19-2 ListPicker組件的兩個關鍵事件及其屬性**
|
事件
|
屬性
|
| --- | --- |
|
BeforePicking:點擊按鈕時觸發
|
Elements:選中的列表
|
|
AfterPicking:用戶做出選擇時觸發
|
Selection:用戶所選項
|
當用戶點擊ListPicker的關聯按鈕時,觸發ListPicker.BeforePicking事件,此時用戶尚未選擇列表項;在ListPicker.BeforePicking事件處理程序中,可以將ListPicker.Elements屬性設置為一個列表變量,例如,在“隨手記”應用中,將Elements屬性設置為列表notes,如圖19-20。

**圖 19-20 ListPicker的Elements屬性被設置為列表變量notes**
這些塊將列表notes的內容顯示在ListPicker中,如果列表中有兩條筆記,其顯示如圖19-21所示。

**圖 19-21 列表notes顯示在ListPicker組件中**
當用戶從列表中選擇一項時,將觸發ListPicker.AfterPicking事件,在該事件的處理程序中,可以利用ListPicker.Selection屬性來訪問用戶的所選項。
但是想到“remove item from list”塊需要的是索引值(列表中的位置),不是具體的項,而Selection屬性卻是實際數據(一條筆記),不是索引值,ListPicker組件不直接提供對列表索引值的訪問(在App Inventor的后續版本中將添加此功能)。
變通的方法是利用Lists抽屜中的另一個塊:“index in list”。對于給定的文本,該塊將返回列表中最先與該文本匹配的項的位置,使用“index in list”,ListPicker1.AfterPicking事件處理程序將刪除用戶選中的項。如圖19-22所示。

**圖 19-22 使用“index in list”塊找出要刪除項的索引值**
AfterPicking事件被觸發后,ListPicker1.Selection中包含了用戶選中的文本(如“忘記了讓筆記顯示出來”)。我們的目標是找到選中項在列表中的索引值,以便將其刪除。如果用戶選擇的是“忘記了讓筆記顯示出來”,則“index in list”塊將返回1,因為這段文本是列表中的第1項。將索引值保存到變量removeIndex中,并將它用作“remove list item”塊中的index值。
再繼續閱讀之前,先思考一個問題:這種方法是否總是有效呢?
回答是肯定的,但條件是列表中沒有重復的項。比如說,用戶輸入的第2條和第10條筆記都是“今天過得太好了!”。如果此時用戶點擊“刪除列表項”按鈕(其實是ListPicker),并選中了第10項,那么被刪除的將是第2項,而非第10項。“index in list”塊只能返回第一個匹配項,然后就停在那里,因此也就找不出應該被刪掉的內容相同的第10項。需要對列表進行遍歷,并使用適當的條件判斷(見第18章)來查看是否還有其他匹配項,并將其刪除。
## **列表中的列表**
列表項可以使數字、文本、顏色、布爾值(true/false),也可以是數據(維基:在計算及數據處理中,數據往往表示一種結構,如表格[由行和列組成]、樹[一組有父子關系的節點]或者圖形[一組連接起來的節點]。數據通常是測量的結果,可以被可視化成圖形。),這是一種常見的數據結構。例如,一個數據的列表可以將第8章總統測試轉變為一個多選題測驗。我們來重溫一下總統測試中數據的基本結構:一個問題列表和一個答案列表,如圖19-23所示。

**圖 19-23 一個問題列表和一個答案列表**
每當用戶回答完一個問題,程序通過與AnswerList中的當前項進行對比來判斷回答是否正確。
為了實現多選測驗,需要為每個問題提供一個可供選擇答案的列表。多選列表可以表示為一個數據列表變量,將三個“make a list”塊放在一個外層“make a list”塊中,來定義這個變量,如圖19-24所示。

**圖 19-24 通過在外層“make a list”塊中插入若干個“make a list”塊來構造出一個數據列表**
變量answerChoices中的每個數據項本身也是一個由三個數據項組成的列表,如果從answerChoices列表中選擇一項,選擇的結果將是一個列表。現在填好多選答案的雙重列表,那么如何向用戶顯示這些數據呢?
在“隨手記”應用中,使用了一個ListPicker來向用戶顯示選項。假如索引值為currentQuestionIndex,則事件處理程序ListPicker.BeforePicking將寫成圖19-25中顯示的樣子。

**圖 19-25 使用ListPicker向用戶展示多選列表**
這些塊將選取并顯示answerChoices中的當前項對應的子列表,供用戶選擇。如果currentQuestionIndex為1,ListPicker將顯示圖19-26中的列表。

**圖 19-26 向用戶展示第1題的多選答案**
用戶選擇之后,用圖19-27中的塊對答案進行檢查。

**圖 19-27 檢查用戶選擇的答案是否正確**
這些塊中,用戶從ListPicker中選擇的答案將與正確答案進行比較,而正確答案保存在另一個列表AnswerList中(注意answerChoices只提供選項而不代表答案)。
## **小結**
你能想到的幾乎每個應用中都會用到數據,理解它們的運行機制是編程的基礎,本章探索了一種最常用的編程模式:使用索引變量,從列表的第一項開始,通過變量的遞增實現對每個列表項的處理。如果能理解并在自己的程序中加以運用,那么你的確是一名程序員了。
然后我們講到了列表處理的其他方式,包括一個典型的讓用戶添加并刪除列表項的表單。如此的編程還需要另一個層次的抽象能力,你必須假想數據的存在,因為直到用戶輸入某些數據之前,這些數據都是空的。如果你能理解這一點,你甚至可以考慮辭掉現在的日常工作了。
最后我們介紹了復雜的數據結構——數據列表。這顯然是一個不太容易理解的概念,但我們利用一些固定的數據對問題進行了探索:多選測驗中的可選擇答案列表。如果你對此以及本章的其余部分都有所掌握,那么你的期末考試題是:使用數據列表創建一個應用,但要求使用動態數據!一個例子就是允許用戶在應用中創建他們自己的多選測驗,這個功能甚至比第10章的出題應用還要強大。祝你好運!
在你思考如何處理這些列表時,要知道我們的探索還沒有結束。在下一章中,我們將繼續討論并重點講解略有不同的列表循環:對列表中的每一項實施一些列的操作。
- 簡介
- 序言
- 前言
- 第 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通信