GitHub : https://github.com/seyoungcho2/ComposeDynamicTheme
도움이 되셨다면 스타★를 눌러주세요!
Dynamic Theme 이란 무엇인가?
지금까지 안드로이드에서 테마를 변경하는 것은 매우 어려운 작업이었다. 이번에 배포한 Dynamic Theme은 안드로이드의 테마 관리를 편하게 만들기 위해 개발되었다. Dynamic Theme는 안드로이드 Jetpack Compose를 위한 Material Design 기반의 테마 관리 시스템으로 단순히 테마를 적용하고 싶은 곳 최상위에 'ProvidesTheme'을 추가하여 테마를 설정하는 것을 가능하게 한다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
DynamicThemeService.get().ProvidesTheme {
// Add Compose Functions
}
}
}
}
이를 활용하면 아래와 같이 간단하게 테마 변경이 가능해진다.
Compose Dynamic Theme 개발 계기
개인 개발을 하면서 테마 관리와 관련된 문제가 많았고, 이를 해결할 수 있는 방안을 고민했다. 해결책은 이에 대한 관리 포인트를 일원화 하고, 독립성 있는 모듈로 만드는 것이었다. 이에 따라 올해 초에 Dynamic Theme 모듈을 제작하였다.
테마 관리 시스템을 모듈로 만든 다음 테마 관리가 매우 편해졌고, 혼자 쓰는 것은 아까워서 이를 오픈소스 라이브러리로 만들어도 좋겠다는 생각을 했다. 그러다가 이번에 휴가기간 동안 약속이 취소되어 시간이 많이 남아서 오픈소스로 만들어 배포하게 되었다.
이왕 만들었으니, 많은 사람들이 이 라이브러리를 써서 편하게 테마 작업을 하면 좋을 것 같다.
Compose Dynamic Theme 사용법
라이브러리 추가하기
프로젝트 수준의 build.gradle에 mavenCentral Repository를 추가한다.
allprojects {
repositories {
mavenCentral()
}
}
모듈 수준의 build.gradle에 dynamic-theme-compose 라이브러리를 추가한다.
dependencies {
implementation "io.github.seyoungcho2:dynamic-theme-compose:0.0.1"
}
구성요소
Compose Dynamic Theme은 세가지 구성요소로 이루어진다.
- ThemeModelKey
- ThemeModel
- How to use DynamicThemeService
ThemeModelKey
ThemeModelKey는 ThemeModel을 추가하거나 추가된 ThemeModel을 가져오는데 사용된다.
- ThemeModelKey는 ThemeModelKey.of 함수를 사용해 정의될 수 있다.
val THEME_MODEL_KEY_BLUE = ThemeModelKey.of("Blue")
- ThemeModelKey.Default는 기본 테마를 위해 미리 정의되어 있다. 만약 테마가 설정되어 있지 않다면 ThemeModelKey.Default가 ThemeModel을 가져오는데 사용된다.
DynamicThemeService.getInstance(context).getThemeModel(ThemeModelKey.Default)
ThemeModel
ThemeModel은 테마의 정보를 담고 있는 data class이다. 이 클래스는 3개의 파라미터를 가진다.
data class ThemeModel(
val colorPalette: ColorPalette = ColorPalette(),
val typography: Typography = Typography(),
val shapes: Shapes = Shapes()
)
- ColorPalette: 테마를 위한 색상 조합. 기기가 다크 모드로 설정되어 있을 때는 darkModeColors가 사용되고, 아닐 경우에는 lightModeColors가 사용된다.
data class ColorPalette(
val lightModeColors: Colors = LightModeColorPalette,
val darkModeColors: Colors = DarkModeColorPalette
)
- Typography: 테마의 문자 모양 설정을 위한 설정. 문자 모양을 계층화 하기 위해 사용된다. h1,h2,h3,subtitle1,subtitle2,body1,body2 등의 모양을 설정할 수 있다.
- Shapes: 테마의 도형 모양 설정이 가능하다.
이 모든 것을 설정할 필요는 없고, 아래와 같이 설정하고 싶은 부분만 설정하면 된다.
val THEME_MODEL_KEY_BLUE = ThemeModelKey.of("Blue")
private val THEME_MODEL_BLUE = ThemeModel(
colorPalette = ColorPalette(
lightModeColors = ColorPalettes.BlueColorPalette,
darkModeColors = ColorPalettes.BlueColorPalette,
)
)
테마 추가하기
단일 테마 추가하기
단일 테마는 아래와 같이 ThemeModelKey와 ThemeModel의 조합으로 추가하면된다.
DynamicThemeService.get().apply {
registerThemeModel(
ThemeModelKey.of("Pink"), ThemeModel(
colorPalette = ColorPalette(
lightModeColors = ColorPalettes.PinkColorPalette,
darkModeColors = ColorPalettes.PinkColorPalette,
),
shapes = Shapes.DEFAULT_ROUNDED_SHAPE
)
)
registerThemeModel(
ThemeModelKey.of("Blue"), ThemeModel(
colorPalette = ColorPalette(
lightModeColors = ColorPalettes.BlueColorPalette,
darkModeColors = ColorPalettes.BlueColorPalette,
)
)
)
}
다중 테마 추가하기
다중 테마는 아래와 같이 Map<ThemeModelKey, ThemeModel> 의 형태로 추가한다.
DynamicThemeService.get().apply {
registerThemeModels(ThemeModels.getSupportedThemeModels())
}
fun getSupportedThemeModels(): Map<ThemeModelKey, ThemeModel> = mapOf(
THEME_MODEL_KEY_DEFAULT to THEME_MODEL_DEFAULT,
THEME_MODEL_KEY_BLACK to THEME_MODEL_BLACK,
THEME_MODEL_KEY_PINK to THEME_MODEL_PINK,
THEME_MODEL_KEY_BLUE to THEME_MODEL_BLUE,
THEME_MODEL_KEY_WHITE to THEME_MODEL_WHITE
)
기본 테마 변경하기
ThemeModelKey.Default를 사용해 기본 테마 변경이 가능하다.
DynamicThemeService.get().apply {
registerThemeModel(
ThemeModelKey.Default, ThemeModel(
colorPalette = ColorPalette(
lightModeColors = ColorPalettes.BlueColorPalette,
darkModeColors = ColorPalettes.BlueColorPalette,
)
)
)
}
추가된 테마 가져오기
하나의 테마 가져오기
ThemeModelKey에 해당하는 테마를 가져오기 위해서는 getThemeModel을 사용하면 된다.
DynamicThemeService.get().getThemeModel(themeModelKey)
추가된 모든 테마 가져오기
추가된 모든 테마를 가져오기 위해서는 getRegisteredThemes()를 사용하면 된다.
val registeredThemes: Map<ThemeModelKey, ThemeModel> = DynamicThemeService.get().getRegisteredThemes()
테마 설정하기
등록된 테마로 테마를 설정하려면 setCurrentTheme(themeModelKey)를 사용하면 된다.
suspend fun setCurrentTheme(themeModelKey: ThemeModelKey){
DynamicThemeService.get().setCurrentTheme(themeModelKey)
}
설정된 테마 가져오기
현재 설정된 테마를 가져오려면 setCurrentThemeModel() 을 사용하면 된다.
val currentThemeModel: ThemeModel = async { DynamicThemeService.get().getCurrentThemeModel() }.await()
현재 설정된 테마는 Flow 형태로 반응형으로 가져올 수도 있다.
val currentThemeModelFlow: Flow<ThemeModel> = DynamicThemeService.get().currentThemeModel
설정된 테마로 Composable 에 Theme 제공하기
설정된 테마로 테마를 제공하려면 아래와 같이 ProvidesTheme을 사용하면 된다.
DynamicThemeService.get().ProvidesTheme {
// Write Composable Functions
}
특정 ThemeModel로 Composable에 테마 제공하기
설정된 테마가 아닌 다른 ThemeModel로 테마를 제공하려면 아래와 같이 쓰면 된다.
DynamicThemeService.get().ProvidesTheme(themeModel) {
// Write Composable Functions
}
테마 값들 Composable에 적용하기
Color 적용하기
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background)
)
Typography 설정 적용하기
Text(
text = "Themes",
style = MaterialTheme.typography.h5
)
Shape 적용하기
Box(
modifier = Modifier
.fillMaxSize()
.clip(MaterialTheme.shapes.medium)
)