這次內容和前面的 BMI 不同,主要是做「學生資料表單」,包含 EditText、RadioButton、CheckBox、Button,以及將選擇結果即時顯示在畫面上
前面幾天已經陸續接觸 Android 的基本 UI 元件,今天 要把這些元件組合起來,製作一個比較完整的 「學生資料輸入 App」。
這次實作的重點不只是把元件放到畫面上,而是讓使用者可以真正操作表單:
輸入姓名 → 選擇性別 → 勾選興趣 → 輸入其他興趣 → 按下 OK → 顯示學生資料
另外還加入 Cancel 功能,可以將輸入內容清除,讓整個 App 具有基本的表單操作流程。
這次作業的主畫面標題為:
學生資料
畫面主要包含以下資料:
Name:姓名
性別:
○ 男生
○ 女生
興趣:
☑ 打球
☑ 看電影
☑ 讀書
□ 其它 [Play Piano]
[ Cancel ] [ OK ]
最下面則是結果顯示區。
例如輸入:
Name:JAY
性別:男生
興趣:
☑ 打球
☑ 看電影
☑ 讀書
按下 OK 後,畫面會顯示:
姓名: JAY
性別: 男
興趣: 打球 看電影 讀書
這表示我們已經把「使用者輸入」成功轉換成「程式可以處理的資料」,最後再將結果更新到畫面。
這次學生資料 App 使用到幾個非常重要的 Android UI 元件:
EditText
RadioGroup
RadioButton
CheckBox
Button
TextView
每個元件負責的功能不同。
| 元件 | 功能 |
|---|---|
| EditText | 輸入姓名及其他興趣 |
| RadioGroup | 管理性別的單選 |
| RadioButton | 男生、女生二選一 |
| CheckBox | 興趣複選 |
| Button | Cancel、OK |
| TextView | 顯示最後輸入結果 |
這些都是 Android 表單設計非常常見的元件。
畫面最上方提供:
Name:
讓使用者輸入自己的姓名。
XML 可以使用:
<EditText
android:id="@+id/editName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Name" />
接著在 Kotlin 中取得輸入內容:
// 取得姓名輸入框中的文字
val name = editName.text.toString()
假設輸入:
JAY
那麼:
val name = editName.text.toString()
取得的結果就是:
JAY
接下來是:
性別:
○ 男生
○ 女生
這裡和興趣最大的差別在於:
性別在這個表單中只允許選擇一個。
所以非常適合:
RadioGroup
+
RadioButton
例如 XML:
<RadioGroup
android:id="@+id/radioGroupGender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/radioMale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="男生" />
<RadioButton
android:id="@+id/radioFemale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="女生" />
</RadioGroup>
把兩個 RadioButton 放進同一個 RadioGroup 後,就可以做到互斥選擇。
也就是:
選男生
↓
男生 ●
女生 ○
如果改選女生:
男生 ○
女生 ●
不需要自己另外寫程式取消另一個選項。
接著就可以在 Kotlin 判斷目前選擇的性別:
// 根據 RadioButton 的 checked 狀態判斷性別
val gender = when {
radioMale.isChecked -> "男"
radioFemale.isChecked -> "女"
else -> "未選擇"
}
這裡使用 Kotlin 的:
when
非常適合處理這種多條件判斷。
如果:
radioMale.isChecked == true
那:
gender = 男
如果:
radioFemale.isChecked == true
則:
gender = 女
接下來是今天很重要的另一個元件:
興趣:
☑ 打球
☑ 看電影
☑ 讀書
這裡不能使用 RadioButton。
因為一個人可能同時喜歡:
打球
+
看電影
+
讀書
所以必須使用:
CheckBox
例如:
<CheckBox
android:id="@+id/checkBall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="打球" />
<CheckBox
android:id="@+id/checkMovie"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="看電影" />
<CheckBox
android:id="@+id/checkBook"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="讀書" />
與 RadioButton 最大不同就是:
RadioButton
→ 通常單選
CheckBox
→ 可以複選
接著要把使用者勾選的興趣組合起來。
可以先建立:
// 建立 MutableList 儲存使用者選擇的興趣
val hobbies = mutableListOf<String>()
然後逐一判斷:
// 如果有勾選「打球」,加入興趣清單
if (checkBall.isChecked) {
hobbies.add("打球")
}
// 如果有勾選「看電影」,加入興趣清單
if (checkMovie.isChecked) {
hobbies.add("看電影")
}
// 如果有勾選「讀書」,加入興趣清單
if (checkBook.isChecked) {
hobbies.add("讀書")
}
假設:
☑ 打球
☑ 看電影
☑ 讀書
那麼 hobbies 裡面就會變成:
["打球", "看電影", "讀書"]
接著利用:
val hobbyText = hobbies.joinToString(" ")
就能把 List 轉換成:
打球 看電影 讀書
這個 joinToString() 在處理多選資料時非常方便。
這次影片裡還有一個很有意思的功能:
□ 其它 Play Piano
因為我們不可能事先把所有興趣都列出來。
所以除了:
打球
看電影
讀書
之外,再提供:
其它
讓使用者自己輸入。
例如:
其它:Play Piano
程式可以這樣處理:
// 如果使用者勾選「其它」
if (checkOther.isChecked) {
// 取得其它興趣輸入內容
val other = editOther.text.toString().trim()
// 確定不是空字串才加入
if (other.isNotEmpty()) {
hobbies.add(other)
}
}
如此一來,如果勾選:
☑ 打球
☑ 讀書
☑ 其它:Play Piano
最後就可以得到:
興趣: 打球 讀書 Play Piano
這是一種很實用的表單設計方式。
接下來就是整個 App 的核心。
當使用者按:
OK
程式要完成:
1. 讀取姓名
2. 判斷性別
3. 取得興趣
4. 取得其它興趣
5. 組合文字
6. 顯示結果
完整邏輯可以寫成:
btnOk.setOnClickListener {
// ==============================
// 1. 取得姓名
// ==============================
val name = editName.text.toString().trim()
// ==============================
// 2. 判斷性別
// ==============================
val gender = when {
radioMale.isChecked -> "男"
radioFemale.isChecked -> "女"
else -> "未選擇"
}
// ==============================
// 3. 建立興趣清單
// ==============================
val hobbies = mutableListOf<String>()
// ==============================
// 4. 判斷各 CheckBox
// ==============================
if (checkBall.isChecked) {
hobbies.add("打球")
}
if (checkMovie.isChecked) {
hobbies.add("看電影")
}
if (checkBook.isChecked) {
hobbies.add("讀書")
}
// ==============================
// 5. 處理「其它」興趣
// ==============================
if (checkOther.isChecked) {
val other = editOther.text
.toString()
.trim()
if (other.isNotEmpty()) {
hobbies.add(other)
}
}
// ==============================
// 6. 將興趣 List 組合成字串
// ==============================
val hobbyText = hobbies.joinToString(" ")
// ==============================
// 7. 組合最後輸出結果
// ==============================
val result = """
姓名: $name
性別: $gender
興趣: $hobbyText
""".trimIndent()
// ==============================
// 8. 顯示到 TextView
// ==============================
textResult.text = result
}
這就是今天整個作業最重要的程式邏輯。
例如輸入姓名:
JAY
性別:
● 男生
○ 女生
興趣選擇:
☑ 打球
☑ 看電影
☑ 讀書
按下:
OK
畫面結果:
姓名: JAY
性別: 男
興趣: 打球 看電影 讀書
代表:
EditText
↓
RadioButton
↓
CheckBox
↓
Button
↓
Kotlin
↓
TextView
整個流程已經成功串接。
接著進一步測試。
如果把:
☑ 看電影
取消掉,變成:
☑ 打球
□ 看電影
☑ 讀書
程式再次取得 isChecked 狀態後,就只會把:
打球
讀書
加入 hobbies。
因此最後結果會變成:
興趣: 打球 讀書
這就是 CheckBox 最大的特色。
每個 CheckBox 都有自己的:
isChecked
可以分別判斷。
除了 OK 之外,畫面還提供:
Cancel
它的用途就是讓使用者重新填寫資料。
可以寫:
btnCancel.setOnClickListener {
// ==============================
// 清除姓名
// ==============================
editName.text.clear()
// ==============================
// 清除性別
// ==============================
radioGroupGender.clearCheck()
// ==============================
// 清除所有興趣
// ==============================
checkBall.isChecked = false
checkMovie.isChecked = false
checkBook.isChecked = false
checkOther.isChecked = false
// ==============================
// 清除其它興趣
// ==============================
editOther.text.clear()
// ==============================
// 清除結果
// ==============================
textResult.text = ""
}
這樣按下 Cancel:
姓名
性別
興趣
其它
結果
就可以全部恢復成初始狀態。
今天這個範例剛好非常適合比較兩者。
適合:
○ 男生
○ 女生
因為通常只能選擇其中一項。
所以:
RadioButton
=單選
搭配:
RadioGroup
可以自動完成互斥選擇。
適合:
☑ 打球
☑ 看電影
☑ 讀書
☑ 其它
因為這些項目:
可以選一個
可以選兩個
可以全部選
也可以全部不選
所以:
CheckBox
=複選
這也是今天一定要理解的差別。
這次作業看起來只是:
學生資料輸入
實際上卻已經包含 App 開發非常重要的資料處理流程:
使用者輸入
↓
取得 View 資料
↓
判斷元件狀態
↓
整理資料
↓
產生結果
↓
更新畫面
其中:
EditText
→ 文字輸入
RadioButton
→ 單選資料
CheckBox
→ 複選資料
Button
→ 觸發事件
TextView
→ 顯示結果
這些觀念之後做:
會員註冊
問卷調查
個人資料
商品規格
餐點選擇
預約系統
設定頁面
其實都會再次遇到。
這次還有一個我覺得很重要的地方:
val hobbies = mutableListOf<String>()
以前學 Kotlin 的:
List
MutableList
add()
joinToString()
可能會覺得只是單純語法。
但是今天真正放到 Android App 後,就可以理解它們的用途。
例如:
val hobbies = mutableListOf<String>()
if (checkBall.isChecked) {
hobbies.add("打球")
}
if (checkMovie.isChecked) {
hobbies.add("看電影")
}
if (checkBook.isChecked) {
hobbies.add("讀書")
}
最後:
val hobbyText = hobbies.joinToString(" ")
就能把多個使用者選項整理成一段文字。
這也是 Kotlin 基本語法開始真正和 Android UI 結合 的地方。
今天透過「學生資料」App,把之前學到的 Android UI 元件整合在同一個畫面。
最重要的是理解:
RadioButton
≠
CheckBox
RadioButton 適合處理單選問題,而 CheckBox 適合處理複選問題。
另外也學到如何透過:
isChecked
取得元件目前的狀態,再使用:
if
when
MutableList
add()
joinToString()
整理使用者輸入的資料。
以前寫 Kotlin 基礎語法時,if、when、List 看起來可能只是單獨的語法練習。
但實際放進 Android App 後,就會發現:
UI 負責讓使用者輸入,而 Kotlin 負責理解使用者輸入了什麼。
最後再透過 TextView 將整理完成的資料呈現回畫面。
今天完成的資料流可以整理成:
EditText
+
RadioButton
+
CheckBox
↓
setOnClickListener
↓
Kotlin 條件判斷
↓
MutableList
↓
joinToString()
↓
TextView
這次的學生資料 App 雖然不大,但已經具備一個完整表單 App 的基本雛形。
DAY18 完成!
下一步還可以加入:
姓名不能為空
性別必須選擇
至少選擇一項興趣
其它勾選後才能輸入
Toast 錯誤提示
Snackbar
Material Design TextInputLayout
儲存學生資料
Activity 頁面切換
如此就可以從今天的簡單學生資料表單,慢慢擴充成真正具有資料驗證與資料保存功能的 Android App。
Android Developers-Radio Button
https://developer.android.com/develop/ui/views/components/radiobutton
Android Developers-Checkbox
https://developer.android.com/develop/ui/views/components/checkbox
Android Developers-Buttons
https://developer.android.com/develop/ui/views/components/button
Kotlin 官方文件-Collections
https://kotlinlang.org/docs/collections-overview.html
Kotlin 官方文件-Control flow
https://kotlinlang.org/docs/control-flow.html