🤖 Compose

[Compose] ConstraintLayout, ConstraintSet

콩드로이드 2025. 1. 7. 14:47

library 추가 필요

[libs.version.toml]

[versions]
//...
constaraintLayoutVersion = "1.1.0"

[libraries]
//...
androidx-constraint-layout = { group = "androidx.constraintlayout", name = "constraintlayout-compose", version.ref = "constaraintLayoutVersion" }

[plugins]
//...

 

createRefs()

- constraintLayout 안에 있는 여러 UI 요소(예: 버튼, 텍스트 등)를 서로 연결하고 배치할 수 있는 참조를 생성

@Stable
fun createRefs(): ConstraintLayoutScope.ConstrainedLayoutReferences =
    referencesObject ?: ConstrainedLayoutReferences().also { referencesObject = it }

 

ConstrainedLayoutReferences를 보면 총 16개까지의 제약 조건을 설정가능,  16개 이상이 필요하면 여러번 호출하면 된다 

inner class ConstrainedLayoutReferences internal constructor() {
    operator fun component1(): ConstrainedLayoutReference = createRef()

    operator fun component2(): ConstrainedLayoutReference = createRef()

    operator fun component3(): ConstrainedLayoutReference = createRef()

    operator fun component4(): ConstrainedLayoutReference = createRef()

    operator fun component5(): ConstrainedLayoutReference = createRef()

    operator fun component6(): ConstrainedLayoutReference = createRef()

    operator fun component7(): ConstrainedLayoutReference = createRef()

    operator fun component8(): ConstrainedLayoutReference = createRef()

    operator fun component9(): ConstrainedLayoutReference = createRef()

    operator fun component10(): ConstrainedLayoutReference = createRef()

    operator fun component11(): ConstrainedLayoutReference = createRef()

    operator fun component12(): ConstrainedLayoutReference = createRef()

    operator fun component13(): ConstrainedLayoutReference = createRef()

    operator fun component14(): ConstrainedLayoutReference = createRef()

    operator fun component15(): ConstrainedLayoutReference = createRef()

    operator fun component16(): ConstrainedLayoutReference = createRef()
}

 


val (first, second, third) = createRefs()

Box(
    modifier = Modifier.size(40.dp)
        .background(Color.Red)
        .constrainAs(first) {//box가 어디에 위치해야할지 랜더링
            bottom.linkTo(parent.bottom)
        }
)

이 예시로 본다면 

Box는 first가 되며 box의 bottom은 parent의 bottom에 연결된다 

기존의 constraintBottomToBottom="parent"의 의미 

 

fun linkTo(
    anchor: ConstraintLayoutBaseScope.HorizontalAnchor,
    margin: Dp = 0.dp,
    goneMargin: Dp = 0.dp
)

linkTo는 위와 같이 margin, goneMargin 값도 설정 가능하다 

 

ConstraintLayout( modifier = Modifier.fillMaxSize()) {
     // 여러개의 reference return
    val (first, second, third) = createRefs()

    Box(
        modifier = Modifier.size(40.dp)
            .background(Color.Red)
            .constrainAs(first) {//box가 어디에 위치해야할지 랜더링
                top.linkTo(parent.top, margin = 8.dp)
            }
    )

    Text(
        text = "second Text",
        modifier = Modifier.constrainAs(second) {
            top.linkTo(first.bottom)
        }
    )
}

linkTo는 꼭 parent에 붙는게 아니다 (xml에서의 constraint를 생각할 것)

 

가운데 정렬은 기존의 constraint에서와 달리 

centerTo(ref), centerVerticallyTo(ref), centerHorizontallyTo(ref)로 사용 가능 

Text(
    text = "second Text",
    modifier = Modifier.constrainAs(second) {
        centerTo(parent)
    }
)

 

 

Chain

이렇게 3개의 ref를 선언한 후 

val (first, second, third) = createRefs()

Box(
    modifier = Modifier.size(40.dp)
        .background(Color.Red)
        .constrainAs(first) {

        }
)

Box(
    modifier = Modifier.size(40.dp)
        .background(Color.Cyan)
        .constrainAs(second) {

        }
)

Box(
    modifier = Modifier.size(40.dp)
        .background(Color.Black)
        .constrainAs(third) {

        }
)

 

