App 개발 일지/Nutri Capture

Nutri Capture 프론트엔드 - Typography

interfacer_han 2024. 12. 28. 12:08

#1 일괄적 글꼴 필요

프로젝트 내에서 쓰인 글씨의 크기가 중구난방이라는 문제점이 있다. 현재는, fontSize 속성을 지닌 컴포저블별로 크기값을 일일히 할당해두었기 때문이다. 이를 전체적으로 통제할 수 있어야 한다. 크기 뿐만 아니라, 버튼에 들어가는 용도의 글꼴을 별도로 선언해두었다가 두고두고 쓰는 경우도 앞으로 생길테다.

 

#2 Typography

#2-1 머터리얼 디자인 가이드

 

Typography – Material Design 3

Learn about Material Design typography. This guide covers everything from font styles and hierarchy to line height to create user-friendly text.

m3.material.io

머터리얼 디자인 가이드에서는 일괄적인 글꼴을 제공한다. 게다가, 안드로이드 프로젝트에 기본적으로 통합되어 있다. 그래서 별도의 라이브러리 다운로드 작업없이 즉시 사용할 수 있다. 아래는 그 예시 코드다.

 

#2-2 Typography 활용 예 1

Text(
    text = "Sample Top Bar Text",
    style = MaterialTheme.typography.headlineLarge
)

style 속성에 MaterialTheme.typography.headlineLarge (이하 headlineLarge)라는 글꼴을 대입한다. 이 작업은 마치 HTML 태그에 가하는 css 스타일과 비슷하다. 이제부터, 이 Text 컴포저블은 headlineLarge에서 선언한 모양이 된다.

 

#2-3 Typography 활용 예 2

Text(
    text = "Sample Top Bar Text",
    fontSize = 20.sp,
    style = MaterialTheme.typography.headlineLarge
)

이런 경우에는 1차적으로 headlineLarge에서 선언한 모양을 적용한 후, 2차적으로 fontSize를 적용한다. 즉, fontSize 속성이 우선한다. 전역적인 설정과 지역적인 설정이 충돌했을 때는 이와 같이 지역적인 설정을 우선하는 게 당연하겠지만 말이다.

 

#2-4 ui.theme.Typography.kt 

// package com.example.nutri_capture_new.ui.theme

import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

// Set of Material typography styles to start with
val Typography = Typography(
    bodyLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp,
        lineHeight = 24.sp,
        letterSpacing = 0.5.sp
    )
    /* Other default text styles to override
    titleLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 22.sp,
        lineHeight = 28.sp,
        letterSpacing = 0.sp
    ),
    labelSmall = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Medium,
        fontSize = 11.sp,
        lineHeight = 16.sp,
        letterSpacing = 0.5.sp
    )
    */
)

Jetpack Compose 프로젝트를 생성하면 기본으로 들어있는 코드다. 위치는 ui.theme 패키지의 Type.kt 파일이다. Typography 클래스는 bodyLarge, titleLarge, labelSmall, headlineMedium, displaySmall 등등의 수 많은 머터리얼 디자인 글꼴을 생성자로서 받는다. 생성자를 넣지 않으면 각 글꼴은 기본값으로 적용된다. 즉, 머터리얼 디자인 글꼴을 그대로 사용하되 내가 원하는 경우 함수를 오버라이드하듯 커스텀할 수 있다는 얘기다.

 

내 프로젝트의 경우 아직 글꼴을 다듬는 단계가 아니기에, 우선은 Typography의 기본값을 그대로 사용하겠다. 또 기본값을 사용하는 게 나쁜 것도 아니다. 머터리얼 디자인 권장 사항을 그대로 따르는 것이기 때문이다.

 

#2-5 참고: Typography 클래스

