티스토리 뷰

안드로이드/코드

Compose ConstraintLayout

알렌보이스 2022. 11. 2. 20:15
728x90

기존 xml 방식에서도 많이 사용되었던 ConstraintLayout 사용 방법입니다.

1) 라이브러리 설치

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

 

 

Constraintlayout  |  Android 개발자  |  Android Developers

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Constraintlayout 상대 위치에 따라 유연한 방식으로 위젯의 위치와 크기를 지정합니다. 이 표는 androidx.constraintlay

developer.android.com

최신 버전은 위의 링크를 확인해 주세요

2) 기본 사용 방법

ConstraintLayout(
    modifier = Modifier.fillMaxSize()
) {
    val button1 = createRef()
    val (button2, text1) = createRefs()

    Button(
        onClick = {},
        modifier = Modifier.constrainAs(button1) {

        }
    ) {
        Text(text = "button1")
    }

    Button(
        onClick = {},
        modifier = Modifier.constrainAs(button2) {

        }
    ) {
        Text(text = "button2")
    }

    Text(
        text = "text1",
        modifier = Modifier.constrainAs(text1) {

        }
    )
}

xml과 가장 큰 차이점은 3~4 라인의 코드입니다.

xml에서 사용했던 id들을 변수에다가 미리 정의를 하는 코드입니다.

한 개만 등록할 시에는 createRef()를, 여러 개 동시에 등록할 때에는 소괄호 안에 넣고 createRefs()를 해주면 됩니다.

단 변수 생성 시 ConstraintLayout 안에서 생성해 주어야 합니다.

제약조건을 넣을 때에는 Modier.constraintAs() 안에 위에서 만든 id를 넣어주면 됩니다.

Button(
    onClick = {},
    modifier = Modifier.constrainAs(button1) {
        top.linkTo(parent.top)
        start.linkTo(parent.start)
        end.linkTo(parent.end)
    }
) {
    Text(text = "button1")
}

Button(
    onClick = {},
    modifier = Modifier.constrainAs(button2) {
        top.linkTo(button1.bottom, 10.dp)
        start.linkTo(parent.start, 20.dp)
    }
) {
    Text(text = "button2")
}

Text(
    text = "text1",
    modifier = Modifier.constrainAs(text1) {

    }
)

제약조건을 추가할 때에는 다음의 조건에 따라 넣어주면 됩니다.

[해당 Composable의 지정하고 싶은 위치 ex) top].linkTo([제약 조건], [Margin], [GoneMargin])

ConstrintLayout을 사용해 본 경험이 있으시면 쉽게 파악이 가능할 겁니다.

Screenshot_20221030_160605.png

3) 크기 지정

val line = createGuidelineFromStart(fraction = 0.5f)
Box(modifier = Modifier
    .background(Color(0xFF1DE9B6))
    .constrainAs(box) {
        start.linkTo(line)
        end.linkTo(parent.end)
        top.linkTo(parent.top)
        bottom.linkTo(parent.bottom)
        height = Dimension.fillToConstraints
        width = Dimension.fillToConstraints
    }
)

Text(
    text = "text1 Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
    color = Color.Red,
    modifier = Modifier
        .background(Color.Black)
        .constrainAs(text1) {
            top.linkTo(parent.top)
            start.linkTo(line)
            end.linkTo(parent.end)
            width = Dimension.preferredWrapContent
        }
)

Text(
    text = "text2 Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
    color = Color.Blue,
    modifier = Modifier
        .background(Color.Black)
        .constrainAs(text2) {
            top.linkTo(text1.bottom, 10.dp)
            start.linkTo(line)
            end.linkTo(parent.end)
            width = Dimension.wrapContent
        }
)

Text(
    text = "text3 Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
    color = Color.Green,
    modifier = Modifier
        .background(Color.Black)
        .constrainAs(text3) {
            top.linkTo(text2.bottom, 10.dp)
            start.linkTo(line)
            end.linkTo(parent.end)
            width = Dimension.fillToConstraints
        }
)

Text(
    text = "text4 Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
    color = Color.White,
    modifier = Modifier
        .background(Color.Black)
        .constrainAs(text4) {
            top.linkTo(text3.bottom, 10.dp)
            start.linkTo(line)
            end.linkTo(parent.end)
            width = Dimension.preferredValue(100.dp)
        }
)

Text(
    text = "text5 Lorem Ipsum is simply dummy text of the printing and typesetting industry.",
    color = Color.Yellow,
    modifier = Modifier
        .background(Color.Black)
        .constrainAs(text5) {
            top.linkTo(text4.bottom, 10.dp)
            start.linkTo(line)
            end.linkTo(parent.end)
            width = Dimension.value(100.dp)
        }
)

우선 가이드라인입니다.

createGuidelineFrom***()로 생성이 가능하며 실제로 앱 화면에 보이지는 않기 때문에 디자인을 편리하게 하기 위해 사용할 수 있습니다.

Box는 눈으로 색상을 줘서 크기가 어느 정도인지 쉽게 보기 위한 것입니다.

ConstraintLayout을 사용하고 값을 아무것도 주지 않으면 with나 height가 Composable의 크기에 맞게만 설정이 됩니다.

그러나 상황에 따라 크기를 지정해 줄 필요가 있는데 그럴 경우 Dimension을 이용하면 됩니다.

Screenshot_20221030_170420.png

순서대로 보면

Dimension.preferredWrapContent는 제약조건의 크기를 최대값으로 갖고 그 안에서 wrapContent를 해주는 방식입니다.

만약 텍슽트가 짧으면 텍스트의 길이 만큼만 영역을 차지합니다.

Dimension.wrapContent는 Composable의 자신의 크기에 맞게 설정을 합니다.

위의 예시에서는 텍스트가 디바이스의 넓이만큼 길이를 잡고 다음 줄로 넘어간 것을 확인할 수 있습니다.

그리고 가이드라인과 화면의 끝에 제약을 넣었으므로 가운데 위치하려고 하다 보니 영역을 벗어나게 표시가 됩니다.

Dimension.fillToConstraints는 제약조건에서 준 영역만큼 자신의 영역을 가지게 설정합니다. 기존 xml에서 0dp 준 것과 동일하다고 보시면 됩니다.

Dimension.preferredValue(100.*dp*)Dimension.value(100.*dp*)는 제약조건과는 무관하게 크기를 지정할 수 있습니다. 대신 preferredValue는 제약조건의 크기를 최대 크기로 가지게 됩니다.

글로 써서 표현이 애매하긴한데 사용해보시면 쉽게 알 수 있습니다.

4) Chain

ConstraintLayout {
    val (text1, text2, text3) = createRefs()
    createHorizontalChain(text1, text2, text3, chainStyle = ChainStyle.Spread)

    Text(
        text = "text1",
        modifier = Modifier.constrainAs(text1) {
            top.linkTo(parent.top, 20.dp)
        }
    )

    Text(
        text = "text2",
        modifier = Modifier.constrainAs(text2) {
            top.linkTo(text1.top)
        }
    )

    Text(
        text = "text3",
        modifier = Modifier.constrainAs(text3) {
            top.linkTo(text1.top)
        }
    )
}

createHorizontalChain / createVerticalChin을 통해 Chain을 설정할 수 있습니다.

스타일의 종류는 xml과 동일합니다.

Group 478664.png

5) ConstraintSet

제약 조건을 Composable 안에서 직접 지정할 수도 있지만 따로 함수로 빼서 작업을 진행할 수도 있습니다.

@Composable
fun ConstraintTest2() {
    ConstraintLayout(decoupledConstraints()) {
        Box(
            modifier = Modifier
                .layoutId("box1")
                .background(Color(0xFFFF8A80))
        )
        Box(
            modifier = Modifier
                .layoutId("box2")
                .background(Color(0xFF80D8FF))
        )
    }
}

private fun decoupledConstraints(): ConstraintSet =
    ConstraintSet {
        val box1 = createRefFor("box1")
        val box2 = createRefFor("box2")

        constrain(box1) {
            top.linkTo(parent.top)
            bottom.linkTo(box2.top)
            start.linkTo(parent.start)
            end.linkTo(parent.end)

            width = Dimension.fillToConstraints
            height = Dimension.fillToConstraints
        }

        constrain(box2) {
            top.linkTo(box1.bottom)
            bottom.linkTo(parent.bottom)
            start.linkTo(parent.start)
            end.linkTo(parent.end)

            width = Dimension.fillToConstraints
            height = Dimension.fillToConstraints
        }
    }

ConstraintSet에서는 createRefFor를 통해 변수를 생성 할 수 있습니다.

여러 개 동시에 만드는 것은 지원하지 않고 있습니다.

변수 명은 ContraintSet에서 사용하기 위함이고 createRefFor는 ContraintLayout에서 사용할 id입니다.

그 이외에는 동일하게 제약조건을 넣어주면 됩니다.

ConstrintLayout에서는 생성 시 만들어둔 ConstraintSet을 넣어주면 되고,

각 Composable의 Modifier에서 LayoutId를 ConstraintSet에서 설정한 id와 동일하게 넣어주면 됩니다.

필수로 사용해야하는 요소는 아니므로 본인의 코딩 취향이나 상황에 따라 사용하면 될 것 같습니다.

728x90

'안드로이드 > 코드' 카테고리의 다른 글

Android Compose MotionLayout 2  (0) 2022.11.03
Compose MotionLayout  (0) 2022.11.02
Compose Naver Map  (1) 2022.10.29
Compose GoogleMap  (0) 2022.10.27
Compose LaunchedEffect  (0) 2022.10.27
댓글