網頁

2020年11月3日 星期二

手機 App 程式設計- AppInventor2 -飲料點餐系統,多 Screen (螢幕)的應用

(2022/06/11 重新編寫)

題目

本程式使用到二個 Screen(螢幕,也有稱為畫面)。

1.第1個Screen , 設計可以先輸入人數,再進入飲料點餐系統。

2.第2個Screen,顯示座號挑選清單。飲料分成2個分類可選,如奶茶咖啡類和果汁類 。每個分類下面還有各種飲料名稱可選。

3.「飲料點選結果 」,顯示每一個人點選的結果。「統計結果」,顯示每一種飲料被點選的次數。

素材

本 app的參考 icon (圖示),可自己繪製或網路搜尋。

執行畫面

先輸入人數,如 班上有 35 位同學,輸入35。按「進入飲料點餐系統」鈕。
按「座號」下拉式選單。
選「座號」。
按「飲料類別」清單選擇器,如果選「奶茶咖啡類」。

如果選 「珍珠奶茶」。

結果:

如果2號選 「果汁類」,選「金桔檸檬」。
結果:
其他同學依此類推,按「統計結果」:

進入 Appinventor 

Google 輸入 appinventor。
選 MIT App Inventor 。


選 「Create Apps」。

開新專案

My projects / Start new project 。
輸入專案的 名稱,如 select_drink,按 「OK」鈕。
 專案的名稱只能使用大小寫字母、數字及「 _」符號,名稱的第一個字元必須是英文字母,不能使用中文字。

Designer (畫面編排)

Screen1:

Screen1 元件相關屬性表

元件

類別

屬性

功能

Screen1

 

Title: 飲料點餐系統
AppName:select_drink_simple,可以改成 「飲料點餐系統(簡單版)」

(出現在手機上面的App名稱)

Icon : 可以上傳1個背景透明的 icon圖。

1Screen元件就是1個螢幕(畫面)

HorizontalArrangement1

Layout(介面配置)

AlignVertical:Center:2(垂直對齊,居中:2 )
Width:Fill Parent (填滿)

水平配置元件,讓裏面的元件可以水平對齊

Label_NumberofPeople

User Interface(使用者介面)

Text:人數:

標籤

TextBox_NumberofPeople

User Interface

Width:50 pixels(像素)
NumbesrOnly:勾選 ,僅限數字
Text: 35  
Hint: (清空)

文字方塊,可以輸入 人數,預設為35

Button_enter

User Interface

Text: 進入飲料點餐系統

按鈕


App Inventor 可以切換Screen 螢幕(畫面),並可在Screen之間傳值。
按 「Add Screen」,新增1個 Screen,輸入新的 Screen名稱,如 Screen2,按 「ok」鈕。
註:Screen名稱 跟專案名稱一樣,祇能使用大小寫字母、數字及「 _」符號,名稱的第一個字元必須是英文字母,不能使用中文字。 
新的 Screen 名稱無法重新命名。
選 「Screen2」。

Screen2:

Screen2 元件相關屬性表

元件

類別

屬性

功能

Screen2

 

Title: 飲料點餐系統

 

1Screen元件就是1個螢幕(畫面)。

HorizontalArrangement1

Layout(介面配置)

AlignVertical:Center:2(垂直對齊,居中:2 )

Width:Fill Parent(填滿)

水平配置元件,讓裏面的元件可以水平對齊。

有三個元件放在裏面:Label_SeatNumber、Spinner_SeatNumber、ListPicker_drink。

Label_SeatNumber

User Interface

Text: 座號

標籤

Spinner_SeatNumber

User Interface

 

下拉式選單,用來選座號。

ListPicker_drink

User Interface

ElementsFromString(元素字串): 奶茶咖啡類,果汁類
Text: 飲料類別
Title: 飲料類別

清單選擇器

本例是第1層選單,先選飲料的類別。

Label_result

User Interface

Text: 飲料點選結果:

標籤

TextBox_result

User Interface

Height:Fill Parent
Width:Fill Parent
Hint: (清空)
MultiLine:勾選 (可以顯示多行 )
Text: (清空)

文字方塊,顯示每一個人點選的結果。

Button_total

User Interface

Text: 統計結果

按鈕

按下去可以顯示每一種飲料被點選的次數。

TextBox_total

User Interface

Height: 150  pixels
Width:Fill Parent
Hint: (清空)
MultiLine:勾選
Text: (清空)

文字方塊,顯示每一種飲料被點選的次數。

ListPicker_tea

User Interface

