第 8 章 總統(tǒng)測驗

2018-02-24 15:51 更新

“總統(tǒng)測驗”是一個關(guān)于美國前總統(tǒng)的問答游戲。雖然測驗的內(nèi)容與總統(tǒng)有關(guān),但你可以把它當(dāng)作模板,來實現(xiàn)對任何題目的測驗。

在前幾章中,你已經(jīng)了解了一些編程的基本概念?,F(xiàn)在,準(zhǔn)備好面對更大的挑戰(zhàn)吧。你會發(fā)現(xiàn),無論是編程技巧,還是抽象思維,這一章都要求你有一個概念性的飛躍。特別需要強調(diào)的是,本章將使用兩個列表變量來存儲數(shù)據(jù)——應(yīng)用中的問題和答案,使用索引變量來跟蹤用戶正在回答的題目。在本章結(jié)束時,對于創(chuàng)建測驗類應(yīng)用和其他需要使用列表的應(yīng)用,你已經(jīng)掌握了必要的知識。

{%}

本章假設(shè)你已經(jīng)熟悉了App Inventor的基礎(chǔ)知識:使用組件設(shè)計器構(gòu)建用戶界面,用塊編輯器來定義事件處理程序并為組件添加行為。如果你還不熟悉,在繼續(xù)學(xué)習(xí)之前,請復(fù)習(xí)前面幾章。

在測驗中,用戶通過單擊“下一題”按鈕,連續(xù)地回答問題,并收到回答是否正確的反饋。

學(xué)習(xí)要點

如圖8-1所示,本章覆蓋以下內(nèi)容:

  • 定義列表變量:用來存儲問題和答案;

  • 使用索引遍歷列表,用戶每次點擊“下一題”按鈕時,顯示下一個問題;

  • 使用條件語句(if)控制行為:只有在特定條件下才能執(zhí)行某些操作。在用戶測驗到最后一題時,將使用if塊來處理程序;

  • 每一道題對應(yīng)一張不同的圖片,要實現(xiàn)圖片的切換。

{%}

圖 8-1 “總統(tǒng)測驗”在手機中

準(zhǔn)備開始

登陸App Inventor網(wǎng)站,創(chuàng)建新項目“PresidentsQuiz”,并設(shè)置屏幕的標(biāo)題為“總統(tǒng)測驗”,連接測試設(shè)備。從appinventor網(wǎng)站下載測驗中用到的圖片:roosChurch.gif,nixon.gif,carterChina.gif和atomic.gif。在下一節(jié)中將這些圖片加載到項目中。

設(shè)計組件

“總統(tǒng)測驗”應(yīng)用的界面很簡單:顯示問題并允許用戶來回答。圖8-2顯示了應(yīng)用在組件設(shè)計器中的截圖,按圖來創(chuàng)建組件。

{%}

圖 8-2 組件設(shè)計器中的“總統(tǒng)測驗”

首先將下載的圖片加載到項目中:單擊Media區(qū)域的Upload File按鈕,選擇一個文件(如roosChurch.gif),其他圖片也是如此。然后添加表8-1中列出的組件。

表8-1 “總統(tǒng)測驗”應(yīng)用所需組件

組件類型 面板中分組 命名 作用
Image User Interface Image1 與問題一同顯示的圖片
Label User Interface QuestionLabel 顯示正在回答的問
HorizontalArrangement Layout HorizontalArrangement1 放置答案輸入框及“提交”按鈕
TextBox User Interface AnswerText 用戶在此輸入答案
Button User Interface AnswerButton 用戶點擊之后提交答案
Label User Interface RightWrongLabel 顯示“正確”或“不正確”的反饋
Button User Interface NextButton 用戶點擊進(jìn)入下一題

按照下面提示設(shè)置組件屬性:

  • Image1:Picture為roosChurch.gif(最先出現(xiàn));Width為“Fill parent”,Height為200;

  • QuestionLabel:Text為“問題…”(在塊編輯器中輸入第一個問題);

  • AnswerText:Hint為“輸入回答”,Text為空,放置到HorizontalArrangement1中;

  • AnswerButton:Text為“提交”,放置到HorizontalArrangement1中;

  • NextButton:Text為“下一步”;

  • RightWrongLabel:Text為空。

為組件添加行為

編程來實現(xiàn)以下行為:

  • 應(yīng)用啟動時,顯示第一個問題以及相應(yīng)的圖片;

  • 點擊“下一題”按鈕時,顯示第二題,再次點擊,顯示第三題,以此類推;

  • 當(dāng)顯示最后一題時,點擊“下一題”按鈕將回到第一題;

  • 在用戶回答問題之后,反饋回答是否正確;

