昨天我們提到,現在的Android Studio改用Jetpack Compose來做為他的UI工具包,這和之前的XML的方式不同,今天我們來探討一下Jetpack Compose和XML的差異,並簡單介紹一下如何使用Jetpack Compose建構基本的Android元件。
Jetpack Compose 是 Android 的現代 UI 工具包,它使用 Kotlin 語言以聲明式的方式來構建界面。他與傳統的 XML 佈局設計方式不同,因為 Compose 允許你直接在 Kotlin 程式碼中定義 UI,並且能夠根據狀態的變化來自動更新界面。
Jetpack Compose 和傳統的 XML 佈局在 Android 開發中有很大的不同,這些差異主要來自於它們的設計理念、操作方式,以及它們如何處理 UI 元件的變更。下面我們來看看兩者的區別。
聲明式 | 命令式 |
---|---|
Jetpack Compose | XML |
聲明式是 Jetpack Compose 的核心思想。你只需定義界面應該「看起來是什麼樣子」,然後根據狀態的變化自動更新 UI。也就是說,當應用的狀態發生變化時,Compose 會自動重新渲染與該狀態相關的 UI 元件。 | XML 佈局屬於 命令式 UI 編程風格。你首先定義靜態的 XML 文件來描述界面的結構,然後通過 Kotlin 或 Java 程式碼來動態地修改界面元件的屬性和行為。例如,你可能需要明確地告訴某個按鈕何時顯示、何時隱藏,或手動更新文字。 |
可能光看上面的敘述沒辦法很直觀的知道他們的區別,下面讓我們來看一下兩者在Android Studio分別是怎麼使用元件的。
如果你昨天有用Android Studio動手創建一個新的專案,你應該會看到他幫你預設建立的一個MainActivity.kt
檔案,裡面你能見到下面這段程式碼
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
} /
在 Compose 中,你定義 UI 時是基於「狀態驅動」的。當狀態改變時,UI 會自動反映這些改變。在上面的程式碼中Greeting
是一個聲明式的 Composable 函數,它會自動根據 name 的值來更新顯示的文字。
而以前的XML佈局則是在 XML 中,定義靜態的 UI 元素,通常會在layout資料夾中的activity_main.xml
看到下面這段程式碼
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
當你想要更新上面的資訊時往往要在程式碼中手動查找和更新。
Jetpack Compose 完全使用 Kotlin 函數 來定義 UI,不再需要 XML 文件。每個 UI 元件都是由 @Composable
函數來描述的,並且這些函數可以很容易地組合和重用,UI 的定義和應用邏輯是在同一個檔案內進行。
例如,Jetpack Compose使用Text 元件顯示文字的程式碼如下
@Composable
fun MyText() {
Text(text = "Hello Jetpack Compose")
}
XML的UI是在 XML 文件中定義的,而邏輯處理則是在 Kotlin 或 Java 檔案中進行。這意味著你需要在兩個不同的檔案(XML 和 Kotlin/Java)中來回切換,這樣會增加開發的複雜度。
和Jetpack Compose不同的是XML要使用使用Text 元件顯示文字的話,需要在兩個程式碼裡進行宣告,程式碼如下
<TextView
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello XML Layout" />
然後在 Kotlin 或 Java 檔案中的Activity手動綁定:
val textView: TextView = findViewById(R.id.textView)
textView.text = "Updated Text"
在 Compose 中,UI 是根據應用狀態自動更新的。Compose 內部會自動觀察狀態的變化並重新渲染相關的 UI。你只需要改變狀態,UI 就會自動更新,這使得狀態管理更加簡單且直觀。
以下面這個程式為例
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Clicked $count times")
}
}
上面這個程式碼使用 remember
和 mutableStateOf
來管理元件的狀態,其功能是使該Button 按下後,count 值會改變,並且 UI 會自動更新顯示新的點擊次數。
而在XML 中,狀態管理則需要使用例如 findViewById
方法手動操作 UI 元件。你需要自己追蹤狀態並手動更新 UI,這可能會導致程式碼冗長且難以維護。想完成和上面一樣更新按鈕的點擊次數功能的程式碼如下
val button: Button = findViewById(R.id.button)
val textView: TextView = findViewById(R.id.textView)
var count = 0
button.setOnClickListener {
count++
textView.text = "Clicked $count times"
}
我們可以從上面的比較和範例了解,相較於需要在複數程式檔中來回編譯的命令式 UI,UI 的定義與邏輯分離,狀態管理和 UI 更新需要手動處理的XML來說,基於聲明式UI的Jetpack Compose提供了一個現代化的 UI 開發方式,狀態驅動的設計使得 UI 更新更加直觀和高效。為了學習新的UI方式我們後續將會使用Jetpack Compose的布局方式來進行開發。
Jetpack Compose 提供了許多基本的 UI 元件(稱為 Composables),例如 Text、Button、Column、Row 等。
@Composable
fun MyText() {
Text(text = "Hello, Jetpack Compose!")
}
@Composable
fun MyButton() {
Button(onClick = { /* 執行按鈕點擊事件 */ }) {
Text(text = "Click me")
}
}
@Composable
fun MyColumn() {
Column {
Text("First line")
Text("Second line")
}
}
@Composable
fun MyRow() {
Row {
Text("Left")
Text("Right")
}
}
結合今天的內容,我們可以實作一個簡單的更新按鈕的點擊次數小廢物
package com.example.a2024ironman
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.a2024ironman.ui.theme._2024ironmanTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
_2024ironmanTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
// 定義按鈕點擊次數的狀態
var clickCount by remember { mutableIntStateOf(0) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// 傳入點擊次數到 Greeting 函數
Greeting(name = "Clicked $clickCount times")
Spacer(modifier = Modifier.height(16.dp))
// 當按鈕被點擊時,更新點擊次數
MyButton(onClick = {
clickCount++
})
}
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = name, // 顯示點擊次數
modifier = modifier
)
}
@Composable
fun MyButton(onClick: () -> Unit) {
Button(
onClick = onClick,
colors = ButtonDefaults.buttonColors(
containerColor = Color.Blue, // 自定義按鈕背景色
contentColor = Color.White // 自定義按鈕文字顏色
)
) {
Text(text = "Click me")
}
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
_2024ironmanTheme {
Greeting(name = "Preview")
}
}
今天簡單的使用了Jetpack Compose寫了一個小廢物,我大學時期使用的還是以前XML的架構,現在看到這個新架構就' 學習新東西來使用看看,今天簡單介紹了一些元件相關的東西,明天研究一下APP最重要的布局方面的資料,好了,今天的內容就到這邊,感謝你能看到這邊,讓我們明天見。