2024. 1. 29. 23:02ㆍIT/Android
<미리 보기>
<소스 코드>
<정리>
Material Design - 앱의 전체적인 테마를 지정할 수 있는 디자인 시스템.
Theme.kt 파일 - 색상, 서체, 도형 같은 전반적인 테마를 정의한 파일로, 컴포저블 함수의 선언을 통해 각각의 요소를 정의하여 사용할 수 있다. 여기서 사용된 테마 색상 구성은 Material 테마 빌더 사이트를 이용하면 쉽게 구현할 수 있다.
테마 색상의 역할
primary - UI 주요 구성 요소.
secondary - UI에서 눈에 덜 띄는 구성 요소.
tertiary - 기본 색상과 보조 색상의 균형을 맞추는 색상으로 입력란 같은 특정 요소.
on - 텍스트, 아이콘, 획 같은 요소.
@Composable
fun WoofTheme(
darkTheme: Boolean = isSystemInDarkTheme(), //기기의 시스템 설정이 다크 모드라면 true, 그렇지 않으면 false를 반환.
dynamicColor: Boolean = false, //Android 12 이후부터 사용 가능하고, 동적 색상 테마를 지원한다면 true, 그렇지 않으면 false를 설정.
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { //동적 색상 테마를 지원하고 Android 12 이후 버전이라면,
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, //위에서 정의한 테마.
typography = Typography, //Type.kt에서 정의한 서체.
shapes = Shapes, //Shape.ket에서 정의한 도형.
content = content
)
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WoofTheme { //Theme.kt에서 컴포저블 함수로 정의한 테마를 컴포지션 바깥에 선언하여 적용할 수 있다.
Surface(
modifier = Modifier.fillMaxSize()
) {
WoofApp()
}
}
}
}
}
*동적 색상 - 사용자의 선호 즉, 배경화면의 색상을 기반으로 설정되는 색상.
*Theme.kt 파일에는 다크 모드와 밝기 모드를 정의하는 변수가 선언되어 있음. 위의 코드의 DarkColors, LightColors.
ChatGPT가 알려준 ColorScheme.
1. primary: 앱의 주요 색상으로, 버튼, 툴바 등 주요 UI 요소에 사용됩니다.
2. onPrimary: primary 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
3. primaryContainer: primary 색상을 배경으로 하는 컨테이너의 색상입니다. 예를 들어, 카드의 배경색으로 사용될 수 있습니다.
4. onPrimaryContainer: primaryContainer 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
5. inversePrimary: primary 색상의 반대색입니다. 보통 다크 모드에서 사용됩니다.
6. secondary: 앱의 보조 색상으로, primary 색상과 대비를 이루며 추가적인 UI 요소에 사용됩니다.
7. onSecondary: secondary 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
8. secondaryContainer: secondary 색상을 배경으로 하는 컨테이너의 색상입니다.
9. onSecondaryContainer: secondaryContainer 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
10. tertiary: 앱의 세 번째 주요 색상으로, 추가적인 강조 요소에 사용됩니다.
11. onTertiary: tertiary 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
12. tertiaryContainer: tertiary 색상을 배경으로 하는 컨테이너의 색상입니다.
13. onTertiaryContainer: tertiaryContainer 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
14. background: 앱의 기본 배경 색상입니다.
15. onBackground: background 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
16. surface: 카드와 같은 표면 요소의 색상입니다.
17. onSurface: surface 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
18. surfaceVariant: 기본 surface 색상의 변형으로, 더 많은 변화를 줄 때 사용됩니다.
19. onSurfaceVariant: surfaceVariant 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
20. surfaceTint: 표면 요소의 색조를 조절하는 색상입니다.
21. inverseSurface: 다크 모드에서의 반전된 표면 색상입니다.
22. inverseOnSurface: inverseSurface 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
23. error: 오류 상태를 표시하는 색상입니다.
24. onError: error 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
25. errorContainer: 오류 상태를 표시하는 컨테이너의 색상입니다.
26. onErrorContainer: errorContainer 색상이 적용된 요소 위에 표시되는 텍스트나 아이콘의 색상입니다.
27. outline: 요소의 테두리 색상입니다.
28. outlineVariant: outline 색상의 변형으로, 추가적인 강조를 위해 사용됩니다.
29. scrim: 배경을 흐리게 하는 데 사용되는 색상입니다.
30. surfaceBright: 밝은 표면 색상입니다.
31. surfaceContainer: 컨테이너 표면 색상입니다.
32. surfaceContainerHigh: 높은 강조를 위한 컨테이너 표면 색상입니다.
33. surfaceContainerHighest: 가장 높은 강조를 위한 컨테이너 표면 색상입니다.
34. surfaceContainerLow: 낮은 강조를 위한 컨테이너 표면 색상입니다.
35. surfaceContainerLowest: 가장 낮은 강조를 위한 컨테이너 표면 색상입니다.
36. surfaceDim: 어두운 표면 색상입니다.
Color.kt 파일 - Theme.kt 파일에서 사용되는 색상을 정의한다.
val md_theme_light_primary = Color(0xFF006C4C)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFF89F8C7)
...
*색상 표현인 #FF006C4C는 앞에서 부터 16진수 두 자리씩 투명도, Red, Green, Blue를 의미한다.
dimens.kt 파일 - 텍스트, 레이아웃에 사용되는 dp, sp 같은 크기를 정의하여 사용할 수 있다.
<resources>
<dimen name="padding_small">8dp</dimen>
<dimen name="padding_medium">16dp</dimen>
<dimen name="image_size">64dp</dimen>
</resources>
Row, Column과 같은 컨테이너들을 Card로 감싸 레이아웃 구성 요소 중 일부를 배경 색상과 구분할 수 있다.
Card(modifier = modifier) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(id = R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
}
}
Shape.kt 파일 - 도형을 정의하는 데 사용되고, 각각의 컴포저블은 구성 요소 크기 7종류 중 하나에 포함된다.
val Shapes = Shapes(
small = RoundedCornerShape(50.dp),
medium = RoundedCornerShape(bottomStart = 16.dp, topEnd = 16.dp) //Card는 medium에 포함된다.
)
*RoundedCornerShape()는 모서리를 원형으로 만드는 데 사용.
*Image같이 어떤 구성 요소 크기에도 포함되지 않는 컴포저블들은 명시적으로 적어줘서 적용할 수 있다.
ex) MaterialTheme.shapes.small
Type.kt 파일 - 서체 스타일을 정의하는 데 사용되고, 구성 요소 크기에는 총 15가지가 존재한다. 특정 폰트를 다운로드하여 사용할 수 있고, 해당 폰트 파일은 res/font 디렉터리에 저장되어야 한다.
val Nomosans = FontFamily(
Font(R.font.notosans_regular),
Font(R.font.notosans_bold, FontWeight.Bold)
)
val Typography = Typography(
displayLarge = TextStyle(
fontFamily = Nomosans,
fontWeight = FontWeight.Normal,
fontSize = 36.sp
),
labelSmall = TextStyle(
fontFamily = Nomosans,
fontWeight = FontWeight.Bold,
fontSize = 14.sp
)
)
*폰트 파일의 이름은 소문자와 언더바로 구성되어야 한다.
*해당 속성의 적용은 텍스트 style 속성에 명시적으로 적어주어야 한다.
ex) MaterialTheme.typography.displayLarge
Scaffold - 일반적인 앱 구조를 생성하는데 유용한 컴포저블로, 상단바, 하단바, 측면 내비게이션, 부착된 아이콘 버튼 등 레이아웃을 편리하게 정의할 수 있다.
@Composable
fun WoofApp() {
Scaffold(
topBar = {
WoofTopAppBar()
}
) { it ->
LazyColumn(contentPadding = it) {
items(dogs) {
DogItem(
dog = it,
modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
)
}
}
}
}
*Scaffold 후행 람다 content의 파라미터 입력 값인 it은 원래 Scaffold 파라미터 contentWindowInsets로부터 전달되는 WindowInsets 타입 값인데, 이 값은 내부적으로 자동 처리되어 내부에 선언된 컴포저블의 contentPadding값으로 적용하면 레이아웃의 위치가 겹치지 않고 올바르게 정렬된다.
Icons - 기본 아이콘들은 디폴트로 제공되지만 다양한 형태의 아이콘들을 사용하기 위해선 종속 항목 추가가 요구된다.
IconButton(
onClick = onClick,
modifier = modifier
) {
Icon(
imageVector = if(expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
contentDescription = stringResource(R.string.expand_button_content_description),
tint = MaterialTheme.colorScheme.secondary
)
}
*아이콘 색상은 tint 속성을 통해 적용할 수 있다.
다른 컴포저블 사이에 Spacer에만 가중치를 적용하여 상위 레이아웃의 빈 공간을 모두 차지할 수 있다.
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
var expanded by remember { mutableStateOf(false) }
Card(modifier = modifier) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
Spacer(modifier = Modifier.weight(1f))
DogItemButton(
expanded = expanded,
onClick = { expanded = expanded.not() },
)
}
}
}
animateContentSize - 크기를 변경하는 애니메이션을 적용할 때 사용된다.
animateContextSize.animateSpec - 애니메이션 사양을 맞춤 설정하기 위한 파라미터.
spring() - 시작 값과 끝 값 사이에 물리학 기반 애니메이션을 만들어 AnimateSpec타입을 반환한다.
spring.dampingRatio - 탄성 값.
spring.stiffness - 속도.
animateColorAsState - 최종 값(targetValue)만 제공하면 현재 값에서 지정된 값으로 애니메이션을 실행한다.
@Composable
fun DogItem(
dog: Dog,
modifier: Modifier = Modifier
) {
var expanded by remember { mutableStateOf(false) }
val color by animateColorAsState(
targetValue = if (expanded) MaterialTheme.colorScheme.tertiaryContainer
else MaterialTheme.colorScheme.primaryContainer,
)
Card(modifier = modifier) {
Column(
modifier = Modifier
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessMedium
)
)
.background(color = color)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_small))
) {
DogIcon(dog.imageResourceId)
DogInformation(dog.name, dog.age)
Spacer(modifier = Modifier.weight(1f))
DogItemButton(
expanded = expanded,
onClick = { expanded = expanded.not() },
)
}
if(expanded) {
DogHobby(
dogHobby = dog.hobbies,
modifier = Modifier.padding(
start = dimensionResource(R.dimen.padding_medium),
top = dimensionResource(R.dimen.padding_small),
end = dimensionResource(R.dimen.padding_medium),
bottom = dimensionResource(R.dimen.padding_medium)
)
)
}
}
}
}
'IT > Android' 카테고리의 다른 글
Jetpack Compose - Activity lifecycle, remember, rememberSaveable (0) | 2024.02.01 |
---|---|
Jetpack Compose - Material Design Practice (0) | 2024.01.30 |
Jetpack Compose - Grid Practice (0) | 2024.01.26 |
Jetpack Compose - LazyColumn, Legacy icons, Adaptive icons (0) | 2024.01.24 |
Jetpack Compose - Layout Practice (0) | 2024.01.24 |