首先按照表8-2的提示,定義兩個列表變量:QuestionList用來保存問題,AnswerList用來保存答案。圖8-3顯示在塊編輯器中創(chuàng)建的兩個列表。

表8-2 用于保存問題和答案的列表變量

塊的類型 所在抽屜 作用
Initialize global QuestionList to Variables 保存問題的列表(更名為QuestionList)
Initialize global AnswerList to Variables 保存答案的列表(更名為AnswerList)
make a list Lists 為QuestionList插入列表項
問題內(nèi)容(三個) Text 問題
make a list Lists 為AnswerList插入列表項
答案內(nèi)容(三個) Text 答案

{%}

圖 8-3 問題及答案列表

定義索引變量

在整個測試過程中,每次用戶點擊“下一題”按鈕,都要跟蹤用戶正在回答的問題。定義變量currentQuestionIndex作為QuestionList和AnswerList的索引值。表8-3列出了所需的塊,圖8-4顯示了變量的定義。

表8-3 創(chuàng)建索引

塊的類型 在抽屜 作用
Initialize global currentQuestionIndex to Variables 保存當(dāng)前問題(與答案)的索引(位置)
數(shù)字1 Math 將currentQuestionIndex的初始值設(shè)為1(第一題)

{%}

圖 8-4 索引變量的初始值為1

顯示第一個問題

有了這些變量,就可以為應(yīng)用設(shè)定交互行為。無論是何種應(yīng)用,漸進(jìn)式的開發(fā)是非常重要的,而且每一步只定義一個行為。我們首先考慮與問題相關(guān)的行為,具體而言,在應(yīng)用啟動時顯示列表中的第一道題,稍后再來處理圖片的事情。

代碼塊的設(shè)定應(yīng)該與列表中的具體問題無關(guān),這樣,如果需要更換問題或創(chuàng)建新的測驗類應(yīng)用時,只需改變列表中的具體問題,而不必修改事件處理程序。

鑒于上述考慮,對于第一道題,不要直接引用“哪位總統(tǒng)在大蕭條時期實施了‘新政’?”這樣的題目內(nèi)容,而是引用“QuestionList的第一個插槽”這樣抽象的形式(與具體問題無關(guān))。這樣,即使第一個插槽中的問題改變了,這些程序塊仍然有效。

select list item塊用來選擇列表中的項,使用中要求指定list(列表)及index(索引)(列表中的位置)。如果列表中有三個項,可以輸入1、2或3作為索引。

第一個行為是,在應(yīng)用啟動時選擇QuestionList中的第一道題,將其寫入QuestionLabel;還記得“Android,我的車在哪兒?”的應(yīng)用吧,如果想讓某件事發(fā)生在應(yīng)用啟動時,可以將有關(guān)指令放在Screen1.Initialize事件處理程序中,表8-4中列出所需的塊。

表8-4 應(yīng)用啟動時加載第一個問題所需的塊

塊的類型 所在抽屜 作用
Screen1.Initialize Screen1 應(yīng)用啟動時觸發(fā)該事件
set QuestionLabel.Text to QuestionLabel 將第一道題內(nèi)容寫入QuestionLabel
select list Item Lists 從QuestionList中選擇第一道題
get Global QuestionList Variables 從其中選擇問題的列表
數(shù)字1 Math 用索引值1來選擇第一道題

塊的作用

應(yīng)用啟動時觸發(fā)Screen1.Initialize事件。如圖8-5所示,變量QuestionList中的第一項被選中,并被寫入QuestionLabel.Text。因此,應(yīng)用啟動時,用戶會看到第一道題。

{%}

圖 8-5 應(yīng)用啟動時選擇并顯示第一道題

 測試:連接裝有AI伴侶的設(shè)備,或點擊“connect?Emulator”打開Android模擬器。當(dāng)應(yīng)用啟動后,你是否看到QuestionList中的第一道題:“哪位總統(tǒng)在大蕭條時期實施了'新政'?”

遍歷所有問題

現(xiàn)在為“下一題”按鈕的行為編程。之前定義的currentQuestionIndex用來記住用戶正在回答的問題,現(xiàn)在設(shè)定當(dāng)用戶單擊“下一題”時,為currentQuestionIndex加1(即,從1變?yōu)?,或從2變?yōu)?,依此類推),并根據(jù)currentQuestionIndex的值來選擇并顯示新的問題。挑戰(zhàn)一下你自己,看看是否可以自己搭建這些塊。完成之后,與圖8-6進(jìn)行對照。

{%}

圖 8-6 顯示下一題

塊的作用

