Compose 레이아웃 기본사항

Jetpack Compose를 사용하면 앱의 UI를 훨씬 쉽게 디자인하고 빌드할 수 있습니다. Compose는 다음을 통해 상태를 UI 요소로 변환합니다.

  1. 요소 구성
  2. 요소 레이아웃
  3. 요소 그림

구성, 레이아웃, 그림을 통해 상태를 UI로 변환하는 Compose

이 문서에서는 요소의 레이아웃을 중심으로, UI 요소를 간단히 배치하는 데 유용한 Compose의 일부 기본 구성요소를 설명합니다.

Compose의 레이아웃 목표

Jetpack Compose의 레이아웃 시스템 구현에는 두 가지 주요 목표가 있습니다.

구성 가능한 함수의 기본사항

구성 가능한 함수는 Compose의 기본 구성요소입니다. 구성 가능한 함수는 UI의 일부를 설명하는 Unit을 내보내는 함수입니다. 이 함수는 몇 가지 입력을 받아서 화면에 표시되는 내용을 생성합니다. 컴포저블에 관한 자세한 내용은 Compose 멘탈 모델 문서를 살펴보세요.

구성 가능한 함수는 여러 UI 요소를 내보낼 수 있습니다. 그러나 개발자가 UI 요소를 어떻게 정렬해야 하는지에 관한 가이드를 제공하지 않으면 Compose는 개발자가 원하지 않는 방식으로 요소를 정렬할 수 있습니다. 예를 들어 다음 코드는 텍스트 요소 두 개를 생성합니다.

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

원하는 정렬 방식에 관한 가이드가 없으면 Compose는 텍스트 요소를 서로 겹치게 표시하므로 텍스트를 읽을 수 없게 됩니다.

두 개의 텍스트 요소가 서로 위에 그려져 텍스트를 읽을 수 없게 됨

Compose는 UI 요소를 정렬하는 데 도움이 되도록 즉시 사용 가능한 레이아웃 컬렉션을 제공하므로 이를 사용하면 더욱 전문적인 고유한 레이아웃을 쉽게 정의할 수 있습니다.

표준 레이아웃 구성요소

많은 경우에 Compose의 표준 레이아웃 요소를 사용할 수 있습니다.

Column을 사용하여 항목을 화면에 세로로 배치합니다.

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

두 개의 텍스트 요소가 열 레이아웃으로 정렬되므로 텍스트를 읽을 수 있음

마찬가지로 Row를 사용하여 항목을 화면에 가로로 배치합니다. ColumnRow는 모두 포함된 요소의 정렬 구성을 지원합니다.

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

텍스트 요소의 열 옆에 작은 그래픽이 있는 더 복잡한 레이아웃 예시

Box를 사용하여 요소를 다른 요소 위에 놓습니다. Box는 포함된 요소의 특정 정렬 구성도 지원합니다.

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

두 요소가 서로 겹쳐서 표시됨

흔히 이러한 구성요소만 있으면 됩니다. 자체 구성 가능한 함수를 작성하여 이러한 여러 레이아웃을 앱에 적합한 더욱 정교한 레이아웃으로 결합할 수 있습니다.

세 가지 간단한 레이아웃 컴포저블(열, 행, 상자) 비교

Row 내에서 하위 요소의 위치를 설정하려면 horizontalArrangementverticalAlignment 인수를 설정하세요. Column의 경우 verticalArrangementhorizontalAlignment 인수를 설정합니다.

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

항목이 오른쪽에 정렬됨

레이아웃 모델

레이아웃 모델에서 UI 트리는 단일 패스로 배치됩니다. 각 노드는 먼저 자체 측정을 요청받고 하위 요소를 반복적으로 측정하여 크기 제약 조건을 트리 아래 하위 요소로 전달합니다. 그러면 리프 노드가 크기 지정 및 배치되고 확인된 크기 및 배치 안내는 다시 트리 위로 전달됩니다.

간단히 말해 상위 요소는 하위 요소보다 먼저 측정되지만 크기와 위치는 하위 요소 다음에 지정됩니다.

다음 SearchResult 함수를 살펴보세요.

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

이 함수는 다음과 같은 UI 트리를 생성합니다.

SearchResult
  Row
    Image
    Column
      Text
      Text

SearchResult 예에서는 다음 순서에 따라 UI 트리가 레이아웃됩니다.

  1. 루트 노드 Row에 측정을 요청합니다.
  2. 루트 노드 Row는 첫 번째 하위 요소 Image에 측정을 요청합니다.
  3. Image는 리프 노드(하위 요소가 없음)이므로 크기를 보고하고 배치 안내를 반환합니다.
  4. 루트 노드 Row는 두 번째 하위 요소 Column에 측정을 요청합니다.
  5. Column 노드는 첫 번째 Text 하위 요소에 측정을 요청합니다.
  6. 첫 번째 Text 노드는 리프 노드이므로 크기를 보고하고 배치 안내를 반환합니다.
  7. Column 노드는 두 번째 Text 하위 요소에 측정을 요청합니다.
  8. 두 번째 Text 노드는 리프 노드이므로 크기를 보고하고 배치 안내를 반환합니다.
  9. 이제 Column 노드가 하위 요소를 측정하여 크기를 지정하고 배치했으므로 자체 크기와 배치를 결정할 수 있습니다.
  10. 이제 루트 노드 Row가 하위 요소를 측정하여 크기를 지정하고 배치했으므로 자체 크기와 배치를 결정할 수 있습니다.

검색결과 UI 트리의 측정, 크기 지정, 배치 순서

성능

Compose는 하위 요소를 한 번만 측정하여 높은 성능을 발휘합니다. 단일 패스 측정은 성능 측면에서 효율적이므로 Compose가 깊은 UI 트리를 효율적으로 처리할 수 있습니다. 요소가 하위 요소를 두 번 측정한 후 이 하위 요소가 각각의 자체 하위 요소를 두 번 측정하는 방식은 전체 UI를 배치하려는 한 번의 시도에서 많은 작업을 실행해야 하므로 앱의 성능을 유지하기가 어렵습니다.

어떤 이유로든 레이아웃에 여러 측정이 필요하면 Compose는 내장 측정 기능이라는 특수한 시스템을 제공합니다. 이 기능에 관한 자세한 내용은 Compose 레이아웃의 내장 기능 측정을 참고하세요.

측정과 배치는 레이아웃 패스의 별개 하위 단계이므로, 항목의 측정이 아닌 배치에만 영향을 미치는 모든 변경사항은 별도로 실행할 수 있습니다.

레이아웃에서 수정자 사용

Compose 수정자에서 설명한 대로 수정자를 사용하여 컴포저블을 장식하거나 강화할 수 있습니다. 수정자는 레이아웃을 맞춤설정하는 데 필수적입니다. 예를 들어 여기서는 여러 수정자를 연결하여 ArtistCard를 맞춤설정합니다.

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

수정자를 사용하여 그래픽이 정렬되는 방식과 사용자 입력에 응답하는 영역을 변경하는 훨씬 더 복잡한 레이아웃

위의 코드에서 다양한 수정자 함수가 함께 사용된 것을 확인할 수 있습니다.

  • clickable: 컴포저블이 사용자 입력에 반응하도록 설정하고 물결 효과를 표시합니다.
  • padding: 요소 주위에 공간을 배치합니다.
  • fillMaxWidth: 컴포저블이 상위 요소로부터 부여받은 최대 너비를 채우도록 합니다.
  • size(): 요소의 기본 너비 및 높이를 지정합니다.

스크롤 가능한 레이아웃

Compose 동작 문서에서 스크롤 가능한 레이아웃에 관해 자세히 알아보세요.

목록과 지연 목록은 Compose 목록 문서를 참고하세요.

반응형 레이아웃

레이아웃은 여러 화면 방향과 폼 팩터 크기를 고려하여 디자인해야 합니다. Compose에서 제공하는 즉시 사용 가능한 몇 가지 메커니즘으로 컴포저블 레이아웃을 다양한 화면 구성에 따라 쉽게 조정할 수 있습니다.

제약 조건

상위 요소의 제약 조건을 파악하고 그에 따라 레이아웃을 디자인하려면 BoxWithConstraints를 사용하면 됩니다. 측정 제약 조건은 콘텐츠 람다의 범위에서 확인할 수 있습니다. 이 측정 제약 조건을 사용하여 다양한 화면 구성에 따라 다양한 레이아웃을 구성할 수 있습니다.

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

슬롯 기반 레이아웃

Compose는 UI를 쉽게 빌드할 수 있도록 머티리얼 디자인 및 Android 스튜디오에서 Compose 프로젝트를 만들 때 포함되는 androidx.compose.material:material 종속 항목을 기반으로 한 다양한 컴포저블을 제공합니다. Drawer, FloatingActionButtonTopAppBar와 같은 요소가 모두 제공됩니다.

머티리얼 구성요소는 Compose가 컴포저블 위에 맞춤설정 레이어를 배치하기 위해 도입한 패턴인 슬롯 API를 많이 사용합니다. 이 접근 방식을 사용하면 하위 요소의 모든 구성 매개변수를 노출하지 않고 자체적으로 하위 요소를 구성할 수 있으므로 구성요소의 유연성이 향상됩니다. 슬롯은 개발자가 원하는 대로 채울 수 있도록 UI에 빈 공간을 남겨둡니다. 예를 들어 다음은 개발자가 TopAppBar에서 맞춤설정할 수 있는 슬롯입니다.

머티리얼 구성요소 앱 바에 사용 가능한 슬롯을 보여주는 다이어그램

컴포저블은 일반적으로 content 컴포저블 람다(content: @Composable () -> Unit)를 사용합니다. 슬롯 API는 특정 용도를 위해 여러 content 매개변수를 노출합니다. 예를 들어 TopAppBar를 사용하면 title, navigationIconactions의 콘텐츠를 제공할 수 있습니다.

예를 들어 Scaffold를 사용하면 기본 머티리얼 디자인 레이아웃 구조로 UI를 구현할 수 있습니다. ScaffoldTopAppBar, BottomAppBar, FloatingActionButton, Drawer 등 가장 일반적인 상위 머티리얼 구성요소용 슬롯을 제공합니다. Scaffold를 사용하면 이러한 구성요소가 적절하게 배치되어 함께 올바르게 작동하는지 쉽게 확인할 수 있습니다.

Scaffold를 사용하여 여러 요소를 배치하는 JetNews 샘플 앱

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}