createVerticalChain(first, second, third)

수직으로 3가지 ref의 체인을 생성하면 동일한 간격으로 수평 체인이 생성됩니다 

 

param으로 ChainStyle을 지정할 수 있는데 (Packed, Spread(default), SpreadInside) 

createVerticalChain(first, second, third, chainStyle = ChainStyle.Packed)

packed / spreadInside

Packed
- 뷰들이 서로 가까이 모여서 배치
- 체인 내의 뷰들은 중앙에 위치하며, 남는 공간은 양쪽에 균등하게 분배

Spread
- 뷰들이 체인 내에서 가능한 한 멀리 떨어져 배치
- 체인 내의 뷰들은 양쪽 끝에 위치하고(끝 사이에 공백있음), 남는 공간은 뷰들 사이에 균등하게 분배

SpreadInside
- 이 스타일은 Spread와 유사하지만, 체인의 양 끝에 있는 뷰는 끝에 고정되고, 나머지 뷰들 사이에만 간격이 균등하게 분배
- 즉, 체인 내의 첫 번째와 마지막 뷰는 끝에 위치하고, 중간 뷰들 사이에만 간격 생김

 

 

Barrier

- 여러 뷰의 경계를 기준으로 제약 조건 설정

- Barrier는 수평 또는 수직 방향으로 설정 가능

val (first, second, third, txt) = createRefs()

Box(
    modifier = Modifier.size(40.dp)
        .background(Color.Red)
        .constrainAs(first) {
            top.linkTo(parent.top)
        }
)

Box(
    modifier = Modifier.size(40.dp)
        .background(Color.Cyan)
        .constrainAs(second) {
            top.linkTo(parent.top)
        }
)

Box(
    modifier = Modifier.size(40.dp)
        .background(Color.Black)
        .constrainAs(third) {
            top.linkTo(parent.top, margin = 32.dp)
        }

)

createHorizontalChain(first, second, third, chainStyle = ChainStyle.SpreadInside)

val barrier = createBottomBarrier(first, second, third)

Text(" barrier 위치를 잘 보세요",
    modifier = Modifier.constrainAs(txt) {
        top.linkTo(barrier)
        end.linkTo(parent.end)
    }
)

 

이렇게 한다면 margin이 적용된 third의 bottom을 기준으로 barrier가 생긴다 


ConstraintSet

- 제약 조건을 modifier에서 선언하지 않고 ConstraintSet을 선언해 밖으로 빼내는 방법

    val constraintSet = ConstraintSet { //여기서 제약 설정
        val box = createRefFor("box")
        val txt = createRefFor("txt")

        constrain(box) {
            top.linkTo(parent.top, margin = 8.dp)
        }

        constrain(txt) {
            centerTo(parent)
        }
    }

    ConstraintLayout(modifier = Modifier.fillMaxSize(), constraintSet = constraintSet) {
        Box(
            modifier = Modifier.size(40.dp)
                .background(Color.Red)
                .layoutId("box")
//                .constrainAs(first) {
//                    top.linkTo(parent.top, margin = 8.dp)
//                }
        )

        Text(
            text = "second Text",
            modifier = Modifier
                .layoutId("txt")
//            modifier = Modifier.constrainAs(second) {
//                centerTo(parent)
//            }
        )
    }

 

1️⃣ ConstraintSet 선언 createRefFor(id)를 통해 식별자 생성

2️⃣ Constrain(원하는 ref) { 원하는 제약 조건 설정 }

3️⃣ ConstraintLayout에 param으로 ConstraintSet 선언 

4️⃣ ConstraintLayout에서 원하는 제약 조건이 필요한 곳에 Modifier의 layoutId에 ConstraintSet에서 설정한 id를 설정

 

레이아웃 구조가 복잡해지면 ConstraintSet으로 빼내는 게 더 좋을 거 같다

'🤖 Compose' 카테고리의 다른 글

[Compose] State, StateHoisting  (0) 2025.01.07
[Compose] Snackbar  (0) 2025.01.07
[Compose] Recomposition  (0) 2025.01.06
[Compose] scaffold  (0) 2025.01.06
[Compose] Slot API  (0) 2025.01.06