본문 바로가기

프로그램/Android

캠월드 개발일지 2(디자인가이드 적용)

캠월드 멀티모듈에 이어서 본격 개발을 진행하기전 디자인 가이드 적용을 선행해야 할거같다.

 

전체적인 색상과 텍스트 스타일, 버튼타입, 리스트타입, 카드타입등등 테마와 공통 컴포넌트 작업을 먼저 진행하였다.

(아래는 디자인된 가이드)

 

 

 

 

먼저 색상을 작업 진행하였고 상황, 상태별 색상을 담기위한 DataClass를 생성하였으며, 

data class ThemeColor(
    val primary: Color = Color.Unspecified,
    val primaryHover: Color = Color.Unspecified,
    val primaryActive : Color = Color.Unspecified,
}

 

 

Light  Color 와 Dark Color를 각각 정의를 아래와 같이 진행하고 staticCompositionLocalOf 을 사용하여 색상값을 공유하여 사용할수 있도록 한다.

 

staticCompositionLocalOf이란 CompositionLocal을 먼저 알아야하는데 CompositionLocal란 상위에서 하위로데이터를 공유하여야할때 변수로 함수, 클래스, 마다 하나씩 밑으로 계속 전달 할수 없으므로, 전역으로 값을 공유하도록 제공하기 위한 함수이다.

staticCompositionLocalOfUI 테마, 색상, 폰트  변경 빈도가 적은 값을 공유할때 사용하기 적합한 함수다.(조금더 상세한 설명)

 

val LocalLightColors = ThemeColor(
    primary = Color(0xFF1a73e8),
    primaryHover = Color(0xFF1765cc),
    primaryActive = Color(0xFF185abc)
}

val LocalDarkColors = ThemeColor(
    primary = Color(0xFF8ab4f8),
    primaryHover = Color(0xFFaecbfa),
    primaryActive = Color(0xFFc2e7ff)
}

val LocalColors = staticCompositionLocalOf { ThemeColor() }

 

위와 같이 설정하고 CompositionLocalProvider로 적용해주면 다른 곳에서 사용이 가능하다

val colorScheme = if(darkTheme) DarkColorScheme else LightColorScheme
val localColor = if(darkTheme) LocalDarkColors else LocalLightColors

CompositionLocalProvider(
    LocalColors provides localColor
) {
    MaterialTheme(
        colorScheme = colorScheme,
        typography = CampTypography,
        content = content
    )
}

 

작업한 색상을 활용하여 상황별 버튼을 작업하였다.  컴포즈에서는 xml 작업이 아닌 다른형태로 버튼 press 작업을 진행하여야 했다.

 

MutableInteractionSource를 사용하여 UI 상태를 추적하여 작업을 진행하여야한다. press 이외에 다른 이벤트도 추적이 가능하며 아래와 같이 사용 가능하다.

val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()

 


나머지  다른 이벤트 추적이 가능하다

 

위의 MutableInteractionSourceCompositionLocalProvider 적용하여 아래와 같이 2가지 버튼을 만들었다.

@Composable
fun CampPrimaryButton(
    text : String,
    onClick : () -> Unit,
    modifier: Modifier = Modifier,
    isEnable : Boolean = true,
    contentPaddingValues: PaddingValues = ButtonDefaults.ContentPadding,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isPressed by interactionSource.collectIsPressedAsState()

    Button(
        onClick = onClick,
        modifier = modifier,
        enabled = isEnable,
        colors = if(isPressed) {
            ButtonDefaults.buttonColors(
                containerColor = LocalColors.current.primaryActive,
                contentColor = LocalColors.current.primaryActive,
                disabledContentColor = LocalColors.current.primaryDisabled,
                disabledContainerColor = LocalColors.current.primaryDisabled
            )
        } else {
            ButtonDefaults.buttonColors(
                containerColor = LocalColors.current.primary,
                contentColor = LocalColors.current.primary,
                disabledContentColor = LocalColors.current.primaryDisabled,
                disabledContainerColor = LocalColors.current.primaryDisabled
            )
        },
        shape = RoundedCornerShape(8.dp),
        contentPadding = contentPaddingValues,
    ) {
        Text(
            text = text,
            color = LocalColors.current.textPrimary,
            style = MaterialTheme.typography.titleMedium
        )
    }
}

