# Android Programming - Lecture 1 ###### tags: `Kotlin Programming` ## Android 專案介紹 ### 組織方式 通常習慣以 Android 方式瀏覽專案。 ![](https://i.imgur.com/DOKL0Lu.png) ### 專案結構 ![](https://i.imgur.com/iuxiXhS.png) * manifests:存放 Android APP 的主要設定檔。 * java:存放專案主要 kotlin 程式碼。 * res:存放專案不是程式碼的資源檔案,例如:Layout、圖片、文字等。 ### Android Application Components * Activities:處理 UI 介面的程式,我們課程著重在這邊。 * Services:處理背景處理執行的程式。 * Broadcast Receivers:處理 Android OS 和 APP 之間的溝通的程式。 * Content Providers:管理資料,可提供給其他 APP 的程式。 參考:https://www.tutorialspoint.com/android/android_application_components.htm #### AndroidManifest.xml 每個 Android APP 都會有此檔案,存放 APP 的基本資訊(icon、label等)、有哪些 Activity、Service、需要使用到的權限等。 ```xml= <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.helloworld"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HelloWorld"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <!--表示可從手機系統桌面圖示啟動--> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> ``` 參考:https://developer.android.com/guide/topics/manifest/manifest-intro #### MainActivity.kt 寫 UI 的 Android APP 一開始會執行的地方,一開始只有 `onCreate()` 方法,載入對應的 Layout。 ```kotlin= package com.example.helloworld import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 這邊載入了 activity_main 的 Layout } } ``` ##### Lifecycle ![](https://i.imgur.com/n0rpTlw.png) 來源:https://developer.android.com/guide/components/activities/activity-lifecycle #### activity_main.xml MainActivity 的介面 Layout 檔案,可透過圖形化介面編輯器或是直接編輯 XML 方式操作。 ![](https://i.imgur.com/8tOgyG7.png) 1. 調色盤:可拉取需要的元件到 UI 上。 2. 元件樹:顯示元件之間的結構。 3. 設計編輯器:分別在設計和藍圖視角編輯 Layout。 4. 屬性:可以修改你選擇的元件的屬性。 5. 觀看模式:可切換使用圖形化介面或 XML 編輯等。 ##### Layout 簡介 * LinearLayout:以縱向或橫向呈現。 * RelativeLayout:以相對位置呈現。 * FrameLayout:只顯示單一元件,所有元件都在左上角疊在一起。 * TableLayout:以表格方式呈現。 * ConstraintLayout:RelativeLayout 的延伸,透過物件自已本身與其它物件之間的約束來決定它的位置。 ## Draw Playing Cards App 接著讓我們開啟一個新專案做一個抽撲克牌的小遊戲。 ### 建立專案 1. Create New Project。 ![](https://i.imgur.com/EgVw0Aa.png) 2. Template 選擇 Empty Activity。 ![](https://i.imgur.com/t01siPx.png) 3. 命名為 Draw Playing Cards,語言選擇 kotlin ,Minimum SDK 選擇 Android 11.0。 ![](https://i.imgur.com/eRrr7uJ.png) ### 新增按鈕 1. 切換至 activity_main.xml,並使用 XML 編輯模式。 ![](https://i.imgur.com/MpvM3VS.png) 2. 將內容修改成: 也可以不直接編輯 XML 使用圖形化編輯介面新增這些元件與設定屬性。 ```xml= <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center_vertical" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="1" android:textSize="30sp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Draw" /> </LinearLayout> ``` 這邊我們使用 LinearLayout,並且放置 TextView 與 Button 兩個元件。 其中設定 LinearLayout 屬性為: * `android:layout_width="match_parent"`:`match_parent`會依照上層的大小填滿上層物件,所以元件在畫面中的寬度會被撐滿。 * `android:layout_height="wrap_content"`:`wrap_content`會依照內容物件的大小去做改變,所以元件的高只吃它使用的範圍。 * `android:layout_gravity="center_vertical"`:設定為置中對齊。 * `android:orientation="vertical"`:設定為垂直排版。 可以在圖形化編輯介面選則 Linear Layout 設定屬性並預覽效果看看。 ![](https://i.imgur.com/OM9JbE0.png) Linear Layout 參考:https://developer.android.com/guide/topics/ui/layout/linear 3. 檢視設計介面。 ![](https://i.imgur.com/46f8IJL.png) 4. 打開模擬器預覽。 ![](https://i.imgur.com/xqndnsm.png) ### 綁定按鈕 #### 使用 View Binding(推薦) 1. 使用 View Binding 前必須先在 `build.gradle` 中設定 `viewBinding` 爲 `true`。 ``` android { ... buildFeatures { viewBinding true } } ``` 2. 在 Button 的 Layout 中新增 `android:id="@+id/draw_button"`。 id 的格式為 `@+id/你想要取得名字`。 ```xml= <Button android:id="@+id/draw_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Draw" /> ``` 3. 在 MainActivity.kt 中對按鈕進行綁定及操作。 MainActivity.kt: ```kotlin= package com.example.drawplayingcards import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import com.example.drawplayingcards.databinding.ActivityMainBinding private lateinit var binding: ActivityMainBinding class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.drawButton.text = "Let's Draw" } } ``` 4. 可看到 `onCreate()` 時確實有把原本按鈕在 Layout 上設定的 `DRAW` 改為剛剛寫的 `Let's Draw`。 ![](https://i.imgur.com/oImMojk.png) 參考:https://developer.android.com/topic/libraries/view-binding#activities #### 使用 findViewById 1. 在 Button 的 Layout 中新增 `android:id="@+id/draw_button"`。 id 的格式為 `@+id/你想取的名字`。 ```xml= <Button android:id="@+id/draw_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Draw" /> ``` 2. 在 MainActivity.kt 中對按鈕進行綁定及操作。 請用輸入的,才會自動提示並 import 需要的元件。 ```kotlin= val drawButton: Button = findViewById(R.id.draw_button) drawButton.text = "Let's Draw" ``` MainActivity.kt: ```kotlin= package com.example.drawplayingcards import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Button class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // -------------------這裡開始------------------- val drawButton: Button = findViewById(R.id.draw_button) drawButton.text = "Let's Draw" // -------------------這裡結束------------------- } } ``` 3. 可看到 `onCreate()` 時確實有把原本按鈕在 Layout 上設定的 `DRAW` 改為剛剛寫的 `Let's Draw`。 ![](https://i.imgur.com/oImMojk.png) ### 點擊事件 1. 在 MainActivity.kt 中對按鈕設定`OnClickListener`,並使用 Toast 顯示訊息。 ```kotlin= binding.drawButton.setOnClickListener { Toast.makeText(this, "button clicked", Toast.LENGTH_SHORT).show() } ``` Toast 元件參考:https://developer.android.com/guide/topics/ui/notifiers/toasts MainActivity.kt: ```kotlin= package com.example.drawplayingcards import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import com.example.drawplayingcards.databinding.ActivityMainBinding private lateinit var binding: ActivityMainBinding class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // -------------------這裡開始------------------- binding.drawButton.setOnClickListener { Toast.makeText(this, "button clicked", Toast.LENGTH_SHORT).show() } // -------------------這裡結束------------------- } } ``` 2. 可看到當點擊按鈕時,跳出 Toast 並顯示 `button clicked` 訊息。 ![](https://i.imgur.com/4DU48Pz.png) ### 點擊並改變 TextView 1. 在 TextView 的 Layout 中新增 `android:id="@+id/result_text"`。 ```xml= <TextView android:id="@+id/result_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="1" android:textSize="30sp" /> ``` 2. 在 MainActivity.kt 中撰寫對應程式。 * 監聽按鈕 `click` 時執行 `darwCard()` Function: ```kotlin= binding.drawButton.setOnClickListener { darwCard() } ``` * 新增 `darwCard()` 方法:取得亂數、綁定 `TextView` 並更改上面的文字。 ```kotlin= private fun drawCard() { val randomInt = Random().nextInt(13) + 1 binding.resultText.text = randomInt.toString() } ``` MainActivity.kt: ```kotlin= package com.example.drawplayingcards import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import com.example.drawplayingcards.databinding.ActivityMainBinding import java.util.* private lateinit var binding: ActivityMainBinding class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // -------------------這裡開始------------------- binding.drawButton.setOnClickListener { drawCard() } // -------------------這裡結束------------------- } // -------------------這裡開始------------------- private fun drawCard() { val randomInt = Random().nextInt(13) + 1 binding.resultText.text = randomInt.toString() } // -------------------這裡結束------------------- } ``` 3. 點擊 DRAW 按鈕時上面 `TextView` 隨機跳出 `1~13` 的數字。 ![](https://i.imgur.com/l264BH7.png) ### 新增撲克牌圖檔 1. 下載 https://drive.google.com/file/d/1-Yx9dQgtEq8nsq5-DGWU9payrv6jNAft/view?usp=sharing 並解壓縮圖檔。 2. 拖曳檔案新增至 `res/drawable` 內。 ![](https://i.imgur.com/R43FNW7.png) 3. 檢視圖檔。 ![](https://i.imgur.com/peNX8R2.png) ### 使用 ImageView 元件 我們將以 `ImageView` 取代剛剛的 `TextView`。 1. 在 Layout 中移除原本的 `TextView` 並新增 `ImageView`。 ```xml= <ImageView android:id="@+id/playing_card_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:src="@drawable/spade_empty" /> ``` activity_main.xml: ```xml= <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:gravity="center_vertical" android:orientation="vertical" tools:context=".MainActivity"> > <ImageView android:id="@+id/playing_card_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:src="@drawable/spade_empty" /> <Button android:id="@+id/draw_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Draw" /> </LinearLayout> ``` 2. 在 MainActivity.kt 中撰寫對應程式。 修改 `drawCard()` Function,綁定 `ImageView` 並依不同的 `randomInt` 設定不同的撲克牌圖片。 ```kotlin= private fun drawCard() { val drawableResource = when (Random().nextInt(10) + 1) { 1 -> R.drawable.spade_1 2 -> R.drawable.spade_2 3 -> R.drawable.spade_3 4 -> R.drawable.spade_4 5 -> R.drawable.spade_5 6 -> R.drawable.spade_6 7 -> R.drawable.spade_7 8 -> R.drawable.spade_8 9 -> R.drawable.spade_9 10 -> R.drawable.spade_10 else -> R.drawable.spade_empty } binding.playingCardImage.setImageResource(drawableResource) } ``` MainActivity.kt: ```kotlin= package com.example.drawplayingcards import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import com.example.drawplayingcards.databinding.ActivityMainBinding import java.util.* private lateinit var binding: ActivityMainBinding class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.drawButton.setOnClickListener { drawCard() } } // -------------------這裡開始------------------- private fun drawCard() { val drawableResource = when (Random().nextInt(10) + 1) { 1 -> R.drawable.spade_1 2 -> R.drawable.spade_2 3 -> R.drawable.spade_3 4 -> R.drawable.spade_4 5 -> R.drawable.spade_5 6 -> R.drawable.spade_6 7 -> R.drawable.spade_7 8 -> R.drawable.spade_8 9 -> R.drawable.spade_9 10 -> R.drawable.spade_10 else -> R.drawable.spade_empty } binding.playingCardImage.setImageResource(drawableResource) } // -------------------這裡結束------------------- } ``` 3. 點擊 DRAW 按鈕時上面 `ImageView` 會隨機顯示 `1~10` 的撲克牌花樣。 ![](https://i.imgur.com/KNlARwT.png)