Android Jetpack Compose UI/Compose Layout

[Compose ConstraintLayout] ConstraintSet과 함께 사용하기 (Id 사용하기)

Id를 쓰지 않고 ConstraintLayout사용하기

앞선 글 [Android Compose Layout] 6. ConstraintLayout 정리 에서는 Id를 쓰지 않고 ConstraintLayoutScope 내에서 ConstrainedLayoutReference를 만들어서 사용했다.

@Preview(showBackground = true, widthDp = 200, heightDp = 200)
@Composable
fun KotlinWorldConstraintLayout() {
    ConstraintLayout(modifier = Modifier.fillMaxSize()) {
    
    /* ConstraintLayoutScope 시작 */
        val kotlinWorldText: ConstrainedLayoutReference = createRef() // 0. ConstrainedLayoutReference 만들기
        val enterButtonText: ConstrainedLayoutReference = createRef() // 0. ConstrainedLayoutReference 만들기

        Text(text = "KotlinWorld",
            modifier = Modifier.constrainAs(kotlinWorldText) { // 1. Text에 kotlinWorldText라는 ConstrainedLayoutReference를 지정한다.
                top.linkTo(parent.top) //  3. ConstraintLayout(parent)의 bottom에 kotlinWorldText의 top을 놓기
            })

        Text("Enter Button", modifier = Modifier.constrainAs(enterButtonText) { // 2. Text에 Enter Button이라는 ConstrainedLayoutReference를 지정한다.
            top.linkTo(kotlinWorldText.bottom)// 4.enterButton을 가지고 있는 Compose의 top을 KotlinWorldText의 Bottom에 위치시킨다.
        })
    /* ConstraintLayoutScope 종료 */
    
    }
}

이유는 ConstraintLayoutScope내에서 createRef()를 통해 ConstrainedLayoutReference를 만들 경우 Id가 Scope내에서 자동 관리되기 때문이다.

 

아래 코드를 보면 childId가 자동으로 ++되면서 ConstrainedLayoutReference를 만들고 있음을 볼 수 있다. 

class ConstraintLayoutScope @PublishedApi internal constructor() : ConstraintLayoutBaseScope() {
	..
    fun createRef() = childrenRefs.getOrNull(childId++) ?:
        ConstrainedLayoutReference(childId).also { childrenRefs.add(it) }
	..
}

 

 

 

 

Id를 직접 접근하여 ConstraintLayout 사용하기(ConstraintSet 사용)

하지만, xml에서는 ConstraintLayout을 Id(String 값)과 함께 사용했다. 이에 대한 migration 등을 하기 위해서 직접 접근해야 하는 경우가 있기 때문에 직접  Id에 접근하여 사용하는 방법을 알아보자. 

먼저 Constraint를 모아놓은 ConstraintSet을 만들어야 한다. ConstraintSet을 만뜰 때 ConstraintSetScope 내에서 createRefFor(id : Any)을 호출하면 Constraint를 받을 ConstrainedLayoutReference를 직접 id를 지정하여 생성 가능하다.

class ConstraintSetScope internal constructor() : ConstraintLayoutBaseScope() {
    ..
    fun createRefFor(id: Any) = ConstrainedLayoutReference(id)
    ..
}

 

자 이제 ConstraintSet을 만들어보자.

그림1. ConstraintSet 만들기

그림1의 코드를 보면 ConstraintSetScope 내에서 ConstrainedLayoutReference 를 아이디를 지정하여 두개를 만든다.

그 후 각 Id 별로 어떤 Constrain(제한사항)을 가질지를 지정한다. 위의 코드에서 kotlinText는 top을 parent의 top에, end를 parent의 end에 붙이도록 하는 Constrain(제한사항)을 가진다.

 

위에서 만든 Constrain을 모은 ConstraintSet을 ConstraintLayout에 넣어야 한다. 아래와 같이 코드를 작성하자.

@Preview(showBackground = true, widthDp = 200, heightDp = 200)
@Composable
fun KotlinWorldConstraintLayout() {
    val kotlinWorldConstraintSet = ConstraintSet {
        val kotlinText = createRefFor("kotlin") // Constraint를 받을 Id지정
        val blogText = createRefFor("blog") // Constraint를 받을 Id지정

        constrain(kotlinText) {
            top.linkTo(parent.top) // kotlin의 위를 parent의 위에 붙이기
            end.linkTo(parent.end) // kotlin의 오른쪽을 parent의 오른쪽에 붙이기
        }

        constrain(blogText) {
            top.linkTo(kotlinText.bottom) // blog의 위를 kotlin의 아래쪽에 붙이
            end.linkTo(parent.end) // blog의 오른쪽을 parent의 오른쪽에 붙이기
        }
    }

    ConstraintLayout(constraintSet = kotlinWorldConstraintSet, modifier = Modifier.fillMaxSize()) {
        Text(
            text = "Kotlin",
            modifier = Modifier.layoutId("kotlin") // 레이아웃 아이디 직접 설정
        )

        Text(
            text = "Blog",
            modifier = Modifier.layoutId("blog") // 레이아웃 아이디 직접 설정
        )
    }
}

ConstraintLayout의 constraintSet parameter로 우리가 만든 kotlinWorldConstraintSet을 넘겼다. 이후 각 Text에 Id를 지정하였다. 위 코드에 대한 결과는 다음과 같다. Kotlin Text가 parent의 오른쪽 위에, Blog는 위는 Kotlin의 아래쪽에 오른쪽은 parent의 오른쪽에 붙은 것을 볼 수 있다.

그림2. ConstraintSet을 이용한 ConstraintLayout 결과

 

반응형

 

이 글의 저작권은 '조세영의 Kotlin World' 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com