@Composable
fun CampOutlineButton(
    text : String,
    onClick : () -> Unit,
    modifier: Modifier = Modifier,
    isEnable : Boolean = true,
    contentPaddingValues: PaddingValues = ButtonDefaults.ContentPadding,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isPressed by interactionSource.collectIsPressedAsState()

    Button(
        onClick = onClick,
        modifier = modifier.border(
            width = 2.dp,
            if (isEnable) LocalColors.current.primary else LocalColors.current.primaryDisabled,
            shape = RoundedCornerShape(8.dp)
        ),
        enabled = isEnable,
        colors = if(isPressed) {
            ButtonDefaults.buttonColors(
                containerColor = LocalColors.current.backgroundAlt,
                contentColor = LocalColors.current.backgroundAlt,
                disabledContentColor = LocalColors.current.primaryDisabled,
                disabledContainerColor = LocalColors.current.primaryDisabled
            )
        } else {
            ButtonDefaults.buttonColors(
                containerColor = LocalColors.current.background,
                contentColor = LocalColors.current.background,
                disabledContentColor = LocalColors.current.primaryDisabled,
                disabledContainerColor = LocalColors.current.primaryDisabled
            )
        },
        contentPadding = contentPaddingValues,
    ) {
        Text(
            text = text,
            color = LocalColors.current.textPrimary,
            style = MaterialTheme.typography.titleMedium
        )
    }
}

 

그리고 Preview로 먼저 UI를 체크해봐야하는데 기본으로 적용하는 Preview를 사용할 경우 하나의 테마 형태밖에 볼수 없어서 2가지로 해야하며, 여러모로 귀찮아져 annotation을 추가하여 2가지 상태를 한번에 본다면 조금 더 생산성이 높아진다.

 

1. ThemePreviews annotation에 Light/Dark Theme를 정의

@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light theme")
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark theme")
annotation class ThemePreviews

 

2. ThemePreviews 를 사용하여 Preview 함수를 만든다.

@ThemePreviews
@Composable
fun PreviewCampPrimaryButton() {
    Camp_worldTheme {
        BackgroundDefault(
            modifier = Modifier
                .width(160.dp)
                .height(50.dp)
        ) {
            CampPrimaryButton(
                text = "테스트",
                modifier = Modifier.fillMaxWidth().width(150.dp).height(50.dp),
                onClick = { },
                isEnable = true
            )
        }
    }
}

@ThemePreviews
@Composable
fun PreviewCampOutlineButton() {
    Camp_worldTheme {
        BackgroundDefault(
            modifier = Modifier
                .width(160.dp)
                .height(50.dp)
        ) {
            CampOutlineButton(
                text = "테스트",
                modifier = Modifier.fillMaxWidth().width(150.dp).height(50.dp),
                onClick = { },
                isEnable = true
            )
        }
    }
}

 

위와 같이 만든다면 Preview에 2가지가 다 보인다.

 

아주 나이스하다.

 

TextStyle은 아래와 같이 Typography를 사용하여 적용한다.

internal val CampTypography = Typography(
    headlineLarge = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Bold,
        fontSize = 32.sp,
        lineHeight = 48.sp
    ),

    headlineMedium = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Bold,
        fontSize = 24.sp,
        lineHeight = 36.sp
    ),

    headlineSmall = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Bold,
        fontSize = 16.sp,
        lineHeight = 30.sp
    ),
}

 

이렇게 버튼 Chip, List, Card등 나머지를 빨리 작업해야겠다...

반응형