就像人類需要記憶一樣,應(yīng)用需要存儲(chǔ)。本章將探究如何在應(yīng)用中實(shí)現(xiàn)信息的存儲(chǔ)。
如果剛剛有人在電話里告訴你一家披薩店的電話號(hào)碼,你的大腦中會(huì)留下一段記憶;這時(shí),如果有人大聲告訴你一串?dāng)?shù)字,并要你記住,你也會(huì)將它們保存到記憶中。在這種情況下,你未必能清楚地意識(shí)到,你的大腦是在保存或調(diào)用信息。
應(yīng)用同樣具備記憶功能,但它的內(nèi)在機(jī)制并不像大腦那樣神秘。本章你將學(xué)習(xí)如何設(shè)置應(yīng)用的存儲(chǔ)功能,如何利用它來(lái)保存信息,以及之后如何提取這些信息。
應(yīng)用的存儲(chǔ)功能由一組有名稱的存儲(chǔ)槽(memory slots)組成。一旦組件被拖到應(yīng)用中,就會(huì)自動(dòng)創(chuàng)建了一組被稱為“屬性”的存儲(chǔ)槽;也可以定義與特定組件無(wú)關(guān)的、有名稱的存儲(chǔ)槽,即變量。如果說(shuō)屬性通常與應(yīng)用的外觀呈現(xiàn)有關(guān),那么變量則被認(rèn)為是應(yīng)用中不可見的“暫時(shí)”記憶。
在應(yīng)用中,組件,或者說(shuō)像Button、TextBox以及Canvas這類可視組件,構(gòu)成了完整的用戶界面。而組件本身的外觀則是又一組屬性來(lái)確定,屬性值就保存在存儲(chǔ)槽中。
在組件設(shè)計(jì)器中,可以直接對(duì)屬性的存儲(chǔ)槽進(jìn)行修改,如圖16-1所示。
圖 16-1 在屬性欄中修改存儲(chǔ)槽來(lái)改變應(yīng)用的外觀
圖16-1中的Canvas組件具有六個(gè)屬性:BackgroundColor及PaintColor是保存顏色的存儲(chǔ)槽,BackgroundImage保存了文件名(kitty.png),Visible屬性保存了一個(gè)布爾值(true或false,依賴于是否勾選了方框),而Width及Height屬性保存了一個(gè)數(shù)字或某個(gè)特定的設(shè)置(如,“Fill parent”)。
在組件設(shè)計(jì)器中設(shè)置組件的屬性,相當(dāng)于設(shè)置應(yīng)用啟動(dòng)時(shí)的外觀。應(yīng)用的最終用戶從未見過(guò)應(yīng)用中有一個(gè)名字為Height、值為300的存儲(chǔ)槽,他們只能看見用戶界面上有一個(gè)300像素高的組件。
像屬性一樣,變量也是被命名的存儲(chǔ)槽,只是與特定的組件無(wú)關(guān)。在應(yīng)用中,需要記住某個(gè)狀態(tài),如果無(wú)法用組件的屬性來(lái)保存它,就需要定義一個(gè)變量來(lái)保存它。例如,一個(gè)游戲類的應(yīng)用可能需要記住玩家到達(dá)的等級(jí)。如果等級(jí)數(shù)用Label組件來(lái)顯示,就不需要定義變量,因?yàn)長(zhǎng)abel組件的Text屬性可以用來(lái)保存這個(gè)等級(jí)。但是,如果等級(jí)數(shù)不需要顯示給用戶,就應(yīng)該定義一個(gè)變量來(lái)保存它。
另一個(gè)使用變量的例子是第8章總統(tǒng)測(cè)驗(yàn)。在這個(gè)應(yīng)用中,用戶界面上一次只能顯示一道測(cè)驗(yàn)題,而其他問(wèn)題用戶是看不見的,因此,就需要定義一個(gè)問(wèn)題列表的變量來(lái)保存它們。
在組件設(shè)計(jì)器中拖入一個(gè)組件,它的屬性就自動(dòng)創(chuàng)建完成了,相比之下,變量的定義需要在塊編輯器中直接拖出一個(gè)變量初始化塊(initialize global name to),點(diǎn)擊塊中的“name”為變量命名,并為變量設(shè)置初始值,方法是拖出一個(gè)塊放入變量初始化塊中,可以是number塊、text塊、color塊或者是make a list塊。跟隨下面的步驟就可以創(chuàng)建一個(gè)叫做score的初始值為0的變量。
1. 從塊編輯器的Built-in分組中找到Variables,點(diǎn)擊打開抽屜并拖出“initialize global name to”塊,如圖16-2所示。
圖 16-2 拖出變量初始化塊
2. 為變量命名:點(diǎn)擊變量初始化塊中的“name”,并輸入“score”,如圖16-3所示。
圖 16-3 為變量命名
3. 為變量設(shè)置初始值:從Math抽屜中拖出數(shù)字塊,將其插入變量初始化塊的插槽中,如圖16-4所示。
圖 16-4 為變量設(shè)初始值
4. 將變量初始值由默認(rèn)值(0)改為123,如圖16-5所示。
圖 16-5 修改變量的初始值
定義一個(gè)變量,就是通知應(yīng)用建立一個(gè)有名稱的存儲(chǔ)槽,來(lái)保存某個(gè)值。像屬性一樣,這些存儲(chǔ)槽用戶是看不見的。
變量的初始值在應(yīng)用啟動(dòng)時(shí)就已經(jīng)被放入存儲(chǔ)槽中??梢杂脭?shù)字或文本對(duì)變量進(jìn)行初始化,除此之外,也可以插入一個(gè)“make a list”塊,它告訴應(yīng)用這個(gè)變量是一個(gè)存儲(chǔ)槽的列表,而不是一個(gè)單獨(dú)的值。關(guān)于list的更多內(nèi)容請(qǐng)參考第19章。
變量定義之后,App Inventor會(huì)生成兩個(gè)屬于這個(gè)變量的塊:set塊及get塊,只要將鼠標(biāo)懸停在變量初始化塊中的變量名稱之上,就可以呼出到這兩個(gè)塊。如圖16-6所示。
圖 16-6 變量初始化塊包含訪問(wèn)該變量的set塊及get塊
其中的“set global score to”塊可以用來(lái)修改(設(shè)置)變量的值,例如圖16-7中,將數(shù)字塊5放在變量score的set塊中。變量初始化塊中的“global”一詞意為“全局的”,指的是變量的適用范圍,一個(gè)全局變量可以被程序中所有事件處理程序及過(guò)程所引用。新版的App Inventor中還可以定義一種“l(fā)ocal”變量,這種變量可以在一個(gè)事件處理程序或某個(gè)過(guò)程的內(nèi)部進(jìn)行定義(這里暫不涉及)。
圖 16-7 將數(shù)字5賦給變量score
另一個(gè)“get global score”塊用于從變量中讀取變量值。例如,如果你想檢查score的值是否大于或等于100,就可以將“get global score”塊插入if塊進(jìn)行測(cè)試,如圖16-8所示。
圖 16-8 使用get global score塊來(lái)獲取變量值
可以用單一的數(shù)字5來(lái)為變量賦值,不過(guò)通常會(huì)用一個(gè)更為復(fù)雜的表達(dá)式來(lái)為變量賦值(“表達(dá)式”是一個(gè)計(jì)算機(jī)科學(xué)的術(shù)語(yǔ),即公式)。例如,在總統(tǒng)測(cè)試的應(yīng)用中,用戶點(diǎn)擊“下一題”按鈕時(shí),要讓變量currentQuestionIndex的值增加1,來(lái)顯示下一道題;又如在游戲類應(yīng)用中,如果玩家失敗,還有可能將他的成績(jī)減10分;還有像第3章打地鼠的游戲中,通過(guò)改變變量x的值,實(shí)現(xiàn)地鼠在Canvas中水平位置的隨機(jī)移動(dòng)。因此可以用若干個(gè)塊組成的表達(dá)式插入“set global score”塊為變量score賦值。
一種最常見的表達(dá)式可能是變量的遞增,或根據(jù)變量的當(dāng)前值進(jìn)行設(shè)定。例如,游戲中當(dāng)玩家獲勝一次,變量score就將增加5,如16-9顯示了實(shí)現(xiàn)這一行為需要的塊。
圖 16-9 分?jǐn)?shù)變量遞增5
如果能夠理解這些塊的含義,你就離程序員又近了一步。這些塊可以理解為“讓成績(jī)?cè)诂F(xiàn)有的值上加1”,這是變量遞增的另一種說(shuō)法。要理解這些塊的工作機(jī)制,需要按照從內(nèi)向外、而不是從左到右的順序,最里面的塊是“get global score”及數(shù)字“5”,它們是最基礎(chǔ)的塊,然后“+”塊執(zhí)行加法運(yùn)算,并將結(jié)果設(shè)定為變量score的值。
假設(shè)存儲(chǔ)槽中score的當(dāng)前值為5,經(jīng)過(guò)這些塊的運(yùn)算,程序執(zhí)行了以下步驟:
1. 從score的存儲(chǔ)槽中讀取當(dāng)前值5;
2. 加上5得到結(jié)果10;
3. 將10放回到score的存儲(chǔ)槽中(來(lái)替代5)。
關(guān)于變量遞增的更多內(nèi)容請(qǐng)參見第19章。
在Math抽屜中(圖16-10),App Inventor提供了許多數(shù)學(xué)函數(shù),就像在電子表格或計(jì)算器中見到的一樣。
圖 16-10 Math抽屜中的運(yùn)算符及函數(shù)
你可以使用這些塊來(lái)構(gòu)造復(fù)雜的表達(dá)式,并將它們作為賦值表達(dá)式插入到“set global to”塊中。例如,要想實(shí)現(xiàn)一個(gè)圖片精靈(image sprite)在canvas范圍內(nèi)的隨機(jī)水平移動(dòng),就需要使用一個(gè)乘法塊(*)、一個(gè)減法塊(-)一個(gè)Canvas.Width屬性以及一個(gè)隨機(jī)小數(shù)函數(shù)來(lái)組織表達(dá)式,如圖16-11所示。
圖 16-11 使用數(shù)學(xué)(Math)塊來(lái)構(gòu)造上面的復(fù)雜表達(dá)式
正如在前面變量遞增的例子中所說(shuō),程序?qū)@些塊的解釋是遵循從內(nèi)而外的順序。假設(shè)Canvas的Width屬性值為300,ImageSprite的Width為50,程序?qū)?zhí)行以下步驟:
1. 分別從Canvas1.Width及ImageSprite.Width的存儲(chǔ)槽中讀取300及50;
2. 減法運(yùn)算:300 - 50 = 250;
3. 調(diào)用隨機(jī)小數(shù)函數(shù)獲得一個(gè)1-1之間的隨機(jī)數(shù)(比如說(shuō)0.5);
4. 乘法運(yùn)算:250 * 0.5 = 125;
5. 將125放在ImageSprite.x屬性的存儲(chǔ)槽中。
在前面的例子中,修改一個(gè)組件的屬性,將直接影響到用戶界面的外觀,而變量則不然,改變一個(gè)變量并不會(huì)直接影響到應(yīng)用的外觀。如果你只是將變量score的值遞增,而不設(shè)法修改用戶界面的話,用戶永遠(yuǎn)都不知道變化的存在,就像俗話說(shuō)的“樹木落入森林”一般:如果沒(méi)有人知道它,怎么證明它的存在呢?
有時(shí),當(dāng)變量變化時(shí),不希望在用戶界面上立即顯示出來(lái),例如,在游戲中,可能會(huì)記錄某些統(tǒng)計(jì)結(jié)果(如失敗次數(shù)),只有游戲結(jié)束時(shí)才會(huì)顯示其結(jié)果。
與組件的屬性相比,這是使用變量來(lái)存儲(chǔ)數(shù)據(jù)的優(yōu)勢(shì):可以在需要的時(shí)間顯示必要的數(shù)據(jù),也可以使應(yīng)用中的計(jì)算與用戶界面分離,這樣做的結(jié)果是更易于稍后對(duì)用戶界面的修改。
例如,在游戲中,可以將成績(jī)直接保存在Label的Text屬性中,也可以保存在變量中。如果保存在Label中,得分時(shí)可以讓Label的Text屬性值遞增,用戶可以直接看到成績(jī)的變化;如果成績(jī)被保存到變量中,并用變量的遞增記錄得分,則需要另外設(shè)置塊,將變量值顯示到Label中。
盡管使用變量保存并顯示數(shù)據(jù)要多出一些步驟,但當(dāng)你決定要修改應(yīng)用,以不同的方式在用戶界面上顯示成績(jī)時(shí),變量的方法讓改變很容易實(shí)現(xiàn)。你不必對(duì)每個(gè)顯示組件上的成績(jī)進(jìn)行修改,它們不需要修改,你只需要修改那些與顯示有關(guān)的塊。
使用Label而非變量的方法,會(huì)讓應(yīng)用變得難于修改,因?yàn)?,比如說(shuō)要用一個(gè)遞增的值來(lái)控制label的寬度(Width),每一次遞增都要執(zhí)行一次對(duì)Width屬性的修改。
應(yīng)用啟動(dòng)之后,開始執(zhí)行一系列的操作,并對(duì)發(fā)生的事件進(jìn)行響應(yīng)。在事件響應(yīng)過(guò)程中,應(yīng)用有時(shí)需要記住一些東西,如,游戲中每個(gè)選手的成績(jī),或者某個(gè)對(duì)象的移動(dòng)方向等。
應(yīng)用可以用組件的屬性來(lái)實(shí)現(xiàn)存儲(chǔ),但當(dāng)你需要與組件無(wú)關(guān)的存儲(chǔ)槽時(shí),就需要定義變量??梢詫⒅当4娴阶兞恐?,也可以從變量中讀取當(dāng)前值,就像使用組件的屬性一樣。
無(wú)論是屬性值,還是變量值,對(duì)用戶來(lái)說(shuō)都是不可見的。如果你想讓用戶看到保存在變量中的信息,只要添加塊,就可以用Label或其他用戶界面組件來(lái)顯示這些信息。
更多建議: