---
# System prepended metadata

title: '[Compose 系列 - 3] Compose UI - ConstraintLayout'
tags: [constraintLayout, compose, android]

---

###### tags: `android`, `compose`, `constraintLayout`
###### title: [Compose 系列 - 3] Compose UI - ConstraintLayout

# [Compose 系列 - 3] Compose UI - ConstraintLayout

[TOC]

## 前言
前面介紹了基本的 Layout:
- [[Compose 系列 - 1] UI - Layout Box & Modifier](/s23tCoUbQ_CN7c5_yNPMHQ)
- [[Compose 系列 - 2] UI - Layout Column/Row](/v4x18WsIRfCqCMSFkfQPLQ)

前面的Layout, 僅能提供間單的佈局位置. 而ConstraintLayout 提供 相對位置 / 百分比例 的方法進行佈局, 從而讓佈局更容易進行.

---

## Gradle
```kotlin=
val constraint_ver = "1.0.0-alpha07"
val compose_ver = "1.0.0-beta07"

dependencies {
    ...
    implementation("androidx.constraintlayout:constraintlayout-compose:${constraint_ver}")

}
```

:::warning
:ghost: 
由於還在 `alpha/beta` 階段
需注意其他相關的 `compose` 版本是否跑起來沒問題
自行斟酌版本號的使用
:::

## Usage


用法跟之前的Constraint 差不多:
> 1. 給定元件id
> 2. 設定上下左右, ...等關係

Ex:
假設我希望有一個`button`, 下方要有一個`text`. 除了使用`Column` 也可以用以下的方式
```kotlin=
@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout(modifier = Modifier.background(Color.Gray)) {
        // 創造元件id
        val (buttonId, textId) = createRefs()

        Button(
            onClick = { /* Do something */ },
            // 透過 ConstraintLayoutScope 底下的 constrainAs(id)
            // 指定該component id: buttonId
            modifier = Modifier.constrainAs(buttonId) {
                // 將 btn 上緣指定至 parent 上緣
                // 並保持16.dp 距離
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text("Button")
        }

        // 一樣將 textId 塞進 Text 元件
        Text("Text", Modifier.background(Color.White).constrainAs(textId) {
            // 將 text 上緣 指定至 btn 下緣, 並保持 16.dp 距離
            top.linkTo(buttonId.bottom, margin = 16.dp)
//             將text 左右, 對齊 btn 左右
            start.linkTo(buttonId.start)
            end.linkTo(buttonId.end)
        })
    }
}
```
![](https://i.imgur.com/J1u94G3.png)


又或者透過ConstraintSet, 先進行關係的聯繫, 在賦予對應component id:
```kotlin=
@Composable
fun ConstraintWithConstraintSet() {
    ConstraintLayout(
        modifier = Modifier.background(Color.Gray),
        constraintSet = ConstraintSet {
//         建立關係
            val buttonId = createRefFor("btnId")
            val textId = createRefFor("textId")

            constrain(buttonId) {
                top.linkTo(parent.top, 16.dp)
            }
            
            constrain(textId) {
                top.linkTo(buttonId.bottom, margin = 16.dp)
                start.linkTo(buttonId.start)
                end.linkTo(buttonId.end)
            }
        }
    ) {
//     賦予 compontent id
        Button(
            onClick = { /* Do something */ },
            modifier = Modifier.layoutId("btnId")
        ) {
            Text(text = "Button")
        }

        Text("Text",
            Modifier
                .background(Color.White)
                .layoutId("textId"))

    }
}
```
畫面是長一樣的~ 

---

### GuideLine
使用者可以創建一條輔助線, 僅提供幫助進行建構畫面, 實際上在繪畫階段時並不會進行繪製.

`ConstraintLayoutBaseScope` 底下, 有`createGuidelineFromXXX()` 可以創建

Ex: 
```kotlin=
@Composable
fun createGuide() {
    ConstraintLayout {
        val textId = createRefs()
//     創建 水平guideLine, 距離parent上緣20dp
        val absTop = createGuidelineFromTop(20.dp)
//     創建 水平guideLine, 距離parent上緣20dp
        val perTop = createGuidelineFromTop(0.4f)
        
        Text("Text", Modifier.constrainAs(textId) {
            top.linkTo(absTop)
        }
    }
}

```

---

### Barrier
類似於GuideLine 的效果, 差別在於他是可隨著條件進行變動的 GuideLine.

:::warning
:book: 詳細介紹可參考
[ConstraintLayout-Barrier 介紹](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/1017/8601.html)
:::
用法同上, `createXXXBarrier()`

---
### Chains

直接上code:
```kotlin=
@Composable
fun ConstraintChain() {
    ConstraintLayout(
        modifier = Modifier.background(Color.Gray),
        constraintSet = ConstraintSet {
            val text1Id = createRefFor("text1")
            val text2Id = createRefFor("text2")
            val text3Id = createRefFor("text3")
            
            createHorizontalChain(
                text1Id,
                text2Id,
                text3Id,
                chainStyle = ChainStyle.Spread
                // ChainStyle 有三種預設模式: 
                // Spread: 平均分配空間, 左右留空白
                // SpreadInside: 平均分配空間, 左右不留空白
                // Packed: 元件互相緊連, 並 水平置中
                // 而 Packed 可以放置bias 參數, 控制水平位移
            ) 
        }
    ) {
        Text("test1", Modifier.layoutId("text1"))
        Text("test2", Modifier.layoutId("text2"))
        Text("test3", Modifier.layoutId("text3"))
    }
}

```
![](https://i.imgur.com/tve43gZ.png)

## 小記
基本的佈局皆以介紹~ 
但我想大家應該會很好奇, `RecyclerLayout` / `ViewPager` / ... 其他常用的 Layout, 在compose 裡要怎麼用呢？


<div style="color:#f04c00;">因此, 下回介紹: compose 版本對應RecyclerView 的使用方法</div>


## 延伸閱讀
:::warning
:book: 
- [Exploring Constraint Layout in JetPack Compose](https://medium.com/android-dev-hacks/exploring-constraint-layout-in-jetpack-compose-67b82123c28b)
- [ConstraintLayout-Barrier 介紹](http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/1017/8601.html)
:::

## 環境配置
- 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`