Jetpack Compose UI 設計 Android App


本文撰寫時間為 2025 年 7 月,請注意該文章的介紹是否與閱讀時有落差。
甚麼是 Jetpack Compose
Jetpack Compose 是為了簡化 UI 開發而設計的新型工具包,目的是解決 XML 開發 UI 上與邏輯割裂與不夠及時響應的問題,負責處理 App 中的 Activity 部分是目前官方最推薦開發 Android UI 的方法。
官方在 https://github.com/android/compose-samples 有提供多個精美 UI參考。
他的核心優勢在於宣告式的 UI,傳統的 XML 會遇到以下問題
使用 XML 建立 UI,然後在 Activity/Fragment 透過
findViewById
取得元件。UI 與邏輯分散在不同檔案,當狀態改變時,需要手動更新元件,例如:
textView.text = "New value" button.isEnabled = false
這容易導致:
重複程式碼
狀態與畫面不同步
維護困難
Compose 的解法
Compose 採用 宣告式 UI:
UI 是資料的函數 (UI = f(State))。
只要狀態改變,UI 會自動重新組合 (Recompose)。
不需要手動更新 UI。
@Composable
fun Greeting() {
var name by remember { mutableStateOf("World") }
Column {
Text("Hello $name")
Button(onClick = { name = "Compose" }) {
Text("Change Name")
}
}
}
優勢:
UI 與狀態自動同步
不需要「命令式」更新 UI
大幅降低 UI bug
完全用 Kotlin 撰寫 UI
傳統 View 系統
UI 在 XML
邏輯在 Kotlin/Java
需要額外的
R.id
來連結 UI 元件重構容易斷鏈(改了 XML id 要同步改 Kotlin)
Compose 的解法
UI 直接寫在 Kotlin 裡
沒有
R.id
、沒有 XML,UI 元件就是 Kotlin 函式任何 IDE refactor 都可直接應用在 UI
@Composable
fun LoginButton(onClick: () -> Unit) {
Button(onClick = onClick) {
Text("Login")
}
}
優勢:
單一語言開發(不用在 XML 和 Kotlin 之間切換)
IDE 自動提示、型別安全
開發流程更順暢
內建狀態管理
傳統 View 系統
狀態管理需要:
findViewById
更新 UILiveData 或 DataBinding 搭配觀察者
維護 UI 狀態同步的額外程式碼
Compose 的解法
Compose 內建狀態機制:
remember
與mutableStateOf
UI 會自動因狀態更新而重新產生
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
Text("Count: $count")
Button(onClick = { count++ }) { Text("Increment") }
}
}
優勢:
不需要額外的 View Binding
不需要手動呼叫
setText()
狀態管理簡單且直覺
可組合與可重複使用元件
傳統 View 系統
UI 重複時需要建立
include layout
或建立自訂 View Class,增加開發複雜度
Compose 的解法
UI 是函式 → 可直接重複使用
支援參數化 → 輕鬆建立可客製化元件
@Composable
fun StyledButton(label: String, onClick: () -> Unit) {
Button(onClick = onClick) {
Text(label)
}
}
@Composable
fun Example() {
Column {
StyledButton("Save") { /* save action */ }
StyledButton("Cancel") { /* cancel action */ }
}
}
優勢:
高度模組化
減少重複程式碼
容易維護與測試
更簡單的 UI 更新與動畫
傳統 View 系統
需要使用
ObjectAnimator
、AnimatorSet
需管理 View 屬性與狀態轉換
動畫與 UI 綁定複雜
Compose 的解法
內建動畫 API
直接用
animate*AsState
即可:
@Composable
fun AnimatedBox() {
var isExpanded by remember { mutableStateOf(false) }
val size by animateDpAsState(if (isExpanded) 200.dp else 100.dp)
Box(
modifier = Modifier
.size(size)
.background(Color.Red)
.clickable { isExpanded = !isExpanded }
)
}
優勢:
無需額外動畫物件
以狀態驅動動畫 → 簡單直覺
更快的 UI 預覽與開發循環
傳統 View 系統
需在 XML Layout Editor 或跑實機看效果
常常要重建專案才能看到 UI 變化
Compose 的解法
@Preview
可直接在 IDE 預覽支援多種裝置尺寸與主題
@Preview(showBackground = true)
@Composable
fun PreviewGreeting() {
Greeting()
}
優勢:
快速調整 UI
大幅縮短開發迭代時間
簡化 Navigation 與架構整合
傳統 View 系統
需要 FragmentManager
Transaction API 冗長
BackStack 管理複雜
Compose 的解法
使用
Navigation Compose
以簡單的
NavHost
管理畫面
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("detail/{id}") { backStackEntry ->
DetailScreen(backStackEntry.arguments?.getString("id"))
}
}
優勢:
減少 Fragment 與 Transaction 的負擔
UI 與導航整合一致
可漸進式採用
Compose 可以和傳統 View 並存。
可以在現有專案中逐步導入,不必完全重寫。
setContentView(ComposeView(this).apply {
setContent { Greeting() }
})
優勢:
無需一次性大改專案
平滑遷移成本低
Android 轉向 Jetpack Compose:
取代 XML 與 View 系統
宣告式 UI
內建狀態管理
API 24 以上無需額外設定
文章目標:
了解基本 Composable
能管理狀態
建立多畫面導航
Compose UI 架構
Compose 架構圖
Activity → setContent { NavHost() }
↓
Composables
↓
State (ViewModel)
核心概念
@Composable
函式:UI 元件State 驅動 UI → 單向資料流
Navigation Compose → 畫面切換
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hello Compose")
}
}
}
Compose 基礎語法
Text
用於顯示文字。
@Composable
fun TextExample() {
Text("Hello Compose")
}
常見屬性
text
:要顯示的文字。fontSize
:文字大小(使用sp
)。fontWeight
:字體粗細。color
:文字顏色。modifier
:修飾(如間距、對齊)。
@Composable
fun TextStyledExample() {
Text(
text = "Welcome to Jetpack Compose",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.Blue,
modifier = Modifier.padding(16.dp)
)
}
Button
用於建立可點擊的按鈕。
@Composable
fun ButtonExample() {
Button(onClick = { println("Button clicked") }) {
Text("Click Me")
}
}
常見屬性
onClick
:按下時的動作。modifier
:設定按鈕大小、間距。enabled
:控制按鈕是否可點擊。
進階範例
@Composable
fun ButtonStyledExample() {
Button(
onClick = { println("Saved!") },
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
enabled = true
) {
Text("Save", fontSize = 18.sp)
}
}
Column
是垂直排列的容器,類似於傳統 Android 的 LinearLayout
(vertical)。
基本用法
@Composable
fun ColumnExample() {
Column {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}
常見屬性
modifier
:控制整體大小、間距。horizontalAlignment
:控制水平對齊方式。verticalArrangement
:控制垂直間距。
進階範例
@Composable
fun ColumnStyledExample() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Top")
Text("Middle")
Text("Bottom")
}
}
Row
是水平排列的容器,類似於 LinearLayout
(horizontal)。
基本用法
@Composable
fun RowExample() {
Row {
Text("Left")
Text("Center")
Text("Right")
}
}
常見屬性
horizontalArrangement
:控制水平間距(例如Arrangement.SpaceBetween
)。verticalAlignment
:垂直對齊方式。
進階範例
@Composable
fun RowStyledExample() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Left")
Button(onClick = {}) { Text("Action") }
Text("Right")
}
}
Modifier
是用來修飾 Composable 的強大工具,可以調整大小、間距、背景、點擊事件等。
常見用法
@Composable
fun ModifierExample() {
Text(
text = "Styled Text",
modifier = Modifier
.padding(16.dp)
.background(Color.Yellow)
.fillMaxWidth()
.clickable { println("Text clicked!") }
)
}
常見函式
padding()
:內距。background()
:背景色。fillMaxWidth()
/fillMaxSize()
:填滿寬度或整個畫面。size()
:設定固定大小。clickable()
:增加點擊事件。
佈局與修飾符
Modifier.padding()
Modifier.fillMaxWidth()
@Composable
fun GreetingScreen() {
Column(
modifier = Modifier.padding(16.dp)
) {
Text("Hello Compose", fontSize = 20.sp)
Button(onClick = { println("Clicked!") }) {
Text("Click Me")
}
}
}
整合範例
以下是把 Text
、Button
、Column
、Row
與 Modifier
整合在一起的範例:
@Composable
fun FullExample() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text("Hello Jetpack Compose", fontSize = 22.sp, fontWeight = FontWeight.Bold)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(onClick = { println("Left clicked") }) {
Text("Left")
}
Button(onClick = { println("Right clicked") }) {
Text("Right")
}
}
Text(
text = "Click Me!",
modifier = Modifier
.background(Color.LightGray)
.fillMaxWidth()
.padding(8.dp)
.clickable { println("Text clicked") }
)
}
}
狀態管理
狀態基礎
remember
、mutableStateOf
State 驅動 UI
實作:計數器
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
Column {
Text("Count: $count")
Button(onClick = { count++ }) { Text("Increment") }
}
}
畫面切換與可重複使用元件
Navigation Compose
可組合 UI 元件
@Composable
fun StyledButton(label: String, onClick: () -> Unit) {
Button(onClick = onClick) { Text(label) }
}
- 導航示範
NavHost(navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("detail/{id}") { backStackEntry ->
DetailScreen(backStackEntry.arguments?.getString("id"))
}
}
動態 Text (顯示狀態)
用
remember
與mutableStateOf
來記錄狀態。Text
會根據狀態變化自動重新組合(Recompose)。
@Composable
fun DynamicTextExample() {
var message by remember { mutableStateOf("Hello Compose") }
Column(modifier = Modifier.padding(16.dp)) {
Text(text = message, fontSize = 20.sp)
Button(onClick = { message = "Button Clicked!" }) {
Text("Change Text")
}
}
}
重點:
當 message
變動時,Compose 會自動重新繪製 Text
,不需要像傳統 Android 手動呼叫 findViewById
或 setText()
。
Button + Counter (計數器)
說明
透過
Button
點擊次數更新狀態。Text
即時顯示最新值。
@Composable
fun CounterExample() {
var count by remember { mutableStateOf(0) }
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(text = "Count: $count", fontSize = 24.sp)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(onClick = { count++ }) {
Text("Increment")
}
Button(onClick = { count = 0 }) {
Text("Reset")
}
}
}
}
重點:
使用
remember
讓狀態在組合期間持續存在。任何狀態變化都會自動更新 UI。
Column + 動態清單
說明
動態清單 UI:按下按鈕新增項目。
展示
Column
與狀態結合的效果。
@Composable
fun DynamicListExample() {
var items by remember { mutableStateOf(listOf("Item 1", "Item 2")) }
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items.forEach { item ->
Text(text = item, fontSize = 18.sp)
}
Button(onClick = {
items = items + "Item ${items.size + 1}"
}) {
Text("Add Item")
}
}
}
重點:
狀態改變(
items
新增元素) → UI 自動重繪。forEach
可直接動態產生多個Text
元件。
Row + 狀態互動
範例說明
使用
Row
水平排列元件。讓使用者可以「加一」或「減一」。
@Composable
fun RowStateExample() {
var value by remember { mutableStateOf(0) }
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Button(onClick = { value-- }) { Text("-") }
Text("Value: $value", fontSize = 20.sp)
Button(onClick = { value++ }) { Text("+") }
}
}
重點:
Row
搭配Arrangement.SpaceBetween
,能清楚分配按鈕與文字的位置。
Modifier + 動態 UI
範例說明
利用狀態切換背景顏色。
示範
Modifier
動態控制 UI 外觀。
@Composable
fun ModifierStateExample() {
var isActive by remember { mutableStateOf(false) }
Text(
text = if (isActive) "Active" else "Inactive",
color = Color.White,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.background(if (isActive) Color.Green else Color.Gray)
.clickable { isActive = !isActive }
.padding(16.dp)
)
}
重點:
Modifier.background()
可根據狀態動態切換。點擊文字即能改變狀態 → UI 自動更新。
Subscribe to my newsletter
Read articles from AgriLinq Admin directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
