iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0

前言

昨天我們提到,現在的Android Studio改用Jetpack Compose來做為他的UI工具包,這和之前的XML的方式不同,今天我們來探討一下Jetpack Compose和XML的差異,並簡單介紹一下如何使用Jetpack Compose建構基本的Android元件。

Jetpack Compose是甚麼?

Jetpack Compose的簡介

Jetpack Compose 是 Android 的現代 UI 工具包,它使用 Kotlin 語言以聲明式的方式來構建界面。他與傳統的 XML 佈局設計方式不同,因為 Compose 允許你直接在 Kotlin 程式碼中定義 UI,並且能夠根據狀態的變化來自動更新界面。

Jetpack Compose和XML的差異

Jetpack Compose 和傳統的 XML 佈局在 Android 開發中有很大的不同,這些差異主要來自於它們的設計理念、操作方式,以及它們如何處理 UI 元件的變更。下面我們來看看兩者的區別。

1. 聲明式 vs. 命令式 UI 開發

聲明式 命令式
Jetpack Compose XML
聲明式是 Jetpack Compose 的核心思想。你只需定義界面應該「看起來是什麼樣子」,然後根據狀態的變化自動更新 UI。也就是說,當應用的狀態發生變化時,Compose 會自動重新渲染與該狀態相關的 UI 元件。 XML 佈局屬於 命令式 UI 編程風格。你首先定義靜態的 XML 文件來描述界面的結構,然後通過 Kotlin 或 Java 程式碼來動態地修改界面元件的屬性和行為。例如,你可能需要明確地告訴某個按鈕何時顯示、何時隱藏,或手動更新文字。

可能光看上面的敘述沒辦法很直觀的知道他們的區別,下面讓我們來看一下兩者在Android Studio分別是怎麼使用元件的。

  • Jetpack Compose

如果你昨天有用Android Studio動手創建一個新的專案,你應該會看到他幫你預設建立的一個MainActivity.kt檔案,裡面你能見到下面這段程式碼

  • kotlin
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
} /

在 Compose 中,你定義 UI 時是基於「狀態驅動」的。當狀態改變時,UI 會自動反映這些改變。在上面的程式碼中Greeting 是一個聲明式的 Composable 函數,它會自動根據 name 的值來更新顯示的文字。

  • XML

而以前的XML佈局則是在 XML 中,定義靜態的 UI 元素,通常會在layout資料夾中的activity_main.xml看到下面這段程式碼

  • 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" />

當你想要更新上面的資訊時往往要在程式碼中手動查找和更新。

2. UI 元件的定義方式

  • Jetpack Compose

Jetpack Compose 完全使用 Kotlin 函數 來定義 UI,不再需要 XML 文件。每個 UI 元件都是由 @Composable 函數來描述的,並且這些函數可以很容易地組合和重用,UI 的定義和應用邏輯是在同一個檔案內進行。
例如,Jetpack Compose使用Text 元件顯示文字的程式碼如下

  • kotlin
@Composable
fun MyText() {
    Text(text = "Hello Jetpack Compose")
}
  • XML

XML的UI是在 XML 文件中定義的,而邏輯處理則是在 Kotlin 或 Java 檔案中進行。這意味著你需要在兩個不同的檔案(XML 和 Kotlin/Java)中來回切換,這樣會增加開發的複雜度。
和Jetpack Compose不同的是XML要使用使用Text 元件顯示文字的話,需要在兩個程式碼裡進行宣告,程式碼如下

  • XML
<TextView
        <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello XML Layout" />

然後在 Kotlin 或 Java 檔案中的Activity手動綁定:

  • kotlin
val textView: TextView = findViewById(R.id.textView)
textView.text = "Updated Text"

3. UI 的更新與狀態管理

  • Jetpack Compose

在 Compose 中,UI 是根據應用狀態自動更新的。Compose 內部會自動觀察狀態的變化並重新渲染相關的 UI。你只需要改變狀態,UI 就會自動更新,這使得狀態管理更加簡單且直觀。
以下面這個程式為例

  • kotlin
@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Clicked $count times")
    }
}

上面這個程式碼使用 remembermutableStateOf 來管理元件的狀態,其功能是使該Button 按下後,count 值會改變,並且 UI 會自動更新顯示新的點擊次數。

  • XML

而在XML 中,狀態管理則需要使用例如 findViewById 方法手動操作 UI 元件。你需要自己追蹤狀態並手動更新 UI,這可能會導致程式碼冗長且難以維護。想完成和上面一樣更新按鈕的點擊次數功能的程式碼如下

  • kotlin
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的基本元件

Jetpack Compose 提供了許多基本的 UI 元件(稱為 Composables),例如 Text、Button、Column、Row 等。

  • Text:顯示文字內容。
@Composable
fun MyText() {
    Text(text = "Hello, Jetpack Compose!")
}
  • Button:建立按鈕,並可以定義按下按鈕時的行為。
@Composable
fun MyButton() {
    Button(onClick = { /* 執行按鈕點擊事件 */ }) {
        Text(text = "Click me")
    }
}
  • Column:垂直排列子元件。
@Composable
fun MyColumn() {
    Column {
        Text("First line")
        Text("Second line")
    }
}
  • Row:水平排列子元件。
@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最重要的布局方面的資料,好了,今天的內容就到這邊,感謝你能看到這邊,讓我們明天見。


上一篇
Day5:Android Studio啟動!!!
下一篇
Day7:Jetpack Compose的Layout方法
系列文
github裡永遠有一個還沒做的SideProject :用Kotlin來開發點沒用的酷東西30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言