Jetpack Compose를 시작하기 위한 강의는 구글에서 제공한다.
레이아웃을 어떻게 만들지는 한 번만 따라해도 어느정도 각이 나온다. 어떻게 디자인하는지 알려주는 강의는 너무 많다. 하지만 중요한건 Integration! 앞으로 블로그에서 다룰 Jetpack Compose 관련 주제가 바로 Integration이 될 것이다. 어떻게 내 프로젝트에 통합시킬 수 있을지!
Theming
그 첫번째로 테마(Theme)와 스타일에 관해서 알아보려고한다.
Jetpack Compose에서 아주 쉽게 Material Design을 적용시킬 수 있다고는 하지만, 디자이너들은(93% 확률로 아이폰을 이용중) 우리에게 Material Design을 던져주지 않는다. 앱 고유의 스타일을 어떻게 확립하고 적용시킬 수 있는지 알아보자.
Colors
기존에는 앱에서 자주쓰는 색상들을 res > colors.xml 에 저장해왔다. 이제는 Colors.kt 같은 kotlin 파일에 저장한다.
github에서 높은 Star를 받은 Jetpack Compose 프로젝트 몇개를 보니 패키지명 > ui > Color.kt 혹은 패키지명 > ui > theme > Color.kt 정도의 경로로 정리하고 있다.
내용은 대충 이렇다.
import androidx.compose.ui.graphics.Color
val louBlue = Color(0xFF407FFC)
val purple500 = Color(0xFF6200EE)
val purple700 = Color(0xFF3700B3)
val teal200 = Color(0xFF03DAC5)
Theme
마찬가지로 theme.xml에 저장하고는 했었던 내용들을 Theme.kt에 작성할 수 있다. darkColors나 lightColors는 각각 다크테마와 라이트테마 기준으로 만들어지며, 값을 주지 않으면 기본 값을 채운다. 반대로 Colors는 테마를 처음부터 재정의하기 때문에 모든 parameter를 다 채워주어야한다.
여기에는 당연하게도 아까 Colors.kt에 정의했던 색깔들을 불러올 수 있다.
import androidx.compose.material.Colors
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.ui.graphics.Color
private val DarkColorPalette = darkColors(
primary = louBlue,
primaryVariant = louBlue,
secondary = teal200,
background = Color.Black,
surface = Color.DarkGray
)
private val LightColorPalette = lightColors(
primary = louBlue,
primaryVariant = louBlue,
secondary = louBlue,
background = Color.White
)
private val customColorPalette = Colors(
primary = purple500,
primaryVariant = purple500,
secondary = purple700,
secondaryVariant = purple700,
background = Color.White,
surface = Color.White,
error = Color.White,
onPrimary = Color.Black,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
onError = Color.White,
isLight = true
)
Theme.kt를 작성했다면, 이 모든 테마를 Composable 함수로 래핑하여 적용가능한 형태로 만들어야한다. 똑같이 Theme.kt 파일에다 작성해주면 된다. @Composable은 Annotation processor처럼 보일 수 있지만 사실은 suspend 키워드 처럼 함수의 타입을 정의하는 language keyword다. 이 사실을 숙지하면 @Composable () -> Unit 같은 코드가 자연스럽게 보이기 시작할 것 이다.
나는 LouAppTheme라는 테마를 정의했고, 시스템이 다크모드냐 아니냐에 따라 colors를 정의하고 MaterialTheme을 만들어서 해당 컬러팔레트를 인자로 넘겨주었다.
@Composable
fun LouAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) DarkColorPalette else LightColorPalette
MaterialTheme(
colors = colors,
content = content
)
}
MaterialTheme은 사실 이렇게 생긴 Composable 함수다. 이걸보면 "shape나 typography도 같이 정의해서 넣을 수 있구나
이 글의 어딘가에 쉐이프랑 폰트 정의를 얘기할꺼구나~"를 직감할 수 있다.
@Composable
fun MaterialTheme(
colors: Colors = MaterialTheme.colors,
typography: Typography = MaterialTheme.typography,
shapes: Shapes = MaterialTheme.shapes,
content: @Composable () -> Unit
) {
...
}
나를 포함하여 추가적인 궁금증이 드시는 분들을 위해... 방금 정의한 Theme은 이렇게 사용할 수 있다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LouAppTheme {
// TODO Composable...
// MaterialTheme.colors.primary 로 접근가능
}
}
}
}
Typography
올 것이 왔군? Typography.kt 파일에 타이포 그래피를 정의할 수 있다. 커스텀 폰트를 사용하기 위해서 앱의 res > font에 사용할 font를 넣는 방식은 같다. 나는 샘플로 Noto sans를 다운받아서 앱에 적용하려한다.
val NotoFontFamily = FontFamily(
Font(R.font.noto_sans_cjk_kr_regular),
Font(R.font.noto_sans_cjk_kr_medium, FontWeight.Medium),
Font(R.font.noto_sans_cjk_kr_bold, FontWeight.Bold)
)
Typography는 이렇게 정의할 수 있다. defaultFontFamily는 따로 FontFamily가 없다면 생략가능하다.
val typography = Typography(
defaultFontFamily = NotoFontFamily,
h1 = TextStyle(
fontWeight = FontWeight.Light,
fontSize = 96.sp,
letterSpacing = 1.2.sp
)
)
앱의 각 구성요소마다 이렇게 텍스트 스타일을 미리 지정해둘 수 있다. 정의 가능한 요소들은 아래 사진과 같다.

Shape
윈도우 UI의 타일 디자인처럼 모든 버튼이 각져있는 앱은 드물다. (미안해 마소...!!ㅋㅋㅋ) 다들 저마다의 Radius를가진 둥글둥글한 버튼들이나, 카드 컴포넌트를 이용한다.

원래는 Shape들을 drawables 아래에 xml로 만들어서 배경으로 넣어주고...했는데 Compose에는 Shape라는 파라미터를 정의해주면 알아서 둥글게 처리된다. 통일감있는 둥글기 처리를 위해 이 Shape의 Radius값들도 테마로서 정의할 수 있다. 이제 대충 Shape.kt 정도에 저장하는게 custom임을 아셨으리...
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(8.dp),
large = RoundedCornerShape(16.dp)
)
아까 잠깐 MaterialTheme에서 보았던 것 처럼 테마에서 파라미터로 넘겨서 정의하고, Composable 함수에서 MaterialTheme.shapes.large 이렇게 접근해서 적용시킬 수 있다.
참고자료
https://medium.com/androiddevelopers/under-the-hood-of-jetpack-compose-part-2-of-2-37b2c20c6cdd
https://infinum.com/the-capsized-eight/jetpack-compose-framework
'프로그래밍 > Android' 카테고리의 다른 글
| [안드로이드] Koin에서 Hilt로, Hilt 배워보기 (0) | 2021.10.09 |
|---|---|
| [안드로이드] 카카오웹툰 앱에서 쓴 오픈소스 라이브러리를 알아보자 (1) | 2021.09.07 |
| Android Studio 한글 깨짐 현상 해결법 (Arctic Fox) (0) | 2021.09.01 |
| [Jetpack Compose] 왜 Jetpack Compose가 나와야만 했을까? (0) | 2021.08.30 |
| [안드로이드] Retrofit2 오프라인 캐시 구현하기 (Offline cache interceptor) (0) | 2021.08.30 |