iT邦幫忙

2024 iThome 鐵人賽

DAY 7
0

前言

我們昨天使用了Jetpack Compose創建了一個類似敲木魚系統的APP(小廢物),昨天我們在實作的過程中,只是簡單的將兩個元件放置在APP的中央,今天我們要更進一步的來探討APP中很重要的一個部分Layout(佈局) 系統,Layout是人使用APP第一眼就會注意到的東西,俗話說第一印像占了九成,一個好的Layout不僅能吸引住別人的目光,透過Layout合理的組合你的元件還能讓APP在使用的過程中更加的順手。

Jetpack Compose的Layout方法

Jetpack Compose 提供了非常靈活的 Layout系統,用於安排和排列UI元件。這與傳統的Android XML佈局方式不同,Jetpack Compose是以程式碼的形式來聲明UI,並且它強調聲明式組合性。接下來我會介紹 Jetpack Compose中一些最常用的佈局設計和概念。

1. 基本佈局元件

  • Column

Column是一個垂直佈局容器,內部的子元件會從上到下依序排列。範例如下

  • kotlin
@Composable
fun ColumnExample() {
    Column(
        verticalArrangement = Arrangement.Center, // 垂直居中排列
        horizontalAlignment = Alignment.CenterHorizontally // 水平置中
    ) {
        Text(text = "Item 1")
        Text(text = "Item 2")
        Text(text = "Item 3")
    }
}
  • verticalArrangement:定義子元件在垂直方向上的排列方式。

  • horizontalAlignment:定義子元件在水平方向上的對齊方式。
    我們將這個程式碼放在我們昨天的Greeting和Button中間,顯示的效果如下
    https://ithelp.ithome.com.tw/upload/images/20240921/201626494wFAKb90MQ.png

  • Row

Row是一個水平佈局容器,內部的子元件會從左到右依序排列。

  • kotlin
@Composable
fun RowExample() {
    Row(
        horizontalArrangement = Arrangement.SpaceBetween, // 在子元件間留空
        verticalAlignment = Alignment.CenterVertically // 垂直置中
    ) {
        Text(text = "Item A")
        Text(text = "Item B")
        Text(text = "Item C")
    }
}

我們將這個程式碼放繼續塞進我們的APP裡面,顯示的效果如下
https://ithelp.ithome.com.tw/upload/images/20240921/20162649T9RWBgLyh3.png

  • Box

Box是Jetpack Compose提供的一種 佈局容器,它允許你將多個子元件進行重疊或對齊,類似於Android傳統視圖系統中的 FrameLayout。Box提供了一個簡單而靈活的方式來堆疊和排列內容,並且可以讓你更加輕鬆地控制元件在父容器中的位置。

  • kotlin
@Composable
fun BoxExample() {
    Box(
        modifier = Modifier
            .size(200.dp) // 設定 Box 的大小
            .background(Color.LightGray), // 設定背景顏色
        contentAlignment = Alignment.Center // 設定 Box 內部子元件的對齊方式
    ) {
        Text(text = "Hello Box")
    }
}

我們將這個程式碼放繼續塞進我們的APP裡面,顯示的效果如下
https://ithelp.ithome.com.tw/upload/images/20240921/20162649cDHH5GSbIc.png
下面我們再介紹一些有關於Box的應用方式,舉例來說,我們可以透過Box將我們上面的Column和Row並排在一起,程式碼如下

  • kotlin
@Composable
fun BoxWithRowAndColumn() {
    Box(
        modifier = Modifier
            .size(300.dp)
            .background(Color.LightGray)
    ) {
        // 左側的 Row
        Row(
            modifier = Modifier
                .align(Alignment.CenterStart) // 將 Row 對齊到 Box 的左側
                .padding(8.dp),
            horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            Text(text = "Item A")
            Text(text = "Item B")
            Text(text = "Item C")
        }

        // 右側的 Column
        Column(
            modifier = Modifier
                .align(Alignment.CenterEnd) // 將 Column 對齊到 Box 的右側
                .padding(8.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Item 1")
            Text(text = "Item 2")
            Text(text = "Item 3")
        }
    }
}

顯示的效果如下
https://ithelp.ithome.com.tw/upload/images/20240921/20162649i32emR54lJ.png
同時Box也和能其他Box嵌套在一起,如下程式碼所示

  • kotlin
@Composable
fun ComplexBoxExample() {
    Box(
        modifier = Modifier
            .size(300.dp)
            .background(Color.Gray),
        contentAlignment = Alignment.TopCenter
    ) {
        Box(
            modifier = Modifier
                .size(150.dp)
                .background(Color.Blue)
                .align(Alignment.Center)
        ) {
            Text(
                text = "Nested Box",
                color = Color.White,
                modifier = Modifier.align(Alignment.Center)
            )
        }
        Text(
            text = "Outer Box",
            color = Color.White,
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(8.dp)
        )
    }
}

顯示的效果如下
https://ithelp.ithome.com.tw/upload/images/20240921/201626492SkvdjHZy9.png

2. 佈局修飾符 (Modifiers)

上面再演示基本元件時,為了設置元件的大小以免重疊和出界,我們使用了Modifiers 來設置元件的大小、佈局、外觀以及行為,下面簡單的介紹一下Modifiers會用到的參數

常用的 Modifiers:

  • padding(): 設置內邊距。
  • fillMaxSize(): 讓元件佔據可用空間。
  • width(), height(): 設置元件的寬度和高度。
  • background(): 設置背景色。

3. 更進階的佈局控制

  • Spacer

Spacer 是一個佔位元件,用來在佈局中增加間距。通常與Row或Column一起使用。

  • kotlin
@Composable
fun SpacerExample() {
    Column {
        Text(text = "Item 1")
        Spacer(modifier = Modifier.height(16.dp)) // 添加16dp的垂直間距
        Text(text = "Item 2")
    }
}

顯示的效果如下
https://ithelp.ithome.com.tw/upload/images/20240921/20162649aDIMZ98CT6.png

  • LazyColumn / LazyRow
    LazyColumnLazyRow是用來顯示大量資料的佈局,類似於傳統的RecyclerView。
  • kotlin
@Composable
fun LazyColumnExample() {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.spacedBy(8.dp) // 子元件間距
    ) {
        items(100) { index ->
            Text(text = "Item $index", modifier = Modifier.padding(16.dp))
        }
    }
}

