真的假的啦 — 真心話大冒險App
Lecture: 一直來不及備課的鹽亞倫
今天所有東西的連結和要下載的檔案都放在這裡:
今天最多只能讓大家對於App的開發有著初步的認識,並且完成簡單小型的App設計。
所以不太可能上完課以後你就寫得出傳說對決
如果真的有興趣的話
建議大家自行自學更多進階的技巧喔
Android
iOS
所以今天會教Android啦
安裝完之後長相
按 New Project
選擇 empty activity
按一下視窗底部的「Next」(下一步)。
「New Project」(新增專案) 對話方塊隨即開啟。
應用程式的名稱,幫我改成Truth_or_Dare
Android 系統用來識別應用程式的名稱
專案資料夾位置(可用預設值)
程式語言(請選Kotlin)
SDK版本,越舊支援度越高,今天選 API 19: Android 4.4 (KitKat)。
第一次開啟 Android Studio 時,會看到三個視窗:
「Project」(專案) 視窗會顯示專案的檔案和資料夾。
「Editing」(編輯) 視窗可編輯程式碼
「What’s New」(最新消息)(可以直接關掉)
按這裡可以快速開啟Device Manager
按這裡可以叫出已建立的模擬器
執行程式碼
手機的各種按鈕
Android Studio 檔案結構介紹
有Android模式和project Files模式
Android
Project Files
fun main() {
println("Hello, world!!!")
}
可以看到幾個重點:
println()
輸出並且換行
用print(),就不會自動換行
注意:所有型態首字為大寫
整數
小數
變數宣告關鍵字:
val
var
var 變數名稱: 型態 = 初始值
或是
val 變數名稱: 型態 = 初始值
val a: Int = 10
var b: String = "I am so weak."
val c: Float = 1.0 + 3.0
有初始化的變數也可以省略型態
var a = 10
今天不會用到,做個補充而已
用readLine()可以讀一行
import java.util.Scanner 之後
可以用類似cin的東西
import java.util.Scanner
fun main() {
val read = Scanner(System.`in`)
println("請輸入你的年齡:")
var age = read.nextInt()
println("你的年齡是:"+age)
}
val count = 10
if (count < 5) {
println("太少")
} else if (count > 5) {
println("太多")
} else {
println("剛剛好")
}
神奇的是,Kotlin中的if可以有回傳值。
每個區段的最後一行的值,會被當作若執行到該區段會讓整個語句回傳的值:
var score: Int = 85
var grade: String = if(score<=100 && score>80){
"A"
}else if(score<=80 && score>=60){
"B"
}else{
"C"
}
println("Grade: $grade")
//印出 Grade: A
類似Switch case
when{
條件一 -> {
//如果條件一成立就執行區塊,反之繼續判斷條件二
}
條件二 -> {
//如果條件二成立就執行區塊,反之繼續判斷條件三,以此類推
}
else -> {
//如果以上的條件都不符合,就執行這個區塊
}
}
範例
var score: Int = 85
var grade = ""
when{
score<=100 && score>80 -> grade = "A"
score<=80 && score>=60 -> grade = "B"
else -> grade = "C"
}
var score: Int = 85
var grade: String = when{
score<=100 && score>80 -> "A"
score<=80 && score>=60 -> "B"
else -> "C"
}
一樣可以有回傳值
範例
// while (節錄自 Kotlin 官方教學程式碼)
// 會接續印出 0 到 9
var x = 0
while (x < 10) {
println(x)
x++
}
蠻多種類的,但很簡單
// 會接續印出 0 到 10
for (x in 0..10) {
println(x)
}
// 會接續印出 0 到 9
for (x in 0 until 10) println(x)
// 會接續印出 0, 2, 4, 6, 8
for (x in 0 until 10 step 2) println(x)
// 會接續印出 10, 8, 6, 4, 2, 0
for (x in 10 downTo 0 step 2) println(x)
val names = listOf("Anne", "Peter", "Jeff")
// 會接續印出 Anne 、 Peter 、 Jeff
for (name in names) {
println(name)
}
就...類似python的range
(1..10)
一個1到10的range:
for i in (1..10) {
print(i)
print(" ")
}
// output: 1 2 3 4 5 6 7 8 9 10
除了for迴圈以外,range可以幹嘛
Random.nextInt() // 產生一個Int亂數
Random.nextFloat() // 產生一個Float亂數
Random.nextInt(1, 10) // 產生一個1到10的Int亂數
(1..10).random() // 產生一個1到10的亂數
定義方式:
fun [函式名稱]([參數 1 名稱]: [參數 1 型態]): [回傳型態] {
[內容]
}
範例:
fun BMI(val w: Float, val h: Float): Float {
return w/(h*h);
}
// 如果函式只有一行 return 的話,可簡寫成等式
fun happyBirthday2(name: String, age: Int) =
"Happy ${age}th birthday, $name!"
class
類似C++的STL
包含Array、Map、Set、List等等
名稱 | 中文名稱 | 備註 | 教學網址 |
---|---|---|---|
Array | 陣列 | 長度固定 | https://ithelp.ithome.com.tw/articles/10237401 |
List | 清單 | 長度&內容不可變 | https://ithelp.ithome.com.tw/articles/10238930 |
MutableList | 可變清單 | 長度&內容可變 | |
Set | 集合 | 內容不可變、內容不重複 | https://ithelp.ithome.com.tw/articles/10239409 |
MutableSet | 可變集合 | 內容可變、內容不重複 | |
Map | 就是Map | 鍵(Key)值(Value)對照 | https://ithelp.ithome.com.tw/articles/10240148 |
MutableMap | 就是可變Map | 內容可變 |
宣告
用mutableSetOf()函數:
val muSet = mutableSetOf("Jim", "Sue", "Sue", "Nick", "Nick")
// 只會存放不重複的 Jim, Sue, Nick
val SetOfAges: MutableSet<int> = mutableSetOf(31, 25, 10, 32, 12)
若要宣告空的:
val emptyMutableSet = mutableSetOf<String>()
取值、亂數取值
muSet.elementAt(0) // "tim"
println(muSet.random()) // 亂數輸出一個
var returnValue = muSet.randomOrNull() // set沒東西時會回傳null
取大小&修改&刪除
// 印出 set 大小
println(muSet.size)
// 在 set 最後面加入
muSet.add("Bob")
// 在 set 最後面加入一堆 data
muSet.addAll(listOf("a", "b", "c"))
// remove 某資料
muSet.remove("tim")
// 清除所有
muSet.clear()
輸出10階金字塔
fun main() {
var n = 10
for (i in 1..n){
for (j in 1..i){
print('*')
}
print('\n')
}
}
之一
基本介紹
先回到剛剛Android Studio建立的專案當中
目前你的畫面應該是這樣子的
此時你編輯器開啟的檔案是MainActivity.kt,也就是之後要寫Kotlin程式碼的位置
點按執行,可以看到一個Hello World的App
這就是一個預設的基本UI長相
從左邊專案資料夾中選擇 res/layout/activity_main.xml
如果打開activity_main.xml之後沒有看到版面配置編輯器,可在右上角找到這個地方
請選擇Design
之二
View的使用
以TextView為例
2. 固定位置
用這些彈簧定義了物件的相對位置限制
3. 調整設定
常用的設定如下:
基本上用法和ppt差不多
亂試幾次就會了
另外,這個區域可以針對剛剛彈簧做進一步設定:
修改其數值代表最少該側我要留多少空間,為0則代表允許緊貼。
修改為60,25時的長相
試試看做出這個版面配置
ID: TitleText
大小:34sp
ID: SubtitleText
大小:20sp
palette中的Text標籤,除了TextView都是不同種的EditText
今天只會用到Plain Text
editText在模擬器中長相:
做出今天真心話大冒險的UI長相
指針圖片可以在雲端找到
紅字為id
之三
補充:Xml檔案
xml檔案
剛剛有說過,實際上的UI是使用XML檔案紀錄的。
XML是一種標記語言,
語法上類似html,記錄的東西類似css
我們透過以上切換到code,可以看到如以下之長相。
我們可以發覺,所有我們動過的屬性設定皆會被以文字方式記錄下來。
切換到split標籤的話我們可以同時看到程式碼與UI的預覽。
還有很多東西是用xml紀錄的
res/values/strings.xml
記錄字串變數的地方
app_name 指的是手機上icon底下會顯示的名字
package com.example.helloworldtest1
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { // main函數
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 從這裡接者寫
}
// 函數可以宣告在這裡
}
// class 可以宣告在這裡
打開 mainActivity.kt
程式碼都要寫在這裡
其中 onCreate函數相當於main函數
直接看教學
或講義
Toast.makeText(this, "要顯示的訊息", Toast.LENGTH_SHORT).show()
使用方式如下:
val 變數名: View類別 = findViewById(R.id.物件的id)
舉例,如果你的UI裡面有一個id為my_button的按鈕,那麼你可以這樣寫:
val a: Button = findViewById(R.id.my_button)
這樣一來變數a就會是那個按鈕了。如此一來,你可以透過變數a來存取my_button的屬性
使用方法為 a.屬性名
例如如果你要輸出Button上面寫的字(text屬性),你可以這樣做:
val textString: String = a.text.toString()
Toast.makeText(this, textString, Toast.LENGTH_SHORT).show()
用這個方法函數修改UI元件的text屬性
val k: TextView = findViewById(R.id.textViewid);
k.setText("hello CKEFGISC")
這個函數主要和按鈕連用,代表當一個按鈕被按下時,要執行什麼程式。
按鈕變數.setOnClickListener{
// 這裡寫被按下之後要執行的東西
}
使用例子:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val myButton: Button = findViewById(R.id.ButtonId1)
myButton.setOnClickListener{
// 這裡寫被按下之後要執行的東西
Toast.makeText(this, "hahahahaha", Toast.LENGTH_SHORT).show()
}
}
}
時間暫停器
先等一段時間後再執行一段程式碼
val handler = Handler() // 建立handler()
handler.postDelayed( { // 計時器
// 一段時間之後要做的事
}, delayTime) // delayTime單位是毫秒
真心話大冒險app
兩大目標:
剛剛應該已經把UI建立完了吧
接下來直接從撰寫程式碼開始了喔
回到 mainActivity.kt
以下是這次程式碼的大致架構
package com.example.truth_or_dare
import android.os.Bundle
import android.view.animation.RotateAnimation
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import android.os.Handler
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 取得各個UI元件
val rollButton: Button = findViewById(R.id.start_button)
val addQuestionButton: Button = findViewById(R.id.add_button)
val questionTextBlock: TextView = findViewById(R.id.question)
val inputTextBlock: EditText = findViewById(R.id.text_input)
val rotateArrowImage : ImageView = findViewById(R.id.spinnerImage)
val questionSet: MutableSet<String> = mutableSetOf<String>(
"測試問題一",
"測試問題二"
) // app題目清單
rollButton.setOnClickListener {
// 開始轉動按鈕被按下之後要執行的程式
}
addQuestionButton.setOnClickListener {
// 新增問題按鈕被按下之後要執行的程式
}
}
繪製流程圖
功能一
好像沒講過?
繪製流程圖
功能二
轉動動畫可以使用kotlin內建的Animation完成,使用方法如下
val am = RotateAnimation(開始角度, 結束角度,
RotateAnimation.RELATIVE_TO_SELF, 旋轉x座標中心,
RotateAnimation.RELATIVE_TO_SELF, 旋轉y座標中心) // 建立動畫物件,注意,角度要用Float型態
am.duration = 旋轉時間 // 設定旋轉時間
am.setFillAfter(true) // 設定旋轉完後停在該角度
myImageView.startAnimation(am) // 將myImageView以am動畫執行
使用的範例
val am = RotateAnimation(0.0F, 720.0F,
RotateAnimation.RELATIVE_TO_SELF, 0.5F,
RotateAnimation.RELATIVE_TO_SELF, 0.5F)
// 從0度到720度,共轉兩圈
// 轉動中心在圖片的(50%, 50%)位置,即原本圖片的中心
am.duration = 2000
am.setFillAfter(true)
rotateArrowImage.startAnimation(am)
答案
var startDegree = 0.0f // 記錄目前指針的角度
var endDeg = 0.0f
rollButton.setOnClickListener {
// 跳出提示訊息
Toast.makeText(this, "開始旋轉!", Toast.LENGTH_SHORT).show()
// 旋轉動畫
var rotateDeg : Int = (1..360).random() // 要旋轉幾度
rotateDeg += (3..6).random() * 360 // 多加幾圈提高動畫效果
endDeg = startDegree + rotateDeg; // 結束的角度為當前角度加上要旋轉的角度
val am = RotateAnimation(startDegree, endDeg,
RotateAnimation.RELATIVE_TO_SELF, 0.5F,
RotateAnimation.RELATIVE_TO_SELF, 0.5F)
val spinTime : Long= (rotateDeg*3).toLong() // 轉多久
am.duration = spinTime
am.setFillAfter(true)
rotateArrowImage.startAnimation(am)
startDegree = endDeg % 360 // 更新起始角度數值
val handler = Handler() // 建立handler()
handler.postDelayed( { // 計時器
// 出現提問
var chosenQuestion = questionSet.random()
while (chosenQuestion == questionTextBlock.text) { // 避免問題和上一題重複
chosenQuestion = questionSet.random()
}
questionTextBlock.setText(chosenQuestion)
}, spinTime)
}
答案
addQuestionButton.setOnClickListener {
val stringInTextField = inputTextBlock.text.toString()
if (stringInTextField.isNotEmpty()) {
questionSet.add(stringInTextField)
Toast.makeText(this, "成功新增問題", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "請先輸入內容", Toast.LENGTH_SHORT).show()
}
inputTextBlock.setText("") // 清空文字輸入欄
}
package com.example.truth_or_dare
import android.os.Bundle
import android.view.animation.RotateAnimation
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import android.os.Handler
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.start_button)
val addQuestionButton: Button = findViewById(R.id.add_button)
val questionTextBlock: TextView = findViewById(R.id.question)
val inputTextBlock: EditText = findViewById(R.id.text_input)
val rotateArrowImage : ImageView = findViewById(R.id.spinnerImage)
val questionSet: MutableSet<String> = mutableSetOf<String>(
"測試問題一",
"測試問題二"
) // 寫在這裡的會是app預設就有的問題
var startDegree = 0.0f
var endDeg = 0.0f
rollButton.setOnClickListener {
// 跳出提示訊息
Toast.makeText(this, "開始旋轉!", Toast.LENGTH_SHORT).show()
// 旋轉動畫
var rotateDeg : Int = (1..360).random()
rotateDeg += (3..6).random() * 360
endDeg = startDegree + rotateDeg;
val am = RotateAnimation(startDegree, endDeg,
RotateAnimation.RELATIVE_TO_SELF, 0.5F,
RotateAnimation.RELATIVE_TO_SELF, 0.5F)
val spinTime : Long= (rotateDeg*3).toLong()
am.duration = spinTime
am.setFillAfter(true)
rotateArrowImage.startAnimation(am)
startDegree = endDeg % 360
val handler = Handler() // 建立handler()
handler.postDelayed( { // 計時器
// 出現提問
var chosenQuestion = questionSet.random()
while (chosenQuestion == questionTextBlock.text) {
chosenQuestion = questionSet.random()
}
questionTextBlock.setText(chosenQuestion)
}, spinTime)
}
addQuestionButton.setOnClickListener {
val stringInTextField = inputTextBlock.text.toString()
if (stringInTextField.isNotEmpty()) {
questionSet.add(stringInTextField)
Toast.makeText(this, "成功新增問題", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "請先輸入內容", Toast.LENGTH_SHORT).show()
}
inputTextBlock.setText("") // 清空文字輸入欄
}
}
}
專案資料夾/app/build/outputs/apk/debug
用任何你知道的方式傳到你的手機
安裝他
完成!!!