# Android compose
[官方教學...](https://developer.android.com/develop?hl=zh-tw)其實也是挺強的
主要使用 Kotlin 來撰寫 UI
[kotlin筆記](/aziUKMMTT3qzMkJG5lzrXg)有一些些些些kotliin基本語法
[TOC]
## 開啟
1. 開啟 Android Studio
1. 建立新專案 → 選擇 Empty Compose Activity
1. 設定最小 API Level(建議 API 26 以上)
1. 點擊 Finish,專案會自動配置 Compose 環境
## 圖片位置
在main新增一個資料夾放照片

可以右鍵Open In...開啟資料夾存取位置,直接將圖片存入其中
## 程式開始
在這一開始的地方寫上你要連結的函式即可
💡💡BiggerTheme為專案名稱

## UI撰寫地
* setContent {} 是 Compose 的 入口點
* @Composable 標記的函式負責繪製 UI
* Text(text = "Hello, Compose!") 顯示文字
```kotlin=
@Composable
fun MyApp() {
// 這裡是 UI 程式碼
MyScreen()
}
@Composable
fun MyScreen() {
Text(text = "Hello, Compose!")
}
```
## UI範例
Jetpack Compose 使用 Composable 函式 來建立 UI,以下是一個按鈕的範例:
### 按鈕 + 文字
```kotlin=
@Composable
fun MyScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
var count by remember { mutableStateOf(0) }
Text(text = "按鈕點擊次數: $count", fontSize = 24.sp)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text(text = "點我增加計數")
}
}
}
```
* remember { mutableStateOf(0) } 用來記錄狀態(點擊次數)
* Button(onClick = { count++ }) 按下按鈕時執行 count++
* Text(text = "按鈕點擊次數: $count") 動態顯示點擊次數
### UI 互動(點擊事件)
當 按下按鈕 時,text 變數會改變,UI 自動更新
```kotlin=
@Composable
fun ClickableTextExample() {
var text by remember { mutableStateOf("點擊按鈕變更文字") }
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = text, fontSize = 20.sp)
Button(onClick = { text = "文字已改變!" }) {
Text("點擊更改文字")
}
}
}
```
### Button onClick 觸發不同事件
Jetpack Compose 支援多種點擊事件,例如:
* onClick(一般按鈕點擊)
* Modifier.clickable {}(任何組件點擊)
* onLongClick(長按)
長按事件:
```kotlin=
@Composable
fun LongClickExample() {
var message by remember { mutableStateOf("長按按鈕改變文字") }
Button(
onClick = { message = "短按" },
modifier = Modifier.combinedClickable(
onClick = { message = "短按" },
onLongClick = { message = "長按" }
)
) {
Text("點擊或長按")
}
Text(text = message, fontSize = 20.sp)
}
```
* onClick 短按時執行
* onLongClick 長按時執行
### LaunchedEffect 執行事件
如果你想在 畫面載入時執行某些動作,可以用 LaunchedEffect:
```kotlin=
@Composable
fun LoadDataExample() {
var message by remember { mutableStateOf("載入中...") }
LaunchedEffect(Unit) {
delay(2000) // 模擬載入 2 秒
message = "載入完成!"
}
Text(text = message, fontSize = 24.sp)
}
```
* LaunchedEffect(Unit):只在畫面載入時執行一次
* delay(2000):延遲 2 秒後更新 UI
## UI 範例2
設計一個畫面(包含標題、輸入框、按鈕)
```kotlin=
@Composable
fun LoginScreen() {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "登入", fontSize = 30.sp, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = username,
onValueChange = { username = it },
label = { Text("使用者名稱") }
)
Spacer(modifier = Modifier.height(8.dp))
TextField(
value = password,
onValueChange = { password = it },
label = { Text("密碼") },
visualTransformation = PasswordVisualTransformation()
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { Log.d("LoginScreen", "登入按鈕被點擊") }) {
Text("登入")
}
}
}
```
🔹 TextField → 用來輸入帳號密碼
🔹 PasswordVisualTransformation() → 讓密碼隱藏
🔹 Spacer(modifier = Modifier.height(16.dp)) → 增加間距
🔹 Button → 登入按鈕
### 顯示 UI
💡在 MainActivity.kt
```kotlin=
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyTestTheme {
LoginScreen() // 設定要顯示的 UI
}
}
}
}
```
LoginScreen() 就會顯示在 MainActivity 內!
## UI Text
```kotlin=
@Composable
fun TextExample() {
Text(
text = "Hello, Compose!",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.Blue
)
}
```
🔹 fontSize = 24.sp → 設定文字大小
🔹 fontWeight = FontWeight.Bold → 設定字體加粗
🔹 color = Color.Blue → 設定文字顏色
## UI Button
```kotlin=
@Composable
fun ButtonExample() {
Button(
onClick = { Log.d("ButtonExample", "按鈕被點擊!") }
) {
Text(text = "點我!")
}
}
```
🔹 onClick = {} → 設定按鈕點擊事件
* onClick 短按時執行
* onLongClick 長按時執行
🔹 Text(text = "...") → 按鈕內的文字
## UI Image
```kotlin=
@Composable
fun ImageExample() {
Image(
painter = painterResource(id = R.drawable.sample_image),
contentDescription = "示例圖片",
modifier = Modifier.size(200.dp)
)
}
```
🔹 painterResource(id = R.drawable.sample_image) → 加載 res/drawable 內的圖片
🔹 modifier = Modifier.size(200.dp) → 設定圖片大小
## UI TextField(文字輸入框)
```kotlin=
@Composable
fun TextFieldExample() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("輸入文字") }
)
}
```
🔹 remember { mutableStateOf("") } → 記錄輸入的值
🔹 onValueChange = { text = it } → 更新輸入的內容
## UI Switch(切換開關)
```kotlin=
@Composable
fun SwitchExample() {
var isChecked by remember { mutableStateOf(false) }
Switch(
checked = isChecked,
onCheckedChange = { isChecked = it }
)
}
```
🔹 checked = isChecked → 綁定開關狀態
🔹 onCheckedChange = {} → 設定開關事件
## Column 垂直排列
Column 是一種 Composable 函數,用來 垂直排列 其內部的子項目
```kotlin=
@Composable
fun ColumnExample() {
Column(
modifier = Modifier
.fillMaxSize() //讓 Column 填滿整個螢幕
.padding(16.dp), //設定與邊界的距離
verticalArrangement = Arrangement.spacedBy(8.dp), // 控制子項的垂直間距
horizontalAlignment = Alignment.CenterHorizontally // 控制子項的水平對齊方式
) {
Text(text = "第一個項目")
Text(text = "第二個項目")
Button(onClick = { /* 點擊事件 */ }) {
Text(text = "點我")
}
}
}
```
* modifier:用來設定 Column 的大小、間距、顏色等。
[modifier基本設定](https://hackmd.io/ACwuFxU-RMGrzvzTix4qeA?view#Modifier)
* verticalArrangement:設定子項在垂直方向上的排列方式,例如:
Arrangement.Top(預設):從上到下排列
Arrangement.Center:子項置中排列
Arrangement.Bottom:從下到上排列
Arrangement.SpaceBetween:首項和尾項貼齊,其他均勻分布
Arrangement.SpaceEvenly:所有子項均勻分布(含首尾)
Arrangement.SpaceAround:每個子項之間間距相等,首尾的間距為子項間距的一半
* horizontalAlignment:設定子項在水平方向的對齊方式,例如:
Alignment.Start:靠左對齊
Alignment.CenterHorizontally:置中對齊
Alignment.End:靠右對齊
Arrangement.SpaceAround:元素之間有相等的間距
### 背景設定
```kotlin=
Box(
modifier = Modifier
.fillMaxSize()
.padding(12.dp) // 設定內部間距
.background(Color.Cyan) // 設定背景顏色
.size(200.dp)
.border(4.dp, Color.Red, RoundedCornerShape(16.dp)) // 設定邊框
)
```
### 補充
如果需要 水平排列 子項,可以使用 Row,如果需要 重疊 子項,可以使用 Box。
### 垂直的物件中也有水平的寫法
```csharp=
Box( ... ) {
Text(...)
//這裡用Row,用設定來控制物件水平擺放!!!
Row(Modifier.align(Alignment.Center), verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(...)
Icon(...)
OutlinedTextField(...)
}
Button(...) { Text("HAHAHA") }
}
```
## UI 基本設定
在UI物件後的()內加入一些設定即可
### Text
```kotlin=
@Composable
fun StyledText() {
Text(
text = "Hello Compose!",
fontSize = 24.sp, // 文字大小
fontWeight = FontWeight.Bold, // 字體加粗
color = Color.Blue, // 文字顏色
textAlign = TextAlign.Center, // 文字對齊
fontFamily = FontFamily.Serif, // 設定字型
modifier = Modifier.fillMaxWidth() // 讓文字撐滿整行
modifier = Modifier
.clip(RoundedCornerShape(12.dp)) // 設定圓角
.background(Color.Green) // 設定背景顏色
.padding(12.dp) // 增加內距,讓背景有空間
)
}
```
### Button
```kotlin=
@Composable
fun StyledButton() {
Button(
onClick = { /* 按鈕點擊事件 */ },
colors = ButtonDefaults.buttonColors(
containerColor = Color.Green, // 按鈕背景顏色
contentColor = Color.White // 按鈕內文字顏色
),
modifier = Modifier
.padding(16.dp) // 設定按鈕與周圍的間距
.size(width = 150.dp, height = 50.dp) // 設定按鈕大小
) {
Text("點我!")
}
}
```
### Image
```kotlin=
@Composable
fun ImageExample() {
Image(
painter = painterResource(id = R.drawable.sample_image),
contentDescription = "範例圖片",
modifier = Modifier
.size(150.dp) // 設定圖片大小
.clip(RoundedCornerShape(12.dp)) // 設定圓角
)
}
```
🔹 size(150.dp) → 設定圖片大小
🔹 clip(RoundedCornerShape(12.dp)) → 設定圖片圓角
### TextField(文字輸入框)
```kotlin=
@Composable
fun TextFieldExample() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("輸入文字") }
)
}
```
🔹 remember { mutableStateOf("") } → 記錄輸入的值
🔹 onValueChange = { text = it } → 更新輸入的內容
### Switch(切換開關)
```kotlin=
@Composable
fun SwitchExample() {
var isChecked by remember { mutableStateOf(false) }
Switch(
checked = isChecked,
onCheckedChange = { isChecked = it }
)
}
```
🔹 checked = isChecked → 綁定開關狀態
🔹 onCheckedChange = {} → 設定開關事件
### Spacer(間距)
```kotlin=
@Composable
fun SpacerExample() {
Column {
Text("第一行")
Spacer(modifier = Modifier.height(16.dp)) // 設定垂直間距
Text("第二行")
}
}
```
🔹 Spacer(modifier = Modifier.height(16.dp)) → 設定 16dp 的垂直間距
🔹 Spacer(modifier = Modifier.width(16.dp)) → 設定 16dp 的水平間距
### 總結
Jetpack Compose UI 基本設定
功能 | 語法範例
-------------|------------------------------------------------------
文字大小 | fontSize = 24.sp
文字顏色 | color = Color.Red
按鈕顏色 | ButtonDefaults.buttonColors(containerColor = Color.Green)
填滿畫面 | fillMaxSize()
間距 | padding(16.dp)
背景顏色 | background(Color.Yellow)
邊框 | border(2.dp, Color.Black, RoundedCornerShape(8.dp))
圖片圓角 | clip(RoundedCornerShape(12.dp))
間距元件 | Spacer(modifier = Modifier.height(16.dp))
## Modifier
調整大小、間距、顏色、點擊事件 等等。
```kotlin=
@Composable
fun ModifierExample() {
Text(
text = "Hello Compose!",
modifier = Modifier
.padding(16.dp) // 設定內邊距
.background(Color.Cyan) // 設定背景顏色
.border(2.dp, Color.Black) // 設定邊框
.padding(8.dp) // 內部再加一層 padding
)
}
```
| 功能 | 語法 | 作用 |
|----------------|---------------------------------------|----------------------------|
| 內邊距 | `padding(16.dp)` | 設定內部間距 |
| 外邊距 | `padding(start = 8.dp, top = 16.dp)`| 設定個別方向的間距 |
| 填滿寬度 | `fillMaxWidth()` | 讓元件填滿父容器的寬度 |
| 填滿高度 | `fillMaxHeight()` | 讓元件填滿父容器的高度 |
| 填滿整個區域 | `fillMaxSize()` | 讓元件填滿父容器的寬度 + 高度 |
| 設定大小 | `size(100.dp, 50.dp)` | 設定元件的固定大小 |
| 設定背景顏色 | `background(Color.Red)` | 設定背景顏色 |
| 設定圓角背景 | `clip(RoundedCornerShape(8.dp))` | 設定圓角效果 |
| 設定邊框 | `border(2.dp, Color.Black)` | 設定邊框粗細、顏色 |
| 設定陰影 | `shadow(8.dp, RoundedCornerShape(12.dp))` | 設定陰影效果 |
| 設定點擊事件 | `clickable { /* 點擊時執行 */ }` | 設定點擊行為 |
## 變數
[kotlin筆記](/aziUKMMTT3qzMkJG5lzrXg),這裡有說到一些相關知識
```kotlin=
var random by remember { mutableStateOf(0) } //int
var guess by remember { mutableStateOf("") } //string
```
----
```kotlin=
val name: String = "Alice" // 不可變
var age: Int = 25 // 可變
```
但在 Jetpack Compose 的 Composable 函式中,這種變數 不會觸發 UI 更新。
### remember、random
remember 的作用是讓變數在組合時保持不變,避免每次組合時變數重新初始化:
```kotlin=
val randomValue = (1..100).random() // 這個值每次重組時都會變
val rememberedValue = remember { (1..100).random() } // 這個值只會初始化一次
```
### rememberSaveable 渲染問題
因為螢幕旋轉、App 進入背景等情況被銷毀,remember 會 失效
```kotlin=
@Composable
fun SaveableExample() {
var text by rememberSaveable { mutableStateOf("") } // 當畫面旋轉時,值不會丟失
Column {
TextField(
value = text,
onValueChange = { text = it }
)
Text(text = "輸入: $text")
}
}
-----------------------------------------------------------
var tall by rememberSaveable { 0 } // 正確 ✅
```
🔹 rememberSaveable 會 自動保存變數狀態,即使畫面旋轉,輸入的文字 仍然會被保留。
### mutableStateListOf列表
如果要讓列表具有 狀態管理,可以用 mutableStateListOf():
```kotlin=
@Composable
fun TodoList() {
val tasks = remember { mutableStateListOf("買牛奶", "運動", "寫程式") }
Column {
tasks.forEach { task ->
Text(text = task)
}
Button(onClick = { tasks.add("新任務") }) {
Text("新增任務")
}
}
}
```
🔹 mutableStateListOf() 讓列表能夠響應 add()、remove() 操作,自動更新 UI。
### mutableStateMapOf() (可變 Map)
```kotlin=
@Composable
fun UserMap() {
val users = remember { mutableStateMapOf("Alice" to 25, "Bob" to 30) }
Column {
users.forEach { (name, age) ->
Text(text = "$name, 年齡: $age")
}
Button(onClick = { users["Charlie"] = 28 }) {
Text("新增 Charlie")
}
}
}
```
🔹 mutableStateMapOf() 讓 Map 變數可以響應 UI 變化。
### 總結
| 方法 | 用途 | UI 會自動更新? | 旋轉螢幕後會保留值? |
|-----------------------------------|--------------------|----------------|---------------------|
| `val / var` | 一般變數 | ❌ | ❌ |
| `remember { mutableStateOf(...) }`| UI 變數 | ✅ | ❌ |
| `rememberSaveable { mutableStateOf(...) }` | 可保存的 UI 變數 | ✅ | ✅ |
| `mutableStateListOf()` | 可變列表 | ✅ | ❌ |
| `mutableStateMapOf()` | 可變 Map | ✅ | ❌ |
## LaunchedEffect(Unit) 啟動時運行
LaunchedEffect 會在 Composable 函式啟動時運行一次,當 key 變更時重新運行。
LaunchedEffect 用來執行 副作用 (side effect),例如:
* 網路請求
* 資料庫查詢
* 延遲操作 (delay())
* 只執行一次的初始化邏輯
### 基本語法
```kotlin=
LaunchedEffect(Unit) {
// 這裡的程式碼只會執行一次
Log.d("LaunchedEffect", "這段程式碼執行了!")
}
```
Unit 代表 不依賴任何變數,所以這段程式碼 只會執行一次。
### LaunchedEffect 只執行一次
```kotlin=
@Composable
fun ExampleScreen() {
LaunchedEffect(Unit) {
Log.d("LaunchedEffect", "這段程式碼只會執行一次!")
}
Text(text = "Hello Compose!")
}
```
LaunchedEffect(Unit) 只會執行一次,不管 ExampleScreen() 重新組合多少次。
### LaunchedEffect + delay()
可以用 LaunchedEffect 來做 倒數計時:
```kotlin=
@Composable
fun CountdownTimer() {
var timeLeft by remember { mutableStateOf(5) }
LaunchedEffect(Unit) {
while (timeLeft > 0) {
delay(1000L) // 每秒減 1
timeLeft--
}
}
Text(text = "倒數計時: $timeLeft 秒")
}
```
🔹 delay(1000L) → 每秒更新一次 timeLeft
🔹 當 timeLeft == 0,while 迴圈結束
### key變更時重新執行
如果 LaunchedEffect 傳入一個 可變 Key,當 Key 變更時,LaunchedEffect 會重新執行。
```kotlin=
@Composable
fun LaunchedEffectWithKey(userId: String) {
LaunchedEffect(userId) {
Log.d("LaunchedEffect", "載入用戶資料: $userId")
}
Text(text = "用戶 ID: $userId")
}
```
🔹 LaunchedEffect(userId)
* 當 userId 改變時,LaunchedEffect 會重新執行
* 但如果 userId 沒變,LaunchedEffect 不會重新執行
## Shared Preferences 儲存
這個儲存就是不管你關閉再開啟程式 還是 關閉模擬機再開啟,你的資料都會在
[elias chen](https://hackmd.io/@eliaschen-dev) 裡整理的好棒棒!!默默學習做筆記...
```kotlin=
val context = LocalContext.current //宣告
val sharedPref = context.getSharedPreferences("test", Context.MODE_PRIVATE)
//存取random的值。如果沒有就存0
var random by remember { mutableStateOf(sharedPref.getInt("random", 0) ?: 0) }
LaunchedEffect(random) {
sharedPref.edit().putInt("random", random).apply()
}
```
## let + 函數
用來避免 null 錯誤。
### let 的語法,判斷是否為null
```kotlin=
變數?.let { 非空時執行的程式碼 }
```
### 等價寫法 (使用 if 判斷):
```kotlin=
if (bmi != null) {
Text("你的 BMI: ${String.format("%.2f", bmi)}")
}
```
使用 ?.let 更簡潔,不需要手動檢查 null。
## 套件安裝
在資料裡點選build.gradle.kts

即可加入要安裝的套件,再回到主程式碼重新更新