顯示的效果如下
https://ithelp.ithome.com.tw/upload/images/20240921/20162649rF3RRPZLb2.png
Spacer和上面提到的Modifiers.padding()很像都是用來處理元件之間的間距,但它們的用途和使用情境有所不同,Modifier.padding()是用來設定元件的內邊距或外邊距。它可以用來在元件的四周增加空間,讓元件與其他元件保持距離。
Spacer是一個專門用來在佈局中增加空白空間的組件。它本身沒有任何內容,僅僅是佔據一定的空間,通常用在佈局中需要在兩個元件之間加入彈性間距的時候。由於他是一個元件因此,他可以使用Modifier.height()Modifier.width()來指定Spacer的高度或寬度,從而更自由的建構你想要的布局。

  • ConstraintLayout

ConstraintLayout是一個高級佈局,提供了更精確的佈局控制,類似於 XML 中的 ConstraintLayout。
通常預設的Jetpack Compose並沒有引入相關的依賴庫,因此會出現Unresolved reference: constraintlayout的報錯,你需要在你的build.gradle(Module-level,一般是 app 模組)檔案中添加添加了以下依賴項

dependencies {
    implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
}

在添加完依賴項後,點擊Sync同步你的專案,以確保依賴項正確地被引入。

  • kotlin
@Composable
fun ConstraintLayoutExample() {
    ConstraintLayout(
        modifier = Modifier.fillMaxSize()
    ) {
        val (text1, text2) = createRefs()

        Text(
            text = "Hello",
            modifier = Modifier.constrainAs(text1) {
                top.linkTo(parent.top, margin = 16.dp)
                start.linkTo(parent.start, margin = 16.dp)
            }
        )

        Text(
            text = "World",
            modifier = Modifier.constrainAs(text2) {
                top.linkTo(text1.bottom, margin = 16.dp)
                start.linkTo(text1.start)
            }
        )
    }
}
  • createRefs(): 用來建立約束參考。
  • constrainAs(): 設定元件的佈局約束。

4. Custom Layout 自定義佈局

除了上面這些元件和用法,你還可以使用Layout函數創建自定義的佈局。範例如下

  • kotlin
@Composable
fun CustomLayoutExample(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        content = content,
        modifier = modifier
    ) { measurables, constraints ->
        // 測量所有的子元件
        val placeables = measurables.map { measurable ->
            measurable.measure(constraints)
        }

        // 設定佈局的寬高
        layout(constraints.maxWidth, constraints.maxHeight) {
            // 排列每個元件
            var yPosition = 0
            placeables.forEach { placeable ->
                placeable.placeRelative(x = 0, y = yPosition)
                yPosition += placeable.height
            }
        }
    }
}

上面這個Layout會創建一個垂直佈局,所有子元件會從上到下依次排列,沒有間距,寬度會自動適應到父佈局的最大寬度,高度則根據子元件的高度累積。簡單來說,這個自定義佈局就像一個Column,將內容垂直堆疊在一起。
如果想使用你自訂的Layout,可以像下面一樣

  • kotlin
@Composable
fun CustomLayoutDemo() {
    CustomLayoutExample(
        modifier = Modifier.padding(16.dp) // 你可以傳入 Modifier 來設定 CustomLayoutExample 的修飾
    ) {
        // 傳入多個子組件
        Text(text = "First Item", modifier = Modifier.background(Color.Red))
        Text(text = "Second Item", modifier = Modifier.background(Color.Green))
        Text(text = "Third Item", modifier = Modifier.background(Color.Blue))
    }
}

我們創建了一個CustomLayoutDemo組件,並且在其中使用了CustomLayoutExample。我們傳入了三個 Text 元件作為子組件,這些元件將由CustomLayoutExample中的自定義佈局邏輯進行垂直排列。
顯示的效果如下
https://ithelp.ithome.com.tw/upload/images/20240921/20162649XFV3flbS9K.png

後話

今天我們簡單介紹了Jetpack Compose的Layout方法,為了後續開發出一個自己看得順眼的APP,今天的內容十分的重要,明天我們要結合這幾天的內容,做一個簡單的計算機APP來為第一週的複習內容做一個結尾,感謝你能看到這邊,讓我們明天再見。


上一篇
Day6:Jetpack Compose...?
下一篇
Day8:Jetpack Compose練習之簡易計算機APP
系列文
github裡永遠有一個還沒做的SideProject :用Kotlin來開發點沒用的酷東西30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言