컴포즈가 제공하는 레이아웃 컴포저블을 이용한다면 사용자 인터페이스를 구조화하고, 화면 방향이나 크기 변경과 같은 요소들에 대한 레이아웃의 반응 방법을 정의할 수 있습니다.
@Composable
fun TextCell(text: String, modifier: Modifier = Modifier) {
val cellModifier = Modifier
.size(75.dp)
.padding(4.dp)
.border(width = 4.dp, color = Color.Black)
Text(
text = text,
modifier = cellModifier.then(modifier),
fontSize = 50.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center
)
}
정사각형에 border을 가진 TextCell 컴포저블을 작성합니다.
Row 컴포저블을 사용하면 자식 컴포넌트를 화면의 수평 방향으로 배열합니다.
(LinearLayout의 orientation=”horizontal”과 동일)
@Composable
fun MainScreen() {
Row {
TextCell(text = "1")
TextCell(text = "2")
TextCell(text = "3")
}
}
Column 컴포저블을 이용하면 자식 컴포넌트를 화면의 수직 방향으로 배열합니다.
@Composable
fun MainScreen() {
Column {
TextCell(text = "1")
TextCell(text = "2")
TextCell(text = "3")
}
}
@Composable
fun MainScreen() {
Column {
Row {
TextCell(text = "1")
TextCell(text = "2")
TextCell(text = "3")
}
Row {
TextCell(text = "4")
TextCell(text = "5")
}
Row {
TextCell(text = "6")
TextCell(text = "7")
TextCell(text = "8")
}
}
}
Row, Column 컴포저블 모두 사용자 인터페이스 레이아웃 안의 공간을 차지합니다. 차지하는 공간은 자식 요소, 다른 컴포저블, 크기 관련 설정을 적용하는 모디파이어에 따라 달라집니다.
기본적으로 Row와 Column 내부의 자식 요소 그룹들은 콘텐츠 영역의 가장 왼쪽 위의 모서리 기준으로 정렬됩니다.
fun MainScreen() {
Row(
modifier = Modifier.size(width = 300.dp, height = 150.dp)
) {
TextCell(text = "1")
TextCell(text = "2")
TextCell(text = "3")
}
}
아까와 다르게 Row가 자식들을 감싸고 있지 않습니다. Row가 콘텐츠보다 크기 때문에 기본 정렬에 따라 자식들이 Row 컴포넌트의 왼쪽 위 모서리에 위치하게 됩니다.
Row는 수평 방향으로 배열되기 때문에, 반대 방향인 수직 방향 정렬을 위해 verticalAlignment 파라미터를 사용합니다. verticalAlignment 파라미터에 값을 전달하여 수직 방향의 기본 정렬을 변경할 수 있습니다.
Alignment.Top: 콘텐츠를 Row 콘텐츠 영역의 수직 방향 위 위치에 정렬Alignment.Bottom: 콘텐츠를 Row 콘텐츠 영역의 수직 방향 아래 위치에 정렬Alignment.CenterVertically: 콘텐츠를 Row 콘텐츠 영역의 수직 방향의 중앙 위치에 정렬
verticalAlignment = Alignment.Bottom 인 경우
Column은 수직 방향으로 배열되기 때문에, 반대 방향인 수평 방향 정렬을 위해 horizontalAlignment 파라미터를 사용합니다. horizontalAlignment 파라미터에 값을 전달하여 수평 방향의 기본 정렬을 변경할 수 있습니다.
Alignment.Start: 콘텐츠를 Column 콘텐츠 영역의 수평 방향 시작 위치에 정렬Alignment.End: 콘텐츠를 Column 콘텐츠 영역의 수평 방향 끝 위치에 정렬Alignment.CenterHorizontally: 콘텐츠를 Column 콘텐츠 영역의 수평 방향 가운데 위치에 정렬
horizontalAlignment = Alignment.CenterHorizontally 인 경우
배열(Arrangement)은 정렬(Alignment)과 다르게 자식의 위치를 컨테이너와 동일 축을 따라 제어합니다.
배열 프로퍼티는 위치와 자식 사이의 간격에 영향을 미칩니다.
horizontalArrangement 파라미터에 값을 전달하여 수평 방향의 배열을 변경할 수 있습니다.
Arrangement.Start: 콘텐츠를 Row 콘텐츠 영역의 수평 방향 시작 위치에 정렬Arrangement.End: 콘텐츠를 Row 콘텐츠 영역의 수평 방향 끝 위치에 정렬Arrangement.Center: 콘텐츠를 Row 콘텐츠 영역의 수평 방향 가운데 위치에 정렬Arrangement.SpaceEvenly: 자식들은 균일한 간격을 유지합니다. 첫 번째 자식의 앞, 마지막 자식의 뒤 공간도 균일하게 포함됩니다.Arrangement.SpaceBetween: 자식들은 균일한 간격을 유지합니다. 첫 번째 자식의 앞, 마지막 자식의 공간은 포함되지 않습니다.Arrangement.SpaceAround: 자식들은 균일한 간격을 유지합니다. 첫 번째 자식의 앞, 마지막 자식의 뒤 공간은 자식들 사이 공간의 절반입니다.
verticalArrangement 파라미터에 값을 전달하여 수직 방향의 배열을 변경할 수 있습니다.
Arrangement.Top: 콘텐츠를 Column 콘텐츠 영역의 수직 방향 위 위치에 정렬Arrangement.Bottom: 콘텐츠를 Column 콘텐츠 영역의 수직 방향 아래 위치에 정렬Arrangement.Center: 콘텐츠를 Column 콘텐츠 영역의 수직 방향 가운데 위치에 정렬Arrangement.SpaceEvenly: 자식들은 균일한 간격을 유지합니다. 첫 번째 자식의 앞, 마지막 자식의 뒤 공간도 균일하게 포함됩니다.Arrangement.SpaceBetween: 자식들은 균일한 간격을 유지합니다. 첫 번째 자식의 앞, 마지막 자식의 공간은 포함되지 않습니다.Arrangement.SpaceAround: 자식들은 균일한 간격을 유지합니다. 첫 번째 자식의 앞, 마지막 자식의 뒤 공간은 자식들 사이 공간의 절반입니다.
Row 또는 Column의 자식들은 부모의 스코프(Scope) 안에 있다고 말합니다. 두 스코프 모두 추가 모디파이어 함수를 제공해서 각 자식의 동작이나 형태를 변경할 수 있습니다.
Modifier.align(): Alignment.Top, Alignment.Bottom, Alignment.CenterVertically 값을 이용해서 자식들을 수직으로 정렬Modifier.alignBy(): 자식들을 alignBy() 모디파이어가 적용된 다른 형제들과 정렬, 정렬은 베이스라인 또는 커스텀 정렬 라인 설정에 따라 수행Modifier.alignByBaseline(): 자식의 베이스라인을 alignBy() 또는 alignByBaseline() 모디파이어가 이미 적용된 형제들과 정렬Modifier.paddingFrom(): 자식의 정렬 라인에 패딩을 추가Modifier.weight(): 형제에 할당된 가중치에 따라 자식의 폭을 설정@Composable
fun MainScreen() {
Row(
modifier = Modifier.size(225.dp),
) {
TextCell(text = "1", Modifier.align(Alignment.Top))
TextCell(text = "2", Modifier.align(Alignment.CenterVertically))
TextCell(text = "3", Modifier.align(Alignment.Bottom))
}
}
베이스라인 정렬 옵션은 글꼴 크기가 다른 텍스트 콘텐츠를 정렬할 때 유용합니다.
@Composable
fun MainScreen() {
Row {
Text(
text = "Large Text",
//modifier = Modifier.alignByBaseline(),
fontSize = 24.sp,
)
Text(
text = "Small Text",
//modifier = Modifier.alignByBaseline(),
fontSize = 12.sp,
)
}
}
alignByBaseline() 모디파이어를 적용하기 전에는 위쪽 가장자리를 따라 정렬되었기 때문에 텍스트 베이스라인에 벗어났습니다. 하지만, 적용 후에는 텍스트 베이스라인에 맞게 정렬된 것을 볼 수 있습니다.
alignByBaseline() 모디파이어를 alignBy() 모디파이어로 바꿀 수 있습니다. 이 때는 FirstBaseline을 전달합니다.
@Composable
fun MainScreen() {
Row {
Text(
text = "Large Text",
modifier = Modifier.alignBy(FirstBaseline),
fontSize = 24.sp,
)
Text(
text = "Small Text",
modifier = Modifier.alignByBaseline(),
fontSize = 12.sp,
)
}
}
여러 줄의 텍스트에서 텍스트 정렬을 맞추어야 할 때도 있습니다. 형제 컴포넌트의 첫 번째 또는 마지막 텍스트의 베이스라인에 맞춰서 정렬할 수 있습니다.
@Composable
fun MainScreen() {
Row {
Text(
text = "First Line\nSecondLine",
modifier = Modifier.alignBy(FirstBaseline), //or LastBaseline
fontSize = 24.sp,
)
Text(
text = "another Text",
modifier = Modifier.alignByBaseline(),
fontSize = 12.sp,
)
}
}
paddingFrom() 모디파이어를 사용해서 특정 자식의 정렬에 오프셋을 적용할 수 있습니다.
@Composable
fun MainScreen() {
Row {
Text(
text = "First Line\nSecond Line",
modifier = Modifier.alignBy(FirstBaseline),
fontSize = 24.sp,
)
Text(
text = "Another Text",
modifier = Modifier.paddingFrom(
alignmentLine = FirstBaseline,
before = 40.dp, after = 0.dp
),
fontSize = 12.sp,
)
}
}
FirstBaseline 기준 40dp 아래 지점에 형재 컴포저블을 배치했습니다.
weight() 모디파이어를 사용해서 가중치를 부과해서 자식들의 폭을 상대적으로 지정할 수 있습니다.
각 자식에게 가중치 0.0 ~ 1.0 사이의 가중치 비율을 할당합니다.
@Composable
fun MainScreen() {
Row {
TextCell(text = "1", modifier = Modifier.weight(weight = 0.2f, fill = true))
TextCell(text = "2", modifier = Modifier.weight(weight = 0.3f, fill = true))
TextCell(text = "3", modifier = Modifier.weight(weight = 0.5f, fill = true))
}
}
가중치 모디파이어가 적용되지 않은 형제 요소들은 적용된 형제들과 나머지 공간을 공유합니다.
@Composable
fun MainScreen() {
Row {
TextCell(text = "1", modifier = Modifier.weight(weight = 0.2f, fill = true))
TextCell(text = "2", modifier = Modifier.weight(weight = 0.3f, fill = true))
TextCell(text = "3", modifier = Modifier.weight(weight = 0.4f, fill = true))
TextCell(text = "4")
TextCell(text = "5")
}
}
기존에는 ConstraintLayout으로 UI를 구현했기 때문에 Column과 Row로 UI를 배치하는 것이 어색했습니다. 그러나 다양한 UI를 구현하면서 Arrangement와 Alignment를 자유자재로 사용할 수 있게 된 후, 다양한 배치 옵션을 통해 UI를 쉽게 구성할 수 있었습니다. LinearLayout에서는 레이아웃이 중첩될 경우 성능 저하가 발생하고 코드가 직관적이지 않았기 때문에 ConstraintLayout을 사용해야만 했습니다. 그러나 Compose는 레이아웃 중첩으로 인한 성능 저하에서 자유롭고, Custom Composable을 만들어 여러 번 재사용하여 가독성과 재사용성이 좋은 UI를 구현할 수 있다는 점이 큰 장점으로 다가왔습니다.