ShowFilterBar(顯示搜尋框) : 勾選,讓使用者可以輸入關鍵字來搜尋Text: 奶茶咖啡類
Title: 奶茶咖啡類
Visible(可見性): 取消勾選

清單選擇器

本例是第2層,顯示「奶茶咖啡類」下面的飲料品名清單。

Visible :取消勾選,一開始不會出現在畫面上。

ListPicker_juice

User Interface

ShowFilterBar(顯示搜尋框) : 勾選,讓使用者可以輸入關鍵字來搜尋Text: 果汁類
Title: 果汁類
Visible(可見性): 取消勾選

清單選擇器

本例是第2層,顯示「果汁類」下面的飲料品名清單。

Visible :取消勾選,一開始不會出現在畫面上。



多Screen的問題

  • 不要超過10 個 Screen,免得出問題。能用單一Screen設計,就儘量使用單一Screen。
  • 每個Screen有自己的變數和元件,但可以使用相同的TinyDB 來共用。在不同的Screen,放相同名稱的TinyDB,其實都指到同一個TinyDB。

Spinner(下拉式選單)、ListPicker( 清單選擇器)、ListView(清單顯示器)的差別

Spinner  在Designer(畫面編排),祇有 width (寬度)可設定。

Spinner (下拉式選單,中文 :微調器 )元件會以一個快顯視窗來顯示清單元素,預設是選第1個元素。
Spinner_SeatNumber 用來選座號,按下去。

會顯示所有的座號。

ListPicker 在Designer(畫面編排),有 height(高度)、width (寬度)可設定。


ListPicker( 清單選擇器)預設沒有選任何元素。
 ListPicker_drink 是第一層清單選擇器。

清單選擇器內的元素有二種設定方法

 1.Designer(畫面編排) 
將 ElementsFromString 此屬性欄位設定為一個由逗號隔開的字串(例如:「 奶茶咖啡類,果汁類」 這樣的格式) 


2.Blocks  (程式設計)
有二種方法:
(1)ListPicker_tea.ElementsFromString 要接 一個由逗號隔開的字串。


(2)ListPicker_drink.Elements 要接 一個由字串組成的清單。

字串(String)(AppInventor稱為 Text ,文字)與清單(List)

 initialize global tea  to "珍珠奶茶, 蜂蜜奶茶,烏龍綠茶,咖啡拿鐵",tea 是1個字串,由字元組成。
 initialize global tea_list  to create emtpy list , tea_list 是清單,初始值先設為空的清單。
set global tea_list to list from csv row  text get golbal tea,以逗號為分隔字元,把 tea 內的字串轉成 清單。tea_list 的值為 : "珍珠奶茶", "蜂蜜奶茶","烏龍綠茶","咖啡拿鐵" 。tea_list 內有4個項目(Item)或稱為 元素(Element),可用index(索引)存取 清單內的項目。清單內的項目資料型態,可為字串,也可為其它型態,如數值、清單等。

奶茶咖啡類的飲料名稱
ListPicker_drink 的 text 設定為 :飲料類別。

按下去的結果:
ListPicker_drink 的 title 設定為 :飲料類別。
ListPicker_tea和 ListPicker_juice都是第二層清單選擇器,所以Visible 不勾選,一開始不會出現。根據使用者按了第一層清單選擇器(ListPicker_drink)的那一個元素,再去打開相對應的第二層清單選擇器。
如果選「奶茶咖啡類」,因為是使用 ListPicker_tea.Open 的方法。
所以直接展開,如下:
如果 ListPicker_tea 的ShowFilterBar(顯示搜尋框) 有勾選,則會出現 Search list..
例如: 輸入 「珍」,祇會列出 開頭含有「珍」的飲料名稱。如輸入茶,會找不到,因為找不到品名是以 「茶」為開頭。

ListView (清單顯示器)在Designer(畫面編排),有 height(高度)、width (寬度)可設定。
以我設計的另一個 App 「語音行事曆」為例:
ListView 是以設定好的 height(高度)、width (寬度),直接顯示清單內的元素。而Spinner 、ListPicker 一開始是一個按鈕,當使用者按下去,才會以全螢幕來顯示清單內的元素。


 Blocks(程式設計)拼塊

跟元件有關的拼塊,要先點選該元件後,再選擇是那一個拼塊;不是元件的拼塊,可以根據拼塊的顏色,判斷是屬於那一個類別。

Screen1:



----------------------------------------------------------------
說明:
打開另1個 Screen2,startValue(起始值)為 TextBox_NumberofPeople.Text,也就是使用者輸入人數的值,傳送給Screen2。
----------------------------------------------------------------