第一行的塊讓變量currentQuestionIndex遞增。如果當(dāng)前值為1則加到2;如果是2則加到3,以此類推。一旦currentQuestionIndex值改變,應(yīng)用將以此來選擇新的問題并顯示。首次單擊“下一題”時,currentQuestionIndex從1變?yōu)?,應(yīng)用將選擇并顯示QuestionList中的第二道題:“哪位總統(tǒng)在1979年實現(xiàn)中美建交?”;第二次單擊“下一題”時,currentQuestionIndex從2變?yōu)?,應(yīng)用將選擇并顯示QuestionList中的第三道題:“哪位總統(tǒng)因水門事件而辭職?”

 測試:測試“下一題”按鈕,看看應(yīng)用運行是否正常。在手機上按“下一題”按鈕,是否顯示第二題“哪位總統(tǒng)在1979年實現(xiàn)中美建交?”?應(yīng)該是的;再按“下一題”,應(yīng)該出現(xiàn)第三題。但如果再次點擊,就會看到錯誤提示:“Attempting to get item 4 of a list of length 3.(試圖從只有3個項的列表中獲取第4項。)”這就是程序的bug!知道原因嗎?在繼續(xù)閱讀之前試試看自己解決它。

當(dāng)點擊“下一題”按鈕時,應(yīng)用要問一個問題,并根據(jù)問題的答案執(zhí)行不同的操作。既然已知QuestionList中包含三個問題,問題可以這樣來問:“currentQuestionIndex是否>3?”如果是,將currentQuestionIndex設(shè)回1,這樣就回到了第一道題。表8-5中列出了所需的塊。

表8-5 檢查索引值是否到了列表的結(jié)尾所需的塊

塊的類型 所在抽屜 作用
if Control 判斷用戶是否正在做最后一題
= Math 檢查currentQuestionIndex的值是否為3
get global currentQuestionIndex Variables 放入“=”左邊的插槽
數(shù)字3 Math 放入“=”右邊的插槽
set global currentQuestionIndex to Variables 設(shè)為1來轉(zhuǎn)回到第一道題
數(shù)字1 Math 設(shè)置索引值為1

 測試:單擊手機上的“下一題”按鈕,會照常出現(xiàn)第二題“哪位總統(tǒng)在1979年實現(xiàn)中美建交?”,繼續(xù)點擊“下一題”,將顯示第三題。下面是你真正想測的:如果再次點擊,將出現(xiàn)第一題(“哪位總統(tǒng)在大蕭條時期實施了‘新政’?”)。

{%}

圖 8-7 檢查索引值遞增

單擊“下一題”時,索引照舊會遞增。但程序會檢查是否currentQuestionIndex>3(問題的數(shù)量)。如果大于3,則將currentQuestionIndex重新設(shè)置為1,并顯示第一題;如果≤3,則不執(zhí)行if塊內(nèi)的程序,并照常顯示當(dāng)前問題。

{%}

圖 8-8 檢查測驗是否到了最后一題(第三題)

讓測驗易于修改

如果NextButton.Click中的塊能夠正常運行,恭喜你,你正在成為一名合格的程序員!但是,如果想在測驗中添加新題目(及答案),該怎么辦?這些塊還能正常運行嗎?為了驗證這一點,先在QuestionList中添加第四道題,并在AnswerList中添加第四個答案,如圖8-9。

{%}

圖 8-9 向兩個列表中分別添加一項

 測試:多次單擊“下一題”按鈕,你發(fā)現(xiàn)無論點擊多少次,第四題始終不出現(xiàn)。知道問題所在嗎?在繼續(xù)閱讀之前,嘗試做些修改,以便讓第四題出現(xiàn)。

問題出在“最后一題”的判斷條件太具體:currentQuestionIndex>3。如果把3改為4,程序正常了,但問題是,每次增減問題和答案時,都要記著修改判斷條件。計算機程序中的這種強相關(guān)性最容易導(dǎo)致錯誤,特別是當(dāng)程序變得復(fù)雜時。好的對策是讓程序的設(shè)計與列表中的問題數(shù)量無關(guān)。這種通用性,對于程序員來說,當(dāng)你想創(chuàng)建其他專題的定制測驗時,可以讓程序的移植更加容易。尤其是在處理動態(tài)列表時,這樣做是必須的,例如,測驗中允許用戶添加新問題(見第10章)。一個通用性好的程序不該與3這樣的具體數(shù)字相關(guān)聯(lián),因為這只對那些有三個問題的測驗有效。對currentQuestionIndex的判斷條件應(yīng)該是QuestionList列表的長度(項數(shù)),而不是具體數(shù)字。當(dāng)條件更具通用性時,即使是添加或刪除QuestionList中的項,程序也能正常運行?,F(xiàn)在修改NextButton.Click事件處理程序,替換掉具體數(shù)字3。表8-6中列出了所需要的塊。

