컴포즈에는 앱을 만드는데 필요한 개발자가 사용할 수 있는 머테리얼 컴포넌트 컴포저블이 딸려있다. 가장 높은 수준의 컴포저블은 Scaffold다.

Scaffold

Scaffold(발판)는 기초적인 머테리얼 디자인 레이아웃 구조를 구현할 수 있도록 도와준다. TopAppBar, BottomAppBar, FloatingActionButton 및 Drawer와 같은 가장 일반적인 최상위 머테리얼 컴포넌트에 대한 Slot을 제공한다. Scaffold를 사용하면 이러한 컴포넌트들을 배치하고 올바르게 동작하도록 한다.

안드로이드 스튜디오에서 생성한 템플릿을 기반으로, 샘플 코드를 수정하여 Scaffold를 사용해보자. MainActivity.kt를 열고 더 이상 사용하지 않을 Greeting 및 GreetingPreview 컴포저블을 삭제하자.

LayoutsCodelab 라는 이름으로 새로운 컴포저블을 생성하여, 이 코드랩을 통해 수정해보도록 하자.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LayoutsCodelabTheme {
                LayoutsCodelab()
            }
        }
    }
}

@Composable
fun LayoutsCodelab() {
    Text(text = "Hi there!")
}

@Preview
@Composable
fun LayoutsCodelabPreview() {
    LayoutsCodelabTheme {
        LayoutsCodelab()
    }
}

컴포즈 @Preview 애노테이션이 붙어있는 미리보기 함수를 살펴보면 LayoutCodelab이 다음과 같이 나타난다.

예제에 Scaffold 컴포저블 추가하여 전형적인 머테리얼 디자인 구조를 가질 수 있도록 하자. Scaffold API의 모든 매개변수는 @Composable (InnerPadding) -> Unit 타입의 content를 제외하고는 선택사항이다. 이 content 람다식의 매개변수는 padding을 매개변수로 받는데, 이는 화면에 들어가는 아이템들을 적절하게 제한하기 위해 최상위 컴포저블에 적용하는 패딩이다. 일단 이해하기 쉽도록 단순하게 시작하기 위해, Scaffold를 다른 머테리얼 컴포넌트 없이 추가해보도록 하자.

@Composable
fun LayoutsCodelab() {
    Scaffold { innerPadding ->
        Text(text = "Hi there!", modifier = Modifier.padding(innerPadding))
    }
}

미리보기로 확인하면 다음과 같다.

화면에 메인 컨텐츠를 Column과 함께 넣기를 원하면, Column의 Modifier를 다음과 같이 적용하자.

@Composable
fun LayoutsCodelab() {
    Scaffold { innerPadding ->
        Column(modifier = Modifier.padding(innerPadding)) {
            Text(text = "Hi there!")
            Text(text = "Thanks for going through the Layouts codelab")
        }
    }
}

미리보기로 보면 다음과 같다.

코드의 재사용성과 테스트 용이성을 더 좋게 만들기 위해서, 작은 단위로 구조를 짜야한다. 그러기 위해서는 화면의 컨텐츠와 함께 또 다른 컴포저블 함수를 생성해야 한다.

@Composable
fun LayoutsCodelab() {
    Scaffold { innerPadding ->
        BodyContent(Modifier.padding(innerPadding))
    }
}

@Composable
fun BodyContent(modifier: Modifier = Modifier) {
    Column(modifier = modifier) {
        Text(text = "Hi there!")
        Text(text = "Thanks for going through the Layouts codelab")
    }
}

안드로이드에서는 상단 AppBar에 현재 화면, 네비게이션, 액션에 대한 정보를 보여주는 것이 일반적이다. 예제에 이런 점을 지금 추가해보자.

TopAppBar

Scaffold는 @Composable() -> Unit 타입의 topBar 매개변수와 함께 상단 AppBar를 위한 slot을 가지고 있다. 이것이 의미하는 점은 슬롯에 우리가 원하는 어떤 컴포저블도 채워 넣을 수 있다는 것이다. 예를들어, h3 스타일의 텍스트를 포함하길 원하면 slot에 Text를 사용하기만 하면된다. 다음 코드를 살펴보자.

@Composable
fun LayoutsCodelab() {
    Scaffold(
        topBar = {
            Text(
                text = "LayoutsCodelab",
                style = MaterialTheme.typography.h3
            )
        }
    ) { innerPadding ->
        BodyContent(Modifier.padding(innerPadding))
    }
}

미리보기상으로는 다음과 같이 나온다.

그러나 컴포즈에서 대부분의 머테리얼 컴포넌트를 보면 타이틀, 네비게이션 아이콘 및 액션을 위한 슬롯을 갖는 TopAppBar 컴포넌트가 딸려있다. 또한, 각 컴포넌트에 사용할 색상과 같이 머테리얼 스펙에서 권장하는 사항에 맞게 조정되는 일부 기본값이 함께 제공된다.

다음 slot API 패턴을 보면, TopAppBar의 title 슬롯에 화면 제목을 갖는 Text를 배치하고 있다.

