일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- Flutter
- scroll
- binding
- tabbar
- Dialog
- drift
- Button
- android
- Kotlin
- ScrollView
- DART
- Compose
- textfield
- activity
- 계측
- 테스트
- LifeCycle
- appbar
- viewmodel
- CustomScrollView
- intent
- 안드로이드
- TEST
- 앱
- Navigation
- livedata
- Coroutines
- textview
- 앱바
- data
- Today
- Total
Study Record
[Android] Compose 테마 본문
😶 Material 테마 빌더를 사용하여 색 구성표 만들기
Material 테마 빌더를 이용하면 쉽게 색 구성표를 얻을 수 있다.
Material 테마 빌더로 이동하면 핵심 색상들이 보이는데 색상을 클릭하면 직접 수정할 수 있다. 주 색상(Primary)을 수정하면 주 색상에 맞게 Secondary, Tertiary, Neutral 색상도 변경된다.
설정한 핵심 색상들에 따라 컴포저블의 색상들이 달라진다.
밝은 테마와 어두운 테마는 다음 아이콘을 누르면 변경할 수 있다.
밝은 테마와 어두운 테마의 색상들을 전체 구성표로도 볼 수 있다.
어두운 테마의 예시이다.
각 색상들에 대한 설명은 다음과 같다.
primary, UI 의 주요 구성요소에 사용된다.
secondary, UI 에서 눈에 덜 띄는 구성요소에 사용된다.
tertiary, primary 색상과 second 색상의 균형을 맞추거나 입력란과 같은 특정 요소로 관심을 유됴하는데 사용된다.
on 색상요소는 팔레트 색상 위에 나타나며, 주로 텍스트, 아이콘, 획에 적용된다.
Compose 앱을 시작하면 자동으로 ui/theme 디렉터리의 4개의 파일이 생성되어있다. 각각 Theme.kt, Shape.kt, Color.kt, Type.kt 이다. 그 중 Color.kt 파일은 색상 정보를 저장하는 파일이다. 나머지 3개 파일에 대해서 알아보면 다음과 같다.
😶 테마 파일(Theme.kt)
Material 테마 빌더의 색상 구성표와 같은 색상 스키마를 제공한다. Color.kt 파일에 색상 구성표에 따라 색상 변수를 미리 저장한 뒤,
val md_theme_light_primary = Color(0xFF006C4C)
/* */
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFF6CDBAC)
/* */
val md_theme_dark_scrim = Color(0xFF000000)
만들어진 테마 파일의 테마를 설정할 수 있다. 밝은 테마는 lightColorScheme, 어둔 테마는 darkColorScheme 로 지정할 수 있다. 테마를 적용할 컴포저블의 이름은 예시 프로젝트 이름과 연관지어 WoofTheme 이다.
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
@Composable
fun WoofTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColors
else -> LightColors
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
shapes = Shapes,
typography = Typography,
content = content
)
}
여기서 색상 테마를 저장하는 colorScheme 을 살펴보면, 동적 색상을 사용하지 않으면 현재 darkTheme 인지 아닌지에 따라 다른 색상 스키마를 저장한다.
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColors
else -> LightColors
}
레이아웃의 루트 컴포저블을 테마 컴포저블로 하면 그 하위 항목들은 자동으로 테마의 영향을 받는다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WoofTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize()
) {
WoofApp()
}
}
}
}
}
동적 색상
동적 색상은 사용자의 배경화면을 기반으로 테마를 생성하는 Material3 의 새로운 기능으로 사용가자 선택한 색상으로 특정 테마가 적용된다. 단, 동적 테마 설정은 Android 12 이상 버전을 실행하는 특정 기기에만 적용된다.
테마를 선택할 때 동적 테마를 사용하려면 위에서 생성한 테마 컴포저블의 dynamicColor 를 true 로 설정하면 된다.
@Composable
fun WoofTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
)
😶 도형 파일(Shape.kt)
도형 파일에 앱에서 활용되는 여러가지 도형을 저장하고 불러올 수 있다. Shapes 클래스의 일부는 다음과 같다. extraSamll 부터 extraLarge 까지 도형을 설정할 수 있다.
@Immutable
class Shapes(
// Shapes None and Full are omitted as None is a RectangleShape and Full is a CircleShape.
val extraSmall: CornerBasedShape = ShapeDefaults.ExtraSmall,
val small: CornerBasedShape = ShapeDefaults.Small,
val medium: CornerBasedShape = ShapeDefaults.Medium,
val large: CornerBasedShape = ShapeDefaults.Large,
val extraLarge: CornerBasedShape = ShapeDefaults.ExtraLarge,
)
예시 도형 파일이다.(Shape.kt)
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(50.dp),
medium = RoundedCornerShape(bottomStart = 16.dp, topEnd = 16.dp)
)
테마 파일(Theme.kt) 에서 MaterialTheme 의 shapes 매개변수에 커스텀한 도형 변수로 설정하면 앱 프로젝트에서 사용할 수 있다.
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
사용 예시)
Card 컴포저블은 기본 shapes 의 medium 을 기본으로 사용한다. 따라서 Shape.kt 파일의 medium 을 수정하면 Card 의 모양이 바뀔 것이다.
직접 참조하는 방법으로도 사용할 수 있다. 직접 MaterialTeme.shapes.small 을 불러온다.
Image(
modifier = modifier
.size(dimensionResource(id = R.dimen.image_size))
.padding(dimensionResource(id = R.dimen.padding_small))
.clip(MaterialTheme.shapes.small),
contentScale = ContentScale.Crop,
)
😶 타입 파일(Type.kt)
타입 파일에서는 서체를 추가할 수 있다. Material Design 서체 스케일은 서체 시스템에서 지원하는 15가지 글꼴 스타일이 있다. 이름 지정과 그룹화는 표시, 헤드라인, 제목, 본문, 라벨로 간소화되었고 각각 대, 중, 소 크기가 있다. 이러한 옵션은 앱을 맞춤설정하려는 경우에만 사용하면 된다.
무료 글꼴은 구글 폰트에서 다운받을 수 있다.
다운받은 글꼴을 사용하려면 res/ 디렉터리에 타입이 font 인 Android ResourceDirectory 를 추가해야 한다. 추가하면 res/font 디렉터리가 생긴다. 이 디렉터리 안에 다운받은 폰트의 .ttf 파일들을 넣는다.
Type.kt 파일로 돌아와 FontFamily 메서드로 글골을 추가하고 Typography 에 fontFamily 를설정해준다.
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp
val AbrilFatface = FontFamily(
Font(R.font.abril_fatface_regular)
)
val Montserrat = FontFamily(
Font(R.font.montserrat_regular),
Font(R.font.montserrat_bold, FontWeight.Bold)
)
val Typography = Typography(
displayLarge = TextStyle(
fontFamily = AbrilFatface,
fontWeight = FontWeight.Normal,
fontSize = 36.sp
),
displayMedium = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
),
labelSmall = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Bold,
fontSize = 14.sp
),
bodyLarge = TextStyle(
fontFamily = Montserrat,
fontWeight = FontWeight.Normal,
fontSize = 14.sp
)
)
Theme.kt 의 MaterialTheme 의 typography 매개변수에 Type.kt 파일에서 정의한 Typography 를 설정하면 끝이다.
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
사용 예시) 다음 예시와 같이 MaterialTheme.typography.displayMedium 와 같이 직접 참조하여 사용한다.
@Composable
fun DogInformation(
@StringRes dogName: Int,
dogAge: Int,
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
Text(
text = stringResource(dogName),
style = MaterialTheme.typography.displayMedium,
modifier = Modifier.padding(top = dimensionResource(id = R.dimen.padding_small))
)
Text(
text = stringResource(R.string.years_old, dogAge),
style = MaterialTheme.typography.bodyLarge
)
}
}
'안드로이드 > compose' 카테고리의 다른 글
[Android] Compose 애니메이션 효과 (0) | 2023.09.06 |
---|---|
[Android] Compose Appbar (0) | 2023.09.04 |
[Android] 계측 테스트 - Compose (0) | 2023.08.31 |
[Android] Compose 기초 - State, MutableState, remember (0) | 2023.08.30 |
[Android] Compose 기초 (0) | 2023.08.29 |