Compose의 ConstraintLayout

ConstraintLayout은 화면에 다른 컴포저블을 기준으로 컴포저블을 배치할 수 있는 레이아웃입니다. 여러 중첩된 Row, Column, Box, 맞춤 레이아웃 요소 대신 사용할 수 있습니다. ConstraintLayout은 더 복잡한 정렬 요구사항이 있는 더 큰 레이아웃을 구현할 때 유용합니다.

다음 시나리오에서는 ConstraintLayout을 사용하는 것이 좋습니다.

  • 코드 가독성 개선을 위해 화면에 요소를 배치하는 여러 ColumnRow를 중첩하지 않습니다.
  • 다른 컴포저블을 기준으로 컴포저블을 배치하거나 가이드라인, 배리어, 체인을 기반으로 컴포저블을 배치합니다.

뷰 시스템에서 ConstraintLayout은 크고 복잡한 레이아웃을 만드는 데 권장되는 방법이었습니다. 플랫 뷰 계층 구조가 중첩된 뷰보다 성능 면에서 더 좋았기 때문입니다. 그러나 이는 깊은 레이아웃 계층 구조를 효율적으로 처리할 수 있는 Compose에서는 문제가 되지 않습니다.

ConstraintLayout 시작하기

Compose에서 ConstraintLayout을 사용하려면 build.gradle에 이 종속 항목을 추가해야 합니다(Compose 설정에도).

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"

Compose의 ConstraintLayoutDSL을 사용하여 다음과 같은 방식으로 작동합니다.

  • createRefs() 또는 createRefFor()를 사용하여 ConstraintLayout에서 각 컴포저블의 참조를 만듭니다.
  • 제약 조건은 constrainAs() 수정자를 사용하여 제공됩니다. 이 수정자는 참조를 매개변수로 사용하고 본문 람다에 제약 조건을 지정할 수 있게 합니다.
  • 제약 조건은 linkTo() 또는 다른 유용한 메서드를 사용하여 지정됩니다.
  • parentConstraintLayout 컴포저블 자체에 대한 제약 조건을 지정하는 데 사용할 수 있는 기존 참조입니다.

다음은 ConstraintLayout을 사용하는 컴포저블의 예입니다.

@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout {
        // Create references for the composables to constrain
        val (button, text) = createRefs()

        Button(
            onClick = { /* Do something */ },
            // Assign reference "button" to the Button composable
            // and constrain it to the top of the ConstraintLayout
            modifier = Modifier.constrainAs(button) {
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text("Button")
        }

        // Assign reference "text" to the Text composable
        // and constrain it to the bottom of the Button composable
        Text(
            "Text",
            Modifier.constrainAs(text) {
                top.linkTo(button.bottom, margin = 16.dp)
            }
        )
    }
}

이 코드는 Button의 상단을 여백이 16.dp인 상위 요소로 제한하고 Text를 여백이 16.dpButton의 하단으로 제한합니다.

ConstraintLayout에 정렬된 버튼 및 텍스트 요소 예시

분리된 API

ConstraintLayout 예에서 제약 조건은 적용되는 컴포저블의 수정자와 함께 인라인으로 지정됩니다. 그러나 제약 조건이 적용되는 레이아웃에서 제약 조건을 분리하는 것이 더 좋은 상황이 있습니다. 예를 들어 화면 구성을 기반으로 제약 조건을 변경하거나 두 제약 조건 세트 사이에 애니메이션을 적용할 수 있습니다.

이 같은 경우에는 ConstraintLayout을 서로 다른 방식으로 사용할 수 있습니다.

  1. ConstraintSet을 매개변수로 ConstraintLayout에 전달합니다.
  2. layoutId 수정자를 사용하여 ConstraintSet에 생성된 참조를 컴포저블에 할당합니다.

@Composable
fun DecoupledConstraintLayout() {
    BoxWithConstraints {
        val constraints = if (minWidth < 600.dp) {
            decoupledConstraints(margin = 16.dp) // Portrait constraints
        } else {
            decoupledConstraints(margin = 32.dp) // Landscape constraints
        }

        ConstraintLayout(constraints) {
            Button(
                onClick = { /* Do something */ },
                modifier = Modifier.layoutId("button")
            ) {
                Text("Button")
            }

            Text("Text", Modifier.layoutId("text"))
        }
    }
}

private fun decoupledConstraints(margin: Dp): ConstraintSet {
    return ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        constrain(button) {
            top.linkTo(parent.top, margin = margin)
        }
        constrain(text) {
            top.linkTo(button.bottom, margin)
        }
    }
}

그러면 제약 조건을 변경해야 할 때 다른 ConstraintSet을 전달하기만 하면 됩니다.

ConstraintLayout 개념

ConstraintLayout에는 컴포저블 내에서 요소를 배치하는 데 도움이 될 수 있는 가이드라인, 배리어, 체인과 같은 개념이 포함되어 있습니다.

가이드라인

가이드라인은 레이아웃을 디자인할 때 사용할 수 있는 작은 시각적 도우미입니다. 컴포저블은 가이드라인으로 제한할 수 있습니다. 가이드라인은 상위 컴포저블 내에서 특정 dp 또는 percentage에 요소를 배치하는 데 유용합니다.

가이드라인에는 세로, 가로 두 종류가 있습니다. 가로 가이드라인 두 개는 topbottom이고 세로 두 개는 startend입니다.

ConstraintLayout {
    // Create guideline from the start of the parent at 10% the width of the Composable
    val startGuideline = createGuidelineFromStart(0.1f)
    // Create guideline from the end of the parent at 10% the width of the Composable
    val endGuideline = createGuidelineFromEnd(0.1f)
    //  Create guideline from 16 dp from the top of the parent
    val topGuideline = createGuidelineFromTop(16.dp)
    //  Create guideline from 16 dp from the bottom of the parent
    val bottomGuideline = createGuidelineFromBottom(16.dp)
}

가이드라인을 만들려면 필요한 가이드라인 유형과 함께 createGuidelineFrom*을 사용합니다. 이렇게 하면 Modifier.constrainAs() 블록에서 사용할 수 있는 참조가 만들어집니다.

배리어

배리어는 여러 컴포저블을 참조하여, 지정된 쪽에서 가장 극단적인 위젯을 기반으로 가상 가이드라인을 만듭니다.

배리어를 만들려면 createTopBarrier()(또는 createBottomBarrier(), createEndBarrier(), createStartBarrier())를 사용하고, 배리어를 구성해야 하는 참조를 제공합니다.

ConstraintLayout {
    val constraintSet = ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        val topBarrier = createTopBarrier(button, text)
    }
}

그러면 배리어를 Modifier.constrainAs() 블록에서 사용할 수 있습니다.

체인

체인은 단일 축(가로 또는 세로)에서 그룹과 같은 동작을 제공합니다. 다른 축은 독립적으로 제한할 수 있습니다.

체인을 만들려면 createVerticalChain 또는 createHorizontalChain을 사용합니다.

ConstraintLayout {
    val constraintSet = ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        val verticalChain = createVerticalChain(button, text, chainStyle = ChainStyle.Spread)
        val horizontalChain = createHorizontalChain(button, text)
    }
}

그러면 Modifier.constrainAs() 블록에서 체인을 사용할 수 있습니다.

체인은 여러 ChainStyles로 구성하여 컴포저블을 둘러싼 공간을 처리하는 방법을 결정합니다. 예를 들면 다음과 같습니다.

  • ChainStyle.Spread: 공간이 첫 번째 컴포저블 앞과 마지막 컴포저블 뒤의 여유 공간을 포함하여 모든 컴포저블에서 균등하게 분산됩니다.
  • ChainStyle.SpreadInside: 공간이 첫 번째 컴포저블 앞이나 마지막 컴포저블 뒤의 여유 공간 없이 모든 컴포저블에서 균등하게 분산됩니다.
  • ChainStyle.Packed: 공간이 첫 번째 컴포저블 앞과 마지막 컴포저블 뒤에 분산되고 컴포저블은 서로 간에 공간 없이 패킹됩니다.

자세히 알아보기

ConstraintLayout을 사용하는 Compose 샘플의 작동 중인 API에서 Compose의 ConstraintLayout에 관해 자세히 알아보세요.

현재 추천 자료가 없습니다.

Google 계정에 해 보세요.