[Google Course] Android Basics in Kotlin (第2篇) — 類別繼承及變數可見性
繼上次分享Google Kotlin 基礎課程後,我們終於可以再往前一步邁進了!!在這週的文章會談到Kotlin的類別及物件概念,當然我們現在只會討論到一點概念,主要還是專注在Android 基本開發
接下來一樣透過Kotlin 基礎課程教學這個Google推出的Kotlin課程,我們一樣使用Google的互動式編譯Kotlin的網頁先來進行Kotlin的練習,這篇文章會學習到以下的內容:
● Kotlin的類別、物件語法以及隨機變數
● Android Activity 元件
● Android Button 元件
● Android Studio 加入自定義類別
● Kotlin 判斷式語法
● Kotlin 控制 ImageView 圖片
● QA
[Kotlin的類別、物件語法以及隨機變數]
那我們就開始吧!跟著這篇文章做到最後我們會完成一個擲骰子的App,為了維護我們的程式,在一開始我們就必須規劃好程式的架構,像設計建築物一樣有個藍圖構想,而建築物的內容可以隨著不同住宅的目的來建造,因此我們先建立一個名為Dice的類別class,並且賦予它第一個內容屬性變數 — side,是指這個骰子有幾個邊長:
這個類別class 只是一個定義而已,並不代表你創建了任何實體物件,就像建築物的藍圖只是事先設計好的,還是需要去實際將房子蓋出來才有個實體物件存在,而在Kotlin要如何將類別實體化成一個物件呢:
透過
var dice = Dice()
,創造一個變數 var 名稱叫dice,將等號的右邊實體化的物件指定給左邊的dice變數
接著我們試著獲取物件內的屬性,物件.屬性名稱是獲取一個物件的屬性的語法,並將屬性輸出到畫面上進行確認:
回想當你擲一個骰子時發生了什麼事?骰子隨機的翻到某一面數字(1~6),現在我們有一個類別 — 骰子,他也必須要能夠執行隨機產生某個數字(1~6)的行為,而類別能夠做什麼行為就定義在函式function裡,在物件導向的系列文章中我們會進一步介紹類別及物件的關係,真正了解物件導向的概念後會發現程式設計其實也可以是一件充滿想像力跟創造力的事情!
回到正題….骰子必須要能夠執行隨機產生某個數字(1~6)的函式,因此我們要在Dice類別內定義一個函式 fun roll()
如 下:
並且在main函式dice物件下新增兩行指令獲取物件裡的變數及呼叫函式,如上main 函式
這邊Kotlin 創造了一種語法
(1..6)
這樣的語法代表我們創造了一個1~6的集合物件,而集合物件都擁有一個叫 random 隨機取數的函式,可以隨機獲取集合物件裡的任一個元素因此我們寫 (1..6).random(),會回傳一個數字,就是隨機獲取的結果,將他指定給左邊變數randomNumber
那隨機結果已經創造出來了,要如何讓main 這個主程式獲得dice這個物件(不是類別唷!)的擲骰子動作的結果(randomNumber)呢??
我們有兩種方法可以使用,當然你也可以想看看還有沒有其他辦法
● 第一種是將roll這個函式改寫成有回傳值的函式,可以看到下圖fun roll() : Int,在函式名稱冒號的後面都是函式返回值的資料型態,目前我們設計roll會返回一個整數型態Int:
如果你直接執行會看到一個錯誤:
因為我們需要一個整數作為返回值,但是函式內並沒有返回值出去,所以我們這邊需要用到return 這個關鍵字,將randomNumber這個變數返回出去:
● 第二種方法是將randomNumber設成類別內的屬性(成員變數),這樣就可以在函式內直接將隨機取得的變數直接指定給randomNumber了!
main 主程式內的使用:
● 方法一
● 方法二
這邊值得注意一點的就是在方法一 roll函式裡的 randomNumber 是函式裡的區域變數,而方法二的 roll函式裡的 randomNumber是類別的屬性,是屬於類別物件的,兩者存取權限是不一樣的,這邊要特別注意
接著我們還有一件事情要做,隨著時間的演進,骰子的應用也越來越多元,像是各種桌上遊戲,各種特殊骰子出現,有八面、十二面等等,我們程式的骰子當然也要能夠擲出這些點數,還記得我們有個變數sides代表骰子的邊數量嗎?現在我們只需要把 (1..6) 改成 (1..sides),並且在main 主程式指定邊的數量為100,就可以擲出1到100內的任意數字了
目前完成的程式碼如下:
現在我們可以來玩吃香腸必配的十八逗仔,在我們的主程式創造三個骰子物件,並且執行roll這個動作來看看誰擲出最大點!(這邊使用方法二來擲骰子,大家可以嘗試用方法一來做做看,思考一下哪個方式比較符合骰子遊戲的需求,完整程式碼請點這)
現在我們要進入Android Studio 創建一個新專案叫 Dice Roller,不記得怎麼建立的話可以到這裡複習一下,現在大家應該都有一個空專案了吧!
接著我們先來打開畫面排版檔案(Project>app > res > layout > activity_main.xml),裡面應該只有一個TextView在正中間寫著“Hello World”,我們今天要學習另一個UI元件 — Button,將一個Button從”Palette”拉出來放置在TextView下方
回憶一下上篇文章提到的ConstraintLayout排版方式(拖拉方式),將Button 放到畫面水平正中間、貼緊TextView下緣,完成後Component Tree視窗內的button 旁的紅色驚嘆號應該會變為紅色驚嘆號:
此時黃色驚嘆號顯示“Hardcoded string “Button”, should use @string resource”,提醒我們應該要將按鍵上的BUTTON字串寫到strings.xml裡面,方便管理字串,到Project>app > res > values > strings.xml加入下面一行:
並將Attributes>text 屬性修改成”@strings/roll_button”,黃色驚嘆號也會不見:
此時可以先安裝APP到手機中,不過你會發現按鍵按下去不會有任何作用,接下來就是要進行按鍵按下去的動作了!!我們需要會一些Kotlin的程式語法(之前已經學過了),為了幫按鈕進行設置,我們必須先了解Android App的的架構
[Android Activity 元件]
Android App透過一個叫Activity
的元件,來協助你的App繪製UI及觸發事件,一個App會有一個或多個Activity 元件,通常在創建一個空的應用程式時,Android Studio 就會幫我們創一個Activity元件的程式碼(Project視窗> app > java > com.example.diceroller > MainActivity.kt)
而Activity 是要做什麼的呢?簡單來說,我們可以將一個Activity 看成App執行特定主要目的的元件,假設我們今天做一個相片編輯的App,那我們可能就需要規劃三個不同的Activity,第一個是負責相片選擇,第二個是檢視選擇的相片,第三個則是編輯相片:
現在我們就要來加入響應按鍵事件的動作程式碼了,打開Project 視窗>app > java > com.example.diceroller > MainActivity.kt,你會看到以下畫面:
我們先不用每一行都了解,但我們看到幾個地方:
- class MainActivity:AppCompatActivity(){..}
像是我們之前創建骰子的類別一樣,MainActivity就是一個類別,只是同時他也繼承了Android Framework的AppCompatActivity,我們就是必須將函式及成員變數定義在class裡面
那可能有人會說Kotlin不是都要有一個main 函式的進入點嗎?我們看到第7行 override fun onCreate 這個就是Android App 開啟後第一個會進入的入口點,所以我們需要將App的初始化設定都放置在這個函式 - 接著我們看到第9行的setContentView(R.id.activity_main),就是在執行畫面的初始設置,設定成我們一開始拉元件的.xml,在這邊xml檔案就是規劃Android 畫面的程式
- 因為我們接下來要在Kotlin 程式使用到Android Framework的函式庫,因此我們開啟auto import 這個功能,在 File > Other Settings > Preferences for New Projects內的 Other Settings > Auto Import,將下圖中紅色框框中選項通通勾選起來~
如此一來,待會我們使用到Button這個類別時,IDE會自動幫我們import Button 的類別— android.widget.Button,並且IDE會為我們做import的優化,刪除沒有使用到的類別
Enable auto imports雖然可以使用而且看似方便,不過身為三年經驗的不菜不老工程師,我覺得IDE提供的功能好用,但絕對不能太過依賴,以免後來悔不當初,沒了IDE什麼都不會開發了!
[Android Button 元件]
接著我們先來一小段程式讓Button 跟你有互動,確認程式碼是可執行的,在onCreate()加入以下程式碼片段 :
val rollButton: Button = findViewById(R.id.button)
這行程式做了幾件事:
● 透過AppCompatActivity內的方法findViewById
● 到activity_main.xml 找到 id 為 button的 按鍵
● 創造了一個Button類型的物件
● 指定給變數rollButton
此時會看到以下畫面,IDE跟我們說沒有android.widget.Button類別:
按下Alt+Enter ,IDE就會自動幫我們匯入(import) Button 的類別了:
這時候我們可以用Button內的一個方法setOnClickListener設定當按下按鈕後要執行些什麼事情:
rollButton.setOnClickListener {
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = "6"
}
一樣會出現沒有android.widget.TextView類別的提示,按Alt+Enter把他import 進來,這幾行程式其實跟我們在設計activity_main.xml畫面做的事情很像,只不過是用程式再加入一點互動性
● 透過Button內的方法setOnClickListener
● 到activity_main.xml 找到 id 為 textView的 TextView
● 創造了一個TextView類型的物件
● 指定給變數resultTextView
● 設定resultTextView 的文字屬性 text為 “6”
現在執行看看你的App,按下按鈕後TextView應該會更新成 “6” 這個數字!!!
[Android Studio 加入自定義類別]
接著我們唯一還沒做的事情就是加入骰子的邏輯,在文章一開始我們在Kotlin Onlin 創造了一個 Dice的類別,現在就派上用場了!
Android 官方教學中是把Dice這個類別放在MainActivity.kt的類別裡,不過實務上的經驗是不會這樣子寫程式的,基本上我們會把類別分門別類創造新的class,思考一下,你覺得控制UI的Activity 和 骰子邏輯的類別定義應該是同一個嗎??
● 首先,我們要先創一個新的kotlin檔案,在java 資料夾下的diceroller 資料夾按右鍵>New>Kotlin File/Class:
● 接著在IDE中間會出現一個小視窗,在編輯方塊輸入”Dice”並點選Class
● 就成功創建一個新的類別與MainActivity同一層級,名稱為”Dice”
● 打開Dice,將我們在Online Kotlin的類別程式複製到此,要注意第一行的package 不要刪除掉了噢!
接著我們就可以在MainActivity 中的按鈕事件中創建 Dice 物件:
● 創造 Dice 實體物件指定給 dice 這個變數
● 透過 dice內的方法roll1()取得隨機的數字,儲存到名為rollResult的變數裡
● 轉換成字串(String)
● 設定resultTextView 的文字屬性 text為 roll1()取得隨機的數字的字串
這時候每次按下按鈕都會有不同的結果了唷!!
這邊為了讓我們的程式看起來更有組織更整齊,我們新增一個函式名稱為rollDice在 MainActivity 裡面,並將按鈕事件內的程式碼通通移到rollDice函式裡面,並在按鈕事件內呼叫,如下:
和大部分程式語言一樣,我們可以使用if 來根據條件判斷執行不同的動作,接著我們就來學習Kotlin 中的判斷式語法吧!!
[Kotlin 判斷式語法]
我們如果只有顯示出骰子點數對一個App來說會有點太過於簡單,這樣我們倒不如直接拿骰子骰就好了啊,何必大費功夫做一個App?所以接下來我們要讓App有一些自動化的功能,像是判斷是否擲到最大點 ” 6 ” ,並且給出恭喜訊息或是再一次的訊息,很幸運,Kotlin 剛好也有這個判斷語法,現在讓我們再次打開Kotlin Online 來練習判斷語法吧!
● 我們可以使用
if
來執行動作當我們遇到某些特定的狀況,例如:
fun main() {
val num = 5
if (num > 4) {
println("The variable is greater than 4")
}
}
大部分我們叫程式執行的事情都可以用人類語言表示出來,像上面就是“當數字 num 大於 4 時,跟某人說這個數字大於 4”,而上面的程式做了幾件事情:
● 將變數num設定為 5
● 判斷如果 num 大於 4 則進入大括號
● 印出"The variable is greater than 4"
這邊的第三步是 num 大於 4 才會去執行,因此我們的程式就被分為兩條路徑,不同的執行結果
而if 後面的小括號內()是放置判斷式,num > 4 運算結果會是 true(真) 或是 false(假),”>”是一個判斷的運算子,其他還有”<”、”≥”、”≤”、”==”等等,都是判斷運算子(Operator)
● 除了 if 的判斷其他的狀況都要執行同一個流程,那就可以使用
else
:
fun main() {
val num = 5
if (num > 4) {
println("The variable is greater than 4")
} else {
println("The variable is less than 4")
}
}
● 除了 if 以外還有其他特殊的判斷需要執行其他動作,那我們可以使用
else if
:
fun main() {
val num = 5
if (num > 4) {
println("The variable is greater than 4")
} else if (num == 4) {
println("The variable is equal to 4")
} else {
println("The variable is less than 4")
}
}
●
when
這個語法目前就我所知應該只有 Kotlin 使用,用法有許多種,類似於 C 語言裡的switch
語法,不過擁有更多的彈性,可以做判斷式的判斷,寫法也很多種,以下其中一種:
fun main() {
val num = 5
when {
(num > 4) -> println("The variable is greater than 4")
(num == 4) -> println("The variable is equal to 4")
else -> println("The variable is less than 4")
}
}
可以看到做出跟if-elseif 一樣的功能,程式碼相對地減少,很不錯的新語法,其他範例大家也可以到 官網when 再深入了解
[Kotlin 控制 ImageView 圖片]
接著我們就要來將判斷語法與我們的Android App做結合了!首先先幫我到 骰子圖片 下載六面的圖匯入Android Studio :
之前我們有學習過如何擺放圖片的位置,現在拉出一個ImageView,隨便選一個骰子圖片資源當作預設,將layout_width 設為 160 , layout_height 設為200dp,擺在畫面中的TextView上方並水平置中:
接著我們就要來製作當按下按鈕時,也會同時改變ImageView圖片的效果囉! 首先在 rollDice 函式內新增:
val diceImage: ImageView = findViewById(R.id.imageView)
diceImage.setImageResource(R.drawable.dice_2)
上面兩行程式做了什麼事情:
● 透過AppCompatActivity內的方法findViewById
● 到activity_main.xml 找到 id 為 imageView的 按鍵
● 創造了一個ImageView類型的物件名為 diceImage
● 將diceImage的圖片資源設成 R.drawable.dice_2 (我們剛剛會進來的圖片資源)
當然會出現沒有ImageView這個class的提醒,把他import 進來就可以了,此時執行程式我們會發現不管怎麼按按鍵他都只會顯示dice_2的圖片,沒有跟著擲出來的數字一起變動,我們寫程式的人沒做,程式自然就不會做囉!
所以這時候就要派出我們剛學的Kotlin判斷式語法,我們根據擲出來的數字來決定要放哪張圖片,我們先用if-elseif 來完成:
if (rollResult == 1) {
diceImage.setImageResource(R.drawable.dice_1)
} else if (rollResult == 2) {
diceImage.setImageResource(R.drawable.dice_2)
} else if (rollResult == 3) {
diceImage.setImageResource(R.drawable.dice_3)
} else if (rollResult == 4) {
diceImage.setImageResource(R.drawable.dice_4)
} else if (rollResult == 5) {
diceImage.setImageResource(R.drawable.dice_5)
} else if (rollResult == 6) {
diceImage.setImageResource(R.drawable.dice_6)
} else {
diceImage.setImageResource(R.drawable.dice_6)
}
接著看看用when完成類似C 語言Switch 的語言:
when(rollResult)
{
1 -> diceImage.setImageResource(R.drawable.dice_1)
2 -> diceImage.setImageResource(R.drawable.dice_2)
3 -> diceImage.setImageResource(R.drawable.dice_3)
4 -> diceImage.setImageResource(R.drawable.dice_4)
5 -> diceImage.setImageResource(R.drawable.dice_5)
6 -> diceImage.setImageResource(R.drawable.dice_6)
else -> diceImage.setImageResource(R.drawable.dice_6)
}
這兩種方法其實都可以,就看個人覺得哪種較直覺較好用,我自己是比較喜歡when 的寫法,但是習慣使然,偶而還是會直接用if-else,必須要改進啊!!
到這邊相信大家應該已經有一個擲骰子的App,可以嘗試去想看看這樣簡單的App有怎樣的應用能夠放大它的價值,就像是ramdom這個間單的小功能,其實就可以製作出一款”午餐吃什麼”的應用程式
這邊總結一下Google Kotlin Basic 的重點:
● Kotlin 變數、類別、物件語法、隨機變數以及判斷語法
● Android TextView、ImageView、Activity 和 Button 元件
● Android Studio 加入自定義類別
● 在 Android Studio 透過 Kotlin 控制 UI 元件
下面有一些QA,大家可以測試一下學習的狀況唷!
[QA]
● 如何在Kotlin中取得隨機變數??
● 在Kotlin中 when 語法可以使用在哪裡??
● 下列程式碼做了些什麼事情?val resultTextView: TextView = findViewById(R.id.textView)
● Android Activity 元件是用來做什麼的??
resultTextView.text = “6”
● 在類別class 裡面的變數,我們稱為類別的什麼??在類別class 裡面的函式,我們又稱為類別的什麼??
喜歡我的文章的人也記得幫我按個拍手、分享,覺得很不錯的可以幫我拍個50下!
也要快點追蹤我的 FB粉絲專頁 — 飛比尋常的程式設計世界 ,不會太頻繁出現在你的塗鴉牆騷擾你,好文章生產需要一點時間,有錯誤或想討論的都歡迎留言給我唷!那就下次見拉!