@Immutable
class Typography(
    val displayLarge: TextStyle = TypographyTokens.DisplayLarge,
    val displayMedium: TextStyle = TypographyTokens.DisplayMedium,
    val displaySmall: TextStyle = TypographyTokens.DisplaySmall,
    val headlineLarge: TextStyle = TypographyTokens.HeadlineLarge,
    val headlineMedium: TextStyle = TypographyTokens.HeadlineMedium,
    val headlineSmall: TextStyle = TypographyTokens.HeadlineSmall,
    val titleLarge: TextStyle = TypographyTokens.TitleLarge,
    val titleMedium: TextStyle = TypographyTokens.TitleMedium,
    val titleSmall: TextStyle = TypographyTokens.TitleSmall,
    val bodyLarge: TextStyle = TypographyTokens.BodyLarge,
    val bodyMedium: TextStyle = TypographyTokens.BodyMedium,
    val bodySmall: TextStyle = TypographyTokens.BodySmall,
    val labelLarge: TextStyle = TypographyTokens.LabelLarge,
    val labelMedium: TextStyle = TypographyTokens.LabelMedium,
    val labelSmall: TextStyle = TypographyTokens.LabelSmall,
) {
    ...
}

 

#3 코드

#2-2의 코드대로 기존 프로젝트를 수정한다.

 

#3-1 MainActivity.kt

...
import androidx.compose.material3.MaterialTheme
...

class MainActivity : ComponentActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        setContent {
            NutricapturenewTheme {
                ...

                Scaffold(
                    ...
                    topBar = {
                        TopAppBar(
                            title = {
                                Text(
                                    text = "Sample Top Bar Text",
                                    style = MaterialTheme.typography.headlineLarge
                                )
                            }
                        )
                    },
                    bottomBar = {
                        ...
                    },
                    snackbarHost = {
                        SnackbarHost(
                            ...
                            snackbar = { ...
                                Snackbar(
                                    ...
                                ) {
                                    Text(
                                        text = snackbarData.visuals.message,
                                        style = MaterialTheme.typography.bodyLarge,
                                    )
                                }
                            }
                        )
                    }
                ) { ...
                    ...
                }
            }
        }
    }

    ...
}

...

@Composable
fun SampleContent(
    ...
) {
    Box(
        ...
    ) {
        Button(
            ...
        ) {
            Text(
                text = text,
                style = MaterialTheme.typography.headlineMedium
            )
        }
    }
}

 

#3-2 NutrientScreen.kt

...
import androidx.compose.material3.MaterialTheme
...

@Composable
fun NutrientScreen(
    ...
) {
    ...

    LazyColumn(
        ...
    ) {
        item {
            FilledTonalButton(
                ...
            ) {
                Text(
                    text = "+",
                    style = MaterialTheme.typography.headlineMedium
                )
            }
        }

        ...
        items(...) { ...
            ...

            Card(
                ...
            ) {
                Box(
                    ...
                ) {
                    Column(
                        ...
                    ) {
                        Text(
                            text = DateFormatter.formatDateForNutrientScreen(dayMeal.date) + " " + dayMeal.time,
                            modifier = Modifier.fillMaxWidth(),
                            textAlign = TextAlign.End,
                            style = MaterialTheme.typography.labelLarge
                        )

                        Text(
                            text = "mealId: ${dayMeal.mealId}",
                            modifier = Modifier.fillMaxWidth(),
                            textAlign = TextAlign.Center,
                            style = MaterialTheme.typography.headlineMedium
                        )
                    }

                    ...
                }
            }
        }
    }
}

 

#4 완성된 앱

#4-1 스크린샷

겉모습은 다를 게 거의 없다. 하지만, 내부 코드는 더 깔끔해졌다. 이 시점 이후로 글씨 크기를 지역적으로 할당하는 일이 없도록 해야겠다.

 

#4-2 이 게시글 시점의 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com

 

#4-3 본 프로젝트의 가장 최신 Commit

 

GitHub - Kanmanemone/nutri-capture-new

Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.

github.com