表8-6 檢查列表長度所需的塊

塊的類型 所在抽屜 作用
length of list Lists 詢問列表QuestionList中有多少個列表項
get global QuestionList Variables 插入length of list塊的list插槽中

塊的作用

If塊中將currentQuestionIndex值與QuestionList的列表長度進(jìn)行比較,如圖8-10所示。如果currentQuestionIndex為5,而QuestionList的長度為4,則currentQuestionIndex將被重新設(shè)置為1。值得注意的是:由于程序塊不再與3或任何具體數(shù)字相關(guān)聯(lián),因此無論列表中有多少項,程序都將正常運行。

{%}

圖 8-10 采取更加通用的方式檢查列表的結(jié)尾

 測試:當(dāng)單擊“下一題”按鈕時,程序是否在四個問題間循環(huán)?在第四題后是否又回到第一題?

為每道題切換圖片

現(xiàn)在程序已經(jīng)可以遍歷所有的問題(而且代碼更加聰明靈活,也更抽象),下面來設(shè)置圖片。眼下無論顯示什么問題,圖片都是同一個,我們希望當(dāng)用戶單擊“下一題”時,圖片與問題相匹配。此前在Media中載入了四張圖片,現(xiàn)在用圖片的文件名來創(chuàng)建第三個列表PictureList。然后修改NextButton.Click事件處理程序,同時切換問題與圖片。(想到currentQuestionIndex就說明你已經(jīng)開竅了?。┦紫葎?chuàng)建列表PictureList,用圖片文件名初始化列表,要保證列表中的文件名與先前加載的圖片文件名完全相同。圖8-11顯示了PictureList塊的樣子。

{%}

圖 8-11 PictureList中用圖片文件名來充當(dāng)列表項

下面來修改NextButton.Click事件處理程序,以便圖片可以隨問題索引的改變而改變。Image組件的Picture屬性用于指定要顯示的圖片。表8-7中列出了修改NextButton.Click所需的塊。

表8-7 顯示與問題相匹配的圖片所需的塊

塊的類型 所在抽屜 作用
set Image1.Picture to Image1 改變圖片
select list Item Lists 選擇一個與當(dāng)前問題相匹配的圖片
global PictureList Variables 從列表中選擇一個文件名
get global currentQuestionIndex Variables 選擇第currentQuestionIndex項

塊的作用

rrentQuestionIndex同時充當(dāng)QuestionList和PictureList兩個列表的索引,這要求正確設(shè)置各個列表,如,第一題對應(yīng)第一個答案及第一張圖,第二題對應(yīng)第二個答案及第二張圖,依此類推,這樣一個索引值可用于三個列表,如圖8-12所示。舉例說明:第一張圖roosChurch.gif是羅斯??偨y(tǒng)的圖(與英國首相丘吉爾在一起),而“羅斯?!笔堑谝粋€問題的答案。

{%}

圖 8-12 每次選擇與問題匹配的第currentQuestionIndex張圖片

 測試:多次點擊“下一題”,每次點擊是否出現(xiàn)不同的圖片?

檢查用戶答案

現(xiàn)在應(yīng)用已經(jīng)可以遍歷所有的試題及答案(及匹配答案的圖片),這是列表應(yīng)用的極好案例。但真實的測驗要對用戶的回答判斷正誤。下面添加一些塊來告訴用戶他的回答是否正確。用戶在AnswerText中輸入答案,并點擊AnswerButton提交答案;程序用Ifelse塊將用戶輸入與標(biāo)準(zhǔn)答案作比較,并用RightWrongLabel顯示比較結(jié)果。表8-8列出了程序中用到的塊。

表8-8 用于顯示答案是否正確的塊

塊的類型 所在抽屜 作用
AnswerButton.Click AnswerButton 點擊AnswerButton按鈕時觸發(fā)該事件
ifelse Control 如果回答正確,做一件事,否則做另一件事
= Math 判斷回答是否正確
AnswerText.Text AnswerText 包含了用戶的回答
select list Item Lists 從AnswerList列表中選擇當(dāng)前問題的答案
get global AnswerList Variables 答案的列表
get global currantQuestionIndex Variables 當(dāng)前用戶正在回答的問題的索引值
set RightWrongLabel.Text to RightWrongLabel 顯示回答是否正確
“正確” Text 回答正確時顯示
set RightWrongLabel.Text to RightWrongLabel 顯示回答是否正確
“不正確” Text 回答錯誤時顯示

