###### tags: `android`, `compose`, `column`, `row`
###### title: [Compose 系列 - 2] UI - Layout Column/Row
# [Compose 系列 - 2] UI - Layout Column/Row
[TOC]
## 前言
上次在[[Compose 系列 - 1] UI - Layout Box & Modifier](/s23tCoUbQ_CN7c5_yNPMHQ)
講述了基本的 Layout: `Box` ,與 UI通用操作: `Modifier`, 接下來就繼續介紹 `Column` & `Row`
沒看過上篇的朋友, 也建議先看一下

## Column & Row
Row 和 Column 的操作是類似的. 這邊就以Row 進行示範, Column 反之亦然.
用法:
```kotlin=
@Composable
fun RowExample() {
Row {
Text("Hello World!")
Text("Hello World!2")
}
}
```
文字會並排再一起

> 圖片來源: [Jetpack compose playground](https://foso.github.io/Jetpack-Compose-Playground/layout/column/)
而Row 有兩個重要的操作:
1. 內容間距 `horizontalArrangement`
2. 垂直位置 `verticalAlignment`
---
### 調整間距: `horizontalArrangement`
1. [SpaceEvenly](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement#SpaceEvenly()) **左, 中, 右, 平均分配空白**:
```kotlin=
@Composable
fun RowExample() {
// 排列方式: #A#B#C#
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
Text("Hello World!")
Text("Hello World!2")
}
}
```

2. [SpaceBetween](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement#SpaceBetween) **左右貼齊邊緣, 其餘平均分配**:
```kotlin=
@Composable
fun RowExample() {
// A##B##C
Row(horizontalArrangement = Arrangement.SpaceBetween) {
...
}
}
```

3. [SpaceBy](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement#spacedBy(androidx.compose.ui.unit.Dp)) **固定間距**:
```kotlin=
@Composable
fun RowExample() {
Row(horizontalArrangement = Arrangement.SpaceBy(20.dp)) {
...
}
}
```

4. [Center](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement#Center) **置中**
```kotlin=
@Composable
fun RowExample() {
Row(horizontalArrangement = Arrangement.Center) {
...
}
}
```

更多操作請參考[API 文件](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/Arrangement)
也可以自定義歐~! :+1:
---
### 垂直位置: `verticalAlignment`
假設Row 現在固定高度 `50dp`
```kotlin=
@Composable
fun RowExample() {
Row(modifier = Modifier.height(50.dp)) {
Text("Hello World!")
Text("Hello World!2")
}
}
```
其內容預設擺放位置是在上方

可以更改參數: `verticalAlignment` 來改變垂直位置
```kotlin=
@Composable
fun RowExample() {
Row(
modifier = Modifier.height(50.dp),
verticalAlignment = Alignment.CenterVertically
) {
...
}
}
```

---
### RowScope
上次在講 [Box-Scope](https://hackmd.io/s23tCoUbQ_CN7c5_yNPMHQ?both#Special-Scope) 時有提及, `Modifier` 會在特別的 Layout 下有額外的操作, 聰明的你應該已經猜到了Row 有哪些操作:
1. **Modifier.weight(w: Float, fill: Boolean = true)**
2. **Modifier.align(alignment: Alignment.Vertical)**
3. **Modifier.alignBy(alignment: Alignment.Vertical)** & **Modifier.alignByBaseline()**
----
#### Modifier.weight(w: Float, fill: Boolean = true)
> 對應原有 `android:layout_weight`
先計算沒有權重的UI 空間後, 將剩餘的空間依照權重進行分配:
```kotlin=
@Composable
fun WeightSample() {
Row(Modifier.fillMaxWidth().height(40.dp)) {
Text("text 1w", Modifier.weight(1f).background(Color.LightGray))
Text("text 2w", Modifier.weight(2f).background(Color.Yellow))
Text("text 3w", Modifier.wrapContentSize().background(Color.Cyan))
}
}
```
1. 先行計算 `text 3w` 的寬度
2. 將剩餘空間, 依照 _1:2_ 的比例分配給 `text 1w` & `text 2w`

---
第二個參數`fill`, 意即 ==是否填滿空間== 的意思:
```kotlin=
@Composable
fun WeightSample() {
Row(Modifier.fillMaxWidth().height(40.dp)) {
Text("text 1w", Modifier.weight(1f).background(Color.LightGray))
Text("text 2w", Modifier.weight(1f, false).background(Color.Yellow))
}
}
```

上圖可以清楚看到, `text 1w` 佔了一半的空間.
而`text 2w` 雖然也有權重1, 但依舊是原本的大小.
---
#### Modifier.align
> 對應原有 `android:layout_gravity`
參考 [Box-scope](https://hackmd.io/s23tCoUbQ_CN7c5_yNPMHQ?both#Special-Scope)
---
#### Modifier.alignBy & Modifier.alignByBaseline
```kotlin=
@Composable
fun RowHorizontalAlignmentLineSample() {
Row(Modifier.fillMaxHeight()) {
// The center of the magenta Box and the baselines of the two texts will be
// vertically aligned. Note that alignBy() or alignByBaseline() has to be specified
// for all children we want to take part in the alignment. For example, alignByBaseline()
// means that the baseline of the text should be aligned with the alignment line
// (possibly another baseline) specified for siblings using alignBy or alignByBaseline.
// If no other sibling had alignBy() or alignByBaseline(), the modifier would have no
// effect.
Box(
modifier = Modifier
.size(80.dp, 40.dp)
.alignBy { 0 } // 上緣對齊 text1 的baseline
.background(Color.Magenta)
)
Text(
text = "Text 1",
fontSize = 40.sp,
modifier = Modifier
.alignByBaseline()
.background(color = Color.Red)
)
Text(
text = "Text 2",
modifier = Modifier.alignByBaseline().background(color = Color.Cyan)
)
}
}
```

`alignByBaseline` 就是對齊文字的基線, 可透過觀察 `text1` & `text2` 得知.
`alianBy` 必須在 `alignByBaseline` 出現時才會有效, 其效用是讓 ==該 UI 的垂直高度, 對齊另一個 UI 的 baseLine==.
> 在上述例子, `Box`使用`Modifier.alianBy{ 0 }`, 等於讓 **Box 上緣對齊text1 Baseline**
Box的 Modifier 若改為
```kotlin=
Box(
modifier = Modifier
.size(80.dp, 40.dp)
// .alignBy { 0 }
.alignBy { it.measuredHeight / 2 }
.background(Color.Magenta)
)
```
則Box Center 將會對齊 text1 baseline.

## Next
恩~,基本UI 介紹完了.
... :thinking_face:
那 `RelativeLayout` 或 `ConstraintLayout` 呢?
放心, 當然有相對應的 `ConstraintLayout` 版本, 他被獨立在另外一個package; 而`RelativeLayout` 本身就跟`ConstraintLayout` 功能重疊, 自然直接被取代了~.
<div style="color:#f04c00;">下回介紹: Compose UI - ConstrantLayout 篇</div>
## 延伸閱讀
:::warning
:book:
- [Exploring Jetpack Compose Column](https://joebirch.co/android/exploring-jetpack-compose-column/)
- [官方RowScope API Doc](https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/RowScope)
:::
## 環境配置
- Android Studio Arctic Fox | 2020.3.1 Canary 15
:::spoiler
> Android Studio Arctic Fox | 2020.3.1 Canary 15
Build #AI-203.7717.56.2031.7321754, built on April 29, 2021
Runtime version: 11.0.10+0-b96-7281165 x86_64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
macOS 10.15.7
GC: G1 Young Generation, G1 Old Generation
Memory: 2048M
Cores: 8
Registry: external.system.auto.import.disabled=true
Non-Bundled Plugins: org.jetbrains.kotlin
:::
- compose version:`1.0.0-beta07`
- kotlin-gradle-plugin:`1.4.32`
- com.android.tools.build:gradle:`7.0.0-beta03`