읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 사용해보면서 배운 점을 정리한 글입니다.

Jetpack Compose의 레이아웃 코드랩에서 실습한 내용들을 정리합니다. 기본적인 Modifier 적용과 순서에 따른 동작 차이점에 대해 다룹니다. 코드랩 실습에 요구되는 시간은 대략 1시간 정도로 학습 목적을 고려하면 대략 2 - 3시간 정도를 투자해야 합니다. 분량이 너무 많아 챕터 별로 분할하여 업로드합니다.

프로젝트 시작

실습을 위해 새로운 안드로이드 프로젝트를 생성한 뒤 Empty Compose Activity를 선택하여 생성한다. Compose API의 최소 호환 버전을 맞추기 위해 minimumSdkVersion을 API Level 21 이상으로 설정해야 한다.

Codelab_Jetpack_Compose_layout_001

Codelab_Jetpack_Compose_layout_002

Modifier(수정자)

Codelab_Jetpack_Compose_layout_003

Modifier 섹션에서는 위의 그림대로 컴포저블을 구성해본다.

Modifier를 사용하여 컴포저블을 가공할 수 있다. 라벨 추가, 클릭 및 스크롤 부여, 동작 및 모양 등을 정의할 수 있다. 또한, Modifier는 코틀린 객체로 변수에 할당 및 재사용이 가능하며 여러 수정자를 체이닝하여 구성할 수 있으며 확장함수를 정의할 수도 있다.

@Composable
fun PhotographerCard() {
    Column {
        Text("Alfred Sisley", fontWeight = FontWeight.Bold)
        // LocalContentAlpha는 자신의 scope 하단의 자식 컴포즈의 opacity level을 정의한다.
        CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            Text("3 minutes ago", style = MaterialTheme.typography.body2)
        }
    }
}

@Preview
@Composable
fun PhotographerCardPreview() {
    LayoutsCodelabTheme {
        PhotographerCard()
    }
}

Column 컴포저블을 사용하여 수직 레이아웃을 선언한 뒤 상단에 Text 컴포저블을 선언하고 하단에 CompositionLocalProvider를 사용하여 내부 scope에 Text 컴포저블을 선언한 뒤 스타일을 부여하였다. Preview Annotation을 선언한 Preview용 컴포저블 함수를 선언하여 정의한 레이아웃이 어떤 모습을 갖는지 확인할 수 있다.

Codelab_Jetpack_Compose_layout_004

텍스트 구성은 마무리되었다. 이제 사진사 사진을 로드하기 전까지의 공간을 표시하기 위해 placeholder를 정의해야 한다.

@Composable
fun PhotographerCard() {
    Row {
        Surface(
            modifier = Modifier.size(50.dp),
            shape = CircleShape,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.2f)
        ) {
            // Image goes here
        }
        Column {
            Text("Alfred Sisley", fontWeight = FontWeight.Bold)
            CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                Text("3 minutes ago", style = MaterialTheme.typography.body2)
            }
        }
    }
}

사진사의 사진과 텍스트는 서로 가로로 쌓이는 모양새이므로 바깥에 Row를 선언한 뒤 사진사 사진을 담을 Surface 컴포저블을 선언한다. 이후 두 텍스트 컴포저블이 담긴 Column 컴포저블이 선언되었다.

Codelab_Jetpack_Compose_layout_005

필자의 안드로이드 스튜디오의 모드가 다크모드라 좌측 placeholder의 색상이 가려졌다. 라이트모드에서 보면 확실히 회색 원형 컴포저블이 생성되었음을 확인할 수 있다.

Codelab_Jetpack_Compose_layout_006

섹션 처음에 생성하려는 컴포저블과 비교하여 두 가지 개선할 점이 보인다.

  1. placeholder와 텍스트 사이의 간격이 필요하다.

우측 두 개의 텍스트 컴포저블을 담은 Column 컴포저블과 placeholder 컴포저블 간 간격을 띄워야 하므로 Column 컴포저블의 파라미터에 Modifier를 부여할 수 있다. Modifier.padding(start = 8.dp)를 부여하자.

  1. 사진사의 설명이 적힌 텍스트 컴포저블이 세로로 가운데 위치해야 한다.

우측 Column 컴포저블에 속한 텍스트 컴포저블들이 수직 중앙 정렬되었으면 한다. 그렇다면 Column 컴포저블의 scope에 속한 컴포저블에 대해 정렬 속성을 부여하자. 역시나 Modifier 파라미터에 속성을 부여해야 한다. .align(Alignment.CenterVertically)를 부여하자.

@Composable
fun PhotographerCard() {
    Row {
        Surface(
            modifier = Modifier.size(50.dp),
            shape = CircleShape,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.2f)
        ) {
            // Image goes here
        }
        Column(
            modifier = Modifier
                .padding(start = 8.dp)
                .align(Alignment.CenterVertically)
        ) {
            Text("Alfred Sisley", fontWeight = FontWeight.Bold)
            CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                Text("3 minutes ago", style = MaterialTheme.typography.body2)
            }
        }
    }
}

Codelab_Jetpack_Compose_layout_007

좌측 placeholder와 텍스트 간의 간격이 생겼고 텍스트 또한 수직 중앙정렬된 모습이다. 이렇듯 Modifier를 사용하여 컴포저블을 다양하게 조정할 수 있다. 따라서, 커스텀 컴포저블을 만들 때 파라미터로 Modifier를 받게끔 하여 외부에서 Modifier를 주입할 수 있도록 작성하자.

@Composable
fun PhotographerCard(modifier: Modifier = Modifier) {
    Row(modifier) { ... }
}

주의할 점으로는 위 코드대로 파라미터에 Modifier를 작성하여 Row 컴포저블을 조정할 수 있는 옵션을 부여하였는데 내부에 선언할 컴포저블에 대해서도 스타일을 조정할 필요가 있는지 염두에 둬야 한다. 굳이 Modifier를 부여할 필요가 없다면 스타일 파라미터를 작성하여 개별적으로 부여할 수 있을 것이다.

@Composable
fun PhotographerCard(
    modifier: Modifier = Modifier,
    fontColor: Color = Color.Black,
    content: String = "") {
    Row(modifier) { 
        Text(text = content, color = fontColor)
    }
}

Modifier 순서의 중요성

위에서 작성한 Modifier.padding(start = 8.dp).align(Alignment.CenterVertically처럼 Modifier를 적용할 때는 확장함수들을 적용하여 체이닝한다. 단일 객체로 연결하여 사용하기에 순서에 따라 최종 결과가 달라진다. 사진가 프로필에 클릭 속성을 부여하면서 패딩도 적용해보자.

@Composable
fun PhotographerCard(modifier: Modifier = Modifier) {
    Row(modifier
        .padding(16.dp)
        .clickable(onClick = { /* Ignoring onClick */ })
    ) {
        ...
    }
}

전체 상하좌우 패딩을 16.dp씩 부여하고 클릭 속성을 부여하였다. 이에 대해 클릭을 수행하면 아래와 같이 동작한다.

Codelab_Jetpack_Compose_layout_008

그림을 보면 모든 영역에 클릭이 적용되지 않는다. 그 이유는 padding이 clickable보다 앞에 적용되었기 때문이다. 만약 순서를 바꿔서 padding을 clickable modifier 뒤에 적용하면 패딩 영역도 클릭할 수 있다.

@Composable
fun PhotographerCard(modifier: Modifier = Modifier) {
    Row(modifier
        .clickable(onClick = { /* Ignoring onClick */ })
        .padding(16.dp)
    ) {
        ...
    }
}

Codelab_Jetpack_Compose_layout_009

Modifier를 사용하여 좀 더 그럴듯하게 꾸며보자. 전체 영역에 대해 paddingdmf 8.dp를 부여하고 4.dp만큼 round corner를 부여한다. 그리고 배경은 MaterialTheme에 정의된 기본 surface 색상이고 클릭 속성을 부여하며 내부 컨텐츠에 대해 16.dp padding을 또 부여한다.

@Composable
fun PhotographerCard(modifier: Modifier = Modifier) {
    Row(modifier
        .padding(8.dp)
        .clip(RoundedCornerShape(4.dp))
        .background(MaterialTheme.colors.surface)
        .clickable(onClick = { /* Ignoring onClick */ })
        .padding(16.dp)
    ) {
        ...
    }
}

Codelab_Jetpack_Compose_layout_010

전체 영역에 대해 바깥 8.dp는 클릭속성이 부여되어 있지 않아 클릭할 수 없다. 역시 round corner를 clickable보다 먼저 부여했기에 클릭 시 음영 처리되는 영역의 모서리도 둥그렇게 처리되었다. 클릭 속성 부여 이후 padding을 16.dp만큼 부여하였고 해당 영역은 음영처리됨을 확인할 수 있다.

참고자료

  1. Google Codelab - Jetpack Compose Layout #2 수정자

+ Recent posts