@Composable
fun LayoutsCodelab() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(text = "LayoutsCodelab")
                }
            )
        }
    ) { innerPadding ->
        BodyContent(Modifier.padding(innerPadding))
    }
}

미리보기에서는 다음과 같이 나온다.

상단 AppBar들은 보통 action 항목들을 갖는다. 여기 나오는 예제에서, 무언가 배웠다고 생각이 들 때, 탭 할 수 있는 즐겨찾기(favorite) 버튼을 추가해보자. 컴포즈는 또한 우리가 사용할 수 있는 미리 정의된 머테리얼 아이콘을 포함하고 있는데, 예를 들면 닫기(close), 즐겨찾기(favorite) 및 메뉴(menu) 가 있다.

상단 AppBar에 action 아이템들을 위한 slot은 actions 매개변수로 받는데, 이는 내부적으로 Row를 사용한다. 그래서 다수의 action을 수평적으로 배치한다. 미리 정의된 아이콘들중 하나를 사용하기 위해, 우린 IconButton 컴포저블에 내부에 Icon을 사용할 수 있다.

@Composable
fun LayoutsCodelab() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(text = "LayoutsCodelab")
                },
                actions = {
                    IconButton(onClick = { /* doSomething() */ }) {
                        Icon(Icons.Filled.Favorite, contentDescription = null)
                    }
                }
            )
        }
    ) { innerPadding ->
        BodyContent(Modifier.padding(innerPadding))
    }
}

미리보기로 확인하면 다음과 같다.

보통의 경우, action들은 애플리케이션의 상태(state)를 수정한다. 상태에 대한 더 많은 정보는 Basics Compose codelab에서 상태관리의 기초에 대해 배울 수 있다.

Modifier 배치하기

새로운 컴포저블을 생성할 때마다, 기본값을 갖는 Modifier 매개변수의 사용은 컴포저블을 좀 더 재사용하기 좋은 방법이다. 아래에 나올 BodyContent 컴포저블은 이미 Modifier를 매개변수로 취하고 있다. 만약 더 많은 여백을 갖도록 padding값을 BodyContent에 추가하고 싶다면, padding이 포함된 Modifier를 어디에 배치해야 하는게 좋을까?

두가지 가능성을 고려해볼 수 있다.

  • 1. Modifier를 직접적으로 자식 컴포저블에만 적용하여, BodyContent에 대한 모든 호출이 추가적인 여백을 적용할 수 있도록 하는 것
@Composable
fun BodyContent(modifier: Modifier = Modifier) {
    Column(modifier = modifier.padding(8.dp)) {
        Text(text = "Hi there!")
        Text(text = "Thanks for going through the Layouts codelab")
    }
}
  • 2. 컴포저블을 호출할 때 Modifier를 적용하여 필요할 때 추가적인 여백을 더하는 것
@Composable
fun LayoutsCodelab() {
    Scaffold(...) { innerPadding ->
        BodyContent(Modifier.padding(innerPadding).padding(8.dp))
    }
}

이것은 전적으로 컴포저블 형식 및 유즈케이스를 보고 결정할 수 있다. Modifier가 컴포저블에 본질적인 부분이라면, 이를 안쪽에 배치하도록 하자. 만약에 그게 아니라면, 밖에 배치하자. 우리의 경우 padding이 BodyContent를 호출할 때마다 항상 강제로 적용되지 않을 수 있으므로 두번째 옵션을 선택하자. 이건 정말 case-by-case임을 유의하자.

Modifier는 이전에 호출한 Modifier 함수를 통해 체이닝을 할 수 있다. 체이닝이 불가능한 메서드를 호출했을때는 .then()을 사용할 수 있다. 우리 예제에서는 modifier로 체이닝을 시작하는데, 이는 체이닝이 매개변수로 전달된 modifier에 계속하여 체이닝되는 것을 의미한다.

더 많은 아이콘들

이전에 소개한 아이콘들은 제외하고, 프로젝트에 새로운 의존성을 추가하는 것으로 전체 머테리얼 아이콘 목록을 사용할 수 있다. 이 아이콘들을 실험해보고 싶다면, app/build.gradle 파일을 열어서 ui-material-icons-extended 의존성을 추가한다.

dependencies {
  ...
  implementation "androidx.compose.material:material-icons-extended:$compose_version"
}

이제 원하는 만큼 TopAppBar의 아이콘들을 어서 변경해보도록 하자!

추가 작업

Scaffold 및 TopAppBar는 단지 머테리얼 디자인을 갖는 애플리케이션을 위해 사용되어지는 컴포저블에 불과하다. BottomNavigation 또는 BottomDrawer와 같은 다른 머테리얼 컴포넌트에 대해서도 동일한 작업을 수행할 수 있다. 연습으로 지금까지 우리가 해왔던 겉과 같은 방식으로 이러한 API과 함께 Scaffold의 Slot들을 채워보도록 하자.

카테고리: Compose

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.