ColorBox 컴포저블 추가하기
@Composable
fun MainScreen() {
Box(modifier = Modifier.size(120.dp, 80.dp)) {
ColorBox(Modifier.background(Color.Blue))
}
}
@Composable
fun ColorBox(modifier: Modifier) {
Box(
Modifier
.padding(1.dp)
.size(width = 50.dp, height = 10.dp)
.then(modifier)
)
}
커스텀 레이아웃 모디파이어 만들기
- 앞의 예시에서 Box 레이아웃을 ColorBox 요소를 자신의 콘텐츠 영역의 왼쪽 위 모서리에 배치했다. 이는 정렬 파라미터 또는 다른 모디파이어를 전달하지 않았을 때 Box 레이아웃의 자식의 기본 위치다.
- 간단한 커스텀 레이아웃 모디파이어를 만든다. 이 모디파이어를 ColorBox에 적용하면 부모 Box 안에서 새로운 위치로 이동시킬 수 있다.
- 커스텀 레이아웃 모디파이어는 다음 표준 구문을 이용해 만들 수 있다.
fun Modifier.<커스텀 레이아웃 이름> (
//선택적 파라미터
) = layout { measurable, constraints ->
//요소의 위치와 크기를 조정할 코드
}
- layout의 후행 람다는 measurable, constraints 2개의 파라미터를 전달한다.
- measurable: 해당 모디파이어가 호출된 자식 요소가 배치될 정보이다.
- constraints: 자식이 이용할 수 있는 최대/최소 폭과 높이를 포함한다.
- 즉, 커스텀 레이아웃 모디파이어를 이용하여 부모에 할당된 기본 위치에 상대적으로 자식의 위치를 새로운 x, y 위치로 지정하고자 할 수 있다.
기본 위치
- 레이아웃 모디파이어는 0, 0을 기준으로 새로운 위치를 계산한 뒤 새로운 오프셋으로 반환한다. 부모는 이후 오프셋을 실제 좌표에 적용해 자식을 임의의 위치로 옮긴다.
- 예를 들어 부모가 자식의 기본 좌표를 50,70으로 계산했고 커스텀 레이아웃 모디파이어는 0,0을 기준으로 새로운 위치를 계산하고 오프셋(20,10)을 반환했다고 하자. 그러면 부모는 이 오프셋을 실제 위치에 적용해 자식의 위치를 커스텀 위치인 70,80으로 이동시킨다.
레이아웃 모디파이어 완성하기
fun Modifier.exampleLayout(
x: Int,
y: Int
) = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
placeable.placeRelative(x, y)
}
}
- 모디파이어를 이용해 자식을 배치할 때는 람다에 전달된 제약 조건의 준수 여부를 확인하기 위해 자식의 크기,위치를 알아야 한다. 이는 measurable 인스턴스의 measure() 메서드를 호출해 얻을 수 있다.
- 호출한 결과로 하나의 Placeable 인스턴스가 반환되며, 이 인스턴스는 높이와 폭 값을 갖는다.
- 커스텀 레이아웃을 개발할 때는 모디파이어가 호출될 때마다 자식을 측정하는 규칙이 적용된다.
- layout() 메서드를 호출할 때 placeable 값으로부터 높이와 폭을 전달한다. 또한 자식의 위치를 지정하는 layout() 메서드에 후행 람다를 전달해야 한다.
- 후행 람다 안에서 Placeable 객체의 placeRelative() 메서드 호출을 통해 자식 요소의 위치가 지정된다. 이때 모디파이어에 전달된 새로운 x,y 좌표를 이용한다.
커스텀 모디파이어 적용
@Composable
fun MainScreen() {
Box(modifier = Modifier.size(120.dp, 80.dp)) {
ColorBox(
Modifier
.exampleLayout(90, 50)
.background(Color.Blue)
)
}
}

정렬 선 다루기
- 위의 예시에서는 자식 컴포저블의 위치를 조정할 때 ColorBox의 왼쪽 위 모서리를 특정한 x, y 좌표로 이동시켰다.
- 자식 요소의 높이와 폭 측정값에 접근할 수 있다면 모든 수평 또는 수직 정렬 선에 기반해 위치를 새로 설정할 수 있다.
- 다음은 가상 정렬 선에 따라 자식의 위치를 지정하는 예시이다.
fun Modifier.exampleLayout(
fraction: Float
) = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
val x = -(placeable.width * fraction).roundToInt()
layout(placeable.width, placeable.height) {
placeable.placeRelative(x = x, y = 0)
}
}
- 모디파이어는 더 이상 x, y 좌표를 전달하지 않지만, 새로운 위치는 부모가 정의한 기본 좌표에 따라 상대적으로 계산된다.
- 모디파이어는 좌표 대신 부동소수점을 전달받고, 이 파라미터는 세로 정렬선의 위치를 자식의 폭 비율로 나타낸다.
@Composable
fun MainScreen() {
Box(contentAlignment = Alignment.Center,
modifier = Modifier.size(120.dp, 80.dp)) {
Column {
ColorBox(
Modifier
.exampleLayout(0.0f)
.background(Color.Blue)
)
ColorBox(
Modifier
.exampleLayout(0.25f)
.background(Color.Green)
)
ColorBox(
Modifier
.exampleLayout(0.5f)
.background(Color.Yellow)
)
ColorBox(
Modifier
.exampleLayout(0.25f)
.background(Color.Red)
)
ColorBox(
Modifier
.exampleLayout(0.0f)
.background(Color.Magenta)
)
}
}
}
@Composable
fun ColorBox(modifier: Modifier) {
Box(
Modifier
.padding(1.dp)
.size(width = 50.dp, height = 10.dp)
.then(modifier)
)
}
fun Modifier.exampleLayout(
fraction: Float
) = layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
val x = -(placeable.width * fraction).roundToInt()
layout(placeable.width, placeable.height) {
placeable.placeRelative(x = x, y = 0)
}
}