塊的作用

在圖8-13中,Ifelse塊用來檢驗用戶的輸入(AnswerText.Text)是否等于AnswerList中的第currentQuestionIndex項。如果currentQuestionIndex=1,程序?qū)⒂脩舻幕卮鹋cAnswerList中的第一項“羅斯?!弊鲗Ρ龋瑯?,如果currentQuestionIndex=2,則與AnswerList中的第二項“卡特”作對比,等等。如果對比結(jié)果相同,則執(zhí)行then塊,即RightWrongLabel顯示“正確!”;如果對比結(jié)果不同,執(zhí)行else塊,即RightWrongLabel顯示“不正確!”。

{%}

圖 8-13 檢查用戶的回答,并告訴用戶答案是否正確

 測試:嘗試回答一道題,程序會顯示你的回答是否正確。分別試驗正確和錯誤的回答。你會注意到,回答正確,意味著你的輸入必須與AnswerList中的答案完全匹配(包括大小寫、標(biāo)點或空格)。繼續(xù)測試后面的問題,并確認(rèn)運行正常。

應(yīng)用運行正常,但你會看到,當(dāng)單擊“下一題”時,雖然圖片和問題都切換到下一題,但“正確!”或“不正確!”的文本以及前一題中輸入的回答仍然顯示在屏幕上,如圖8-14所示。盡管這一點無傷大雅,但用戶肯定會發(fā)現(xiàn)這類的界面問題。將RightWrongLabel及AnswerText清空,需要在NextButton.Click事件處理程序中添加幾個塊,表8-9列出了所需的塊。

{%}

圖 8-14 用戶界面上的小問題

表8-9 清除RightWrongLabel及AnswerText的塊

塊的類型 所在抽屜 作用
set RightWrongLabel.Text to RightWrongLabel 需要清空內(nèi)容的label
“” Text 當(dāng)用戶點擊“下一題”時,刪除對上一題回答的反饋
set AnswerText.Text to AnswerText 用戶對上一題的回答
“” Text 當(dāng)用戶點擊“下一題”時,刪除對上一題的回答

塊的作用

用戶單擊“下一題”時,圖8-15中的前兩行用于清空RightWrongLabel和AnswerText。

{%}

圖 8-15 當(dāng)轉(zhuǎn)入下一題時,清空上一題的答案及對答案的反饋

 測試:回答一個問題,然后點擊“提交”,再單擊Next按鈕,上一題的答案及反饋是否消失了?

完整的應(yīng)用:總統(tǒng)知識測驗

圖8-16與8-17顯示了“總統(tǒng)測驗”應(yīng)用中塊的最終配置。

{%}

圖 8-16 “總統(tǒng)測驗”應(yīng)用中塊的最終配置(之一)

{%}

圖 8-17 “總統(tǒng)測驗”應(yīng)用中塊的最終配置(之二)

改進(jìn)

一旦測驗應(yīng)用開始正常運行,你也許會樂于做一些改進(jìn),例如:

  • 現(xiàn)在應(yīng)用中只顯示與問題有關(guān)的圖片,也可以嘗試播放錄音或視頻片段。在使用聲音上,你甚至可以發(fā)展出一款“辯聲識曲(Name That Tune)”的應(yīng)用;

  • 本測驗對正確答案的要求過于嚴(yán)格,有幾種改進(jìn)方法:一是使用text.contains塊,來檢查是用戶的輸入中是否包含了真正的答案;另一種方法是給每道題提供多個答案,通過遍歷(foreach)來檢查是否與標(biāo)準(zhǔn)答案相匹配;你還可以想辦法處理掉那些用戶輸入的多余空格,或者不做大小寫區(qū)分,等等;

  • 將測驗改為多選題,這需要用另一個列表來保存每個問題的可選答案。答案也可能是一個二級列表,第二級列表中保存著特定問題的可選答案。使用ListPicker組件,讓用戶來選擇答案。更多關(guān)于lists的內(nèi)容請參見第19章。

小結(jié)

下面是本教程中所涉及到的概念:

  • 將應(yīng)用程序劃分為數(shù)據(jù)(通常保存在列表中)及事件處理程序兩個部分;使用Ifelse塊來做條件判斷,有關(guān)條件語句的更多信息,請參見第18章;

  • 在事件處理程序中,程序塊只能引用抽象的名稱來指代列表項及列表長度,以便當(dāng)列表數(shù)據(jù)發(fā)生變化時,程序還可以正常運行;

  • 索引變量可以跟蹤當(dāng)前項在列表中的位置,當(dāng)索引遞增時,要小心列表的末尾,使用if塊來處理應(yīng)用中的行為。
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號