如何選另1個Screen?

Screen/Screen2。

Screen2:



需要輸入的文字

奶茶咖啡類,果汁類
珍珠奶茶, 蜂蜜奶茶,烏龍綠茶,咖啡拿鐵
蜂蜜檸檬,金桔檸檬, 葡萄柚汁
座號
NumberofPeople tea juice seatnumber  tea_number 

程式說明

Screen2 開啟之後,可以使用 get start value 指令來接收從 Screen1 傳過來的值,再將所收到的值傳給 NumberofPeople (表示人數)

initialize global : 用來宣告一個全域(global)變數,後面的欄位可自由使用各種資料形態。全域變數可用在程式中所有的副程式或是事件程序。

global tea:表示「奶茶咖啡類」 有 「珍珠奶茶, 蜂蜜奶茶,烏龍綠茶,咖啡拿鐵」這4種飲料,每一種飲料名稱要用逗號隔開。
global juice: 表示「果汁類」 有 「蜂蜜檸檬,金桔檸檬, 葡萄柚汁」這3種飲料。
initialize global seatnumber  to 1 : 座號初值設為1。
initialize global tea_number  to 4 : 「奶茶咖啡類」 有4種飲料。
initialize global 珍珠奶茶  to 0 : 有多少人點選珍珠奶茶,初始值設為0。
每一種飲料都要用一個變數來統計,變數可用中文字。

when Screen2.initialize  , 當切換到 Screen2 要做初始化的事。
set  Spinner_SeatNumber.Prompt to 座號,設定 Spinner_SeatNumber 下拉式選單的標題為 「座號」。

for(迴圈) 從2 (初值)到 人數(終值) ,每次增加1 。
global seatnumber 最後的值為  "1,2,3,.......35"   (NumberofPeople 的值若為35,終值就是 35 )。座號下拉式選單的內容是 從1到 NumberofPeople 。


移到number 上,會出現 get number , 點選 get number  移到join 的最下面。
按 join 前面的 星號圖示,把  string  拉到 join 的下面,可增加一個字串來合併(join)。join 將字串合併在一起。

設定 Spinner_SeatNumber(座號下拉式選單)的內容為 seatnumber 此字串。
設定 ListPicker_tea(奶茶咖啡類 清單選擇器)的內容為 tea 此字串。
設定 ListPicker_juice(果汁類 清單選擇器)的內容為 juice 此字串。

When ListPicker_drink.AfterPicking 當 使用者選擇 ListPicker_drink 中某項目完成後,呼叫本事件程序。
 ListPicker_drink.SelectionIndex (選中項索引),使用者點選 ListPicker_drink 中某項目後傳回其索引值,如果選到第1項 就開啟 ListPicker_tea(奶茶咖啡類 清單選擇器)此清單快顯視窗。
否則就開啟  ListPicker_tea(果汁類 清單選擇器)此清單快顯視窗。

TextBox_result (每一個人點選的結果) : 上一次點選的結果 join  Spinner_SeatNumber (座號下拉式選單)的Selection(選中項) join "號"  join ListPicker_tea(奶茶咖啡類 清單選擇器)的Selection(選中項)  join   "\n"(換行)。
呼叫 count程序 ,參數 x 為 ListPicker_tea.SelectionIndex (選中項索引),例如選 「珍珠奶茶」,傳回索引值 1(第1個元素)。把1再傳給 count程序中的 x。
從 procedure 拉出 to  .. do 拼塊,程序名字改為 count。

滑鼠按 擴充項目鈕(有星號圖示),將 input x 放入到inputs 裏面。
to count do 此程序的說明:
從 Control  類別,拉出  if then  else if then else 拼塊。

如何增加 else if ?

滑鼠按 擴充項目鈕(有星號圖示),將 else if 放入到 else if 下面。(也可移除 else if 拼塊)
結果:

ListPicker_tea(奶茶咖啡類 清單選擇器)的清單內容為 : 珍珠奶茶, 蜂蜜奶茶,烏龍綠茶,咖啡拿鐵,有4項品名。
ListPicker_juice(果汁類 清單選擇器)的清單內容為:蜂蜜檸檬,金桔檸檬, 葡萄柚汁,有3項品名。

如果從 ListPicker_juice 挑選出來的索引值要加上 global tea_number(其值為4,因為 奶茶咖啡類有4項 ),再傳給 count程序。

因為總共有 7種飲料(奶茶咖啡類 有4項,果汁類有3項),要統計這7種飲料被點選的次數。
7項 飲料名稱 : 珍珠奶茶, 蜂蜜奶茶,烏龍綠茶,咖啡拿鐵,蜂蜜檸檬,金桔檸檬, 葡萄柚汁。
對應位置的值分別為 :1,2,3,4,5,6,7。

在 count 程序中,如果 x=1(即  珍珠奶茶 ),就把  global 珍珠奶茶 此變數累加1 否則再判斷 
如果  x=2 (蜂蜜奶茶),就把  global 蜂蜜奶茶 此變數累加1,依此類推,就可算出每一種飲料被點選的次數。


因「統計結果」鈕可以重複按,所以要先將 TextBox_total.Text 先清空。

程式編寫技巧

使用  duplicate(複製)

類似的block,請按右鍵,選「duplicate」(複製),再來修改比較快。

把 4 改成 5。

按右鍵,選「duplicate」(複製)。
按 向下三角形,選一個 正確的飲料名稱,
按右鍵,選「duplicate」(複製)。
按 向下三角形,選一個 正確的飲料名稱,

使用 Backpack(背包)

如果程式寫很長或不同Screen的程式要複製,使用  duplicate(複製),就不方便了,怎麼辦?
這時就要使用 Backpack(背包)。
按右鍵,Add to Backpack(新增至背包)
在別的地方,從背包取出。
結果:
從背包移除。

數學運算

如果要增加二個以上的運算元作運算,如 有3個運算元要相加,怎麼處理?
按 一下擴充項目(星號)圖示,將左邊的 number 移入到 + 裏面,變成有3個number,即有 3個運算元。如果將右邊的number 移出,就是減少運算元。
結果有3個運算元相加。

如果運算元很多,寫成一列就很長,可以按右鍵,選 「External Inputs」(外部輸入項)。
結果如下:
按右鍵,選 「Inline Inputs」(內嵌輸入項),又變成一列。



註解

按右鍵,選 「Add Comment」(新增註解)。

按 「?」增加註解。

按右鍵,選 「Remove Comment」(移除註解)。

程式改良

1.使用上面的方法,去算出每一種飲料的個數,每一種飲料都要使用一個變數,程式要寫很長。如果改用清單的方式,比較節省程式碼。
設計一個清單,第1個元素儲存 珍珠奶茶 的個數,第2個元素儲存蜂蜜奶茶 的個數,依此類推。
詳細程式碼,請看下面的「結合上面 1和2  修改後的專案」。

2.如果有位同學選錯,雖然 可以在「飲料點選結果」 此文字方塊內直接刪除此位同學的資料,再重新選取,但統計結果就不正確。如何修改?
設計新的程序,從 「飲料點選結果」 此文字方塊做字串剖析,先以 \n 分成多列,每一列再以分隔字元,如空白字元,分成 「座號」和「點選結果」二個子字串。
判斷「點選結果」是那一種飲料,去累加此種飲料的總數。
3.可再加入「糖」、「冰」二個 ListPicker。
糖:全糖,少糖,半糖,微糖,無糖。

冰: 正常冰,少冰,半冰,微冰, 去冰。
執行結果:
如果統計結果 要加上「 冰」、「糖」的結果就比較難,因為同一種飲料就有25種組合。

結合上面 1和2  修改後的專案

執行畫面

如果「飲料點選結果」文字方塊都沒有人工修改,按「統計結果」和 「統計結果(可修改點選結果)」,其結果都一樣。
選4號,再選飲料 為「珍珠奶茶」,分別按「統計結果」和 「統計結果(可修改點選結果)」鈕,其結果都一樣。
在「飲料點選結果」文字方塊,將 「4 號 蜂蜜檸檬」刪除,按「統計結果」鈕,其結果是錯的,因為 已沒有「蜂蜜檸檬」。

按「統計結果(可修改點選結果)」鈕,其結果是對的。因為是從「飲料點選結果」此文字方塊的內容,重新統計每一種飲料出現的次數。
故意將飲料名稱刪掉幾個字:

將原來專案 另存新專案
Projects / Save project as ... 。 

專案 名為 :select_drink_modify,即可修改點選結果。

Screen1:
AppName:改成 「飲料點餐系統(可修改點選結果))」。
Screen2:
增加1個 「Button_total_modify」按鈕,text 文字為 : 統計結果(可修改點選結果)。

Screen 2的 Block


----------------------------------------------------------------
說明:
separator(分隔字元),設為空白。
seatnumber :座號從1開始編。
tea :奶茶咖啡類的飲料名稱,「珍珠奶茶, 蜂蜜奶茶,烏龍綠茶,咖啡拿鐵」這4種飲料,每一種飲料名稱要用逗號隔開,為字串資料。
juice  :果汁類的飲料名稱,有 「蜂蜜檸檬,金桔檸檬, 葡萄柚汁」這3種飲料,為字串資料。
tea_list: 奶茶咖啡類的飲料清單。
juice_list: 果汁類的飲料清單。
tea_number : 屬於奶茶咖啡類的飲料個數。
juice_number : 屬於果汁類的飲料個數。
drink_number:所有飲料的數目,即 tea_number+ juice_number。
drink_name:所有飲料名稱的清單。 即 "珍珠奶茶", "蜂蜜奶茶","烏龍綠茶","咖啡拿鐵","蜂蜜檸檬","金桔檸檬", "葡萄柚汁" 。
drink_kind:此清單存放各種飲料被點選的次數,第1項是放第1種飲料被點選的次數。如果沒有修改預設值,那就是「珍珠奶茶」被點選的次數。
----------------------------------------------------------------

----------------------------------------------------------------
說明:
drink_name:所有飲料名稱的清單。
將 tea_list  所有項目附加(append)在 drink_name的後面。
drink_name 初值為空的清單,append後的結果為 :"珍珠奶茶", "蜂蜜奶茶","烏龍綠茶","咖啡拿鐵"。
將 juice_list  所有項目附加(append)在 drink_name的後面。
drink_name  append後的結果為  :"珍珠奶茶", "蜂蜜奶茶","烏龍綠茶","咖啡拿鐵","蜂蜜檸檬","金桔檸檬", "葡萄柚汁" 。
----------------------------------------------------------------



----------------------------------------------------------------
說明:
如何設定區域性的變數?

如何增加區域性的變數?
按 initialize local result_list  前面的星號,跳出一個方塊,把 name x 移到 local names ,就增加1個區域性的變數。 如果把 name x 移出 local names,就減少1個區域變數。

因為這三個變數,祇在 to  count_textbox_result  do 此程序內使用,其它地方沒用到,所以宣告為 區域性的變數。

result_list : 將「飲料點選結果」此文字方塊內的文字,以\n (換行符號) 來分割(分解)(split),結果放在 result_list 清單變數內。
如果「飲料點選結果」文字方塊的內容:
以\n (換行符號) 來分割,結果放在 result_list ,其內容為  : "1號 珍珠奶茶","2號 蜂蜜檸檬"。
result_list 含有2個項目(或稱為元素)。



one_line_list : 從 result_list ,1次取1個項目(即1行),再以 separator 變數為分割字元(以本例來說,就是空白),分割後的結果放在 one_line_list。
以上述為例,result_list 的第1個項目(即第1行為 "1號 珍珠奶茶" ,以空白為分割字元,分割後的結果,放在  one_line_list。  one_line_list 的內容為 : "1號","珍珠奶茶"。
one_line_list 分成「座號」和「點選結果」(飲料名稱) 二個項目。

drink_index : one_line_list的第2個項目(即飲料名稱),是在 drink_name(所有飲料名稱清單)那一個位置。如果找不到,就傳回 0 。

select list item list index   : 從 one_line_list , 選擇第2個項目(即飲料名稱)。
index in list thing list :  thing 此物件在 drink_name 清單中的索引值(即位置)。
如果 one_line_list 的第2個項目 為 珍珠奶茶, 那就傳回 1。

drink_name 為 "珍珠奶茶", "蜂蜜奶茶","烏龍綠茶","咖啡拿鐵","蜂蜜檸檬","金桔檸檬", "葡萄柚汁" 。 "珍珠奶茶" 在 drink_name  的第1個位置,即索引值為1。

如果 不加上trim 此函數,結果會變成 0。 也就是找不到。
trim: 修剪。強制轉成字串,前後的空白字元會被剪掉。



drink_index >0 , 表示可以在  drink_name 找到使用者點選飲料名稱的位置。



選擇(select)  drink_kind 清單 的 drink_index 此位置,把資料抓出來,再加1。 
drink_kind:此清單存放各種飲料被點選的次數,第1項是放第1種飲料被點選的次數。

把累加後的結果,放在drink_kind 清單 drink_index  此位置內,原來的值被取代掉(replace)。
----------------------------------------------------------------

結論

當你完成這二個專案,對於 Appinventor  的寫作技巧,就比較清楚了。global 和local 變數的使用時機、list 的觀念、三種選單的差別等。對於專題實作的能力,應該有很大的幫忙。

進階

請看我的另一篇文章 :