이전 단계에서는 앱의 색상, 서체 스타일 및 모양을 설정하기 위해 고유한 테마를 만드는 방법을 살펴보았다. 모든 Material 컴포넌트는 이러한 커스터마이징을 창조적으로 사용한다. 예를 들어 FloatingActionButton 컴포저블은 기본적으로 테마의 보조(secondary) 색상을 사용하지만, 이 매개변수에 다른 값을 지정하여 다른 색상을 설정할 수 있다.

@Composable
fun FloatingActionButton(
  backgroundColor: Color = MaterialTheme.colors.secondary,
  ...
) {

Note: 머티리얼 컴포넌트에서 사용하는 테마 색상을 확인하는 것은 컴포넌트의 기본 매개변수로 지정되기 때문에 View와 비교하면 Compose에서는 크게 단순하다고 볼 수 있다. Component’s declaration으로 이동하여 이를 쉽게 확인할 수 있다.

항상 기본 설정을 사용하고 싶지는 않을것이다. 이 섹션에서는 앱에서 색상 작업하는 방법을 보여준다.

Raw 색상

앞서 보았듯, 컴포즈는 Color 클래스를 제공한다. 이것들을 로컬로 생성하고 객체등에 보관할 수 있다.

Surface(color = Color.LightGray) {
  Text(
    text = "Hard coded colors don't respond to theme changes :("
    textColor = Color(0xffff00ff)
  )
}

Note:색상 정의를 정적으로 선언할 때는 라이트/다크 테마와 같은 다양한 테마를 지원하는 것이 더 어렵거나 불가능하므로 주의하자. 그러나 중요한 브랜드 색상과 같은 라이트/다크 테마에서 동일한 색상을 사용해야 하는 일부 상황에 대해서는 머티리얼 가이드에서 설명해주고 있다.

Color에는 다른 투명도/빨간색/녹색/파랑색 값으로 새 색상을 생성할 수 있는 copy와 같은 유용한 메서드들이 있다.

테마 색상

보다 유연한 접근 방식은 테마로부터 색상을 가져오는 것이다.

Surface(color = MaterialTheme.colors.primary)

여기에서는 colors 속성이 MaterialTheme 구성 요소에 설정된 Colors를 반환하는 MaterialTheme 객체를 사용하고 있다. 즉, 테마에 다양한 컬러셋을 제공하기만 하면, 다양한 모양과 느낌을 지원할 수 있으며, 애플리케이션 코드를 건드릴 필요가 없다. 예를 들어 AppBar는 기본(primary) 색상을 사용하고 화면 배경은 surface를 사용한다. 테마 색상 변경은 이러한 컴포저블에 반영됩니다.

e042098f0d035638.png
3dfb0d1134625efc.png
우리 테마의 각 색상은 Color 인스턴스들로, 우리는 또한 쉽게 copy 메서드를 사용하여 색상을 파생시킬 수 있다.
val derivedColor = MaterialTheme.colors.onSurface.copy(alpha = 0.1f)

여기서는 불투명도가 10%인 onSurface 색상의 복사본을 만들고 있다. 이 접근 방식은 하드 코딩된 정적인 색상이 아닌 다른 테마에서 색상이 작동하도록 한다.

Surface & Content 색상

많은 컴포넌트들이 color 및 contentColors를 쌍으로 수용한다.

Surface(
  color: Color = MaterialTheme.colors.surface,
  contentColor: Color = contentColorFor(color),
  ...

TopAppBar(
  backgroundColor: Color = MaterialTheme.colors.primarySurface,
  contentColor: Color = contentColorFor(backgroundColor),
  ...

이를 통해 컴포저블의 색상을 설정할 수 있을 뿐만 아니라 “콘텐츠”, 즉 그 안에 있는 컴포저블의 기본 색상을 제공할 수 있다. 많은 컴포저블들은 기본적으로 이 콘텐츠 색상을 사용한다(예를들면 Text 색상 또는 Icon tint). contentColorFor 메소드는 모든 테마 색상에 대해 적절한 “on” 색상을 가져온다(예를 들어, primary 배경색을 지정하면, onPrimary를 content color로써 반환한다.) 테마가 아닌 배경색을 설정했다면, 감각적인 콘텐츠 색상을 직접 제공해야 한다.

Surface(color = MaterialTheme.colors.primary) {
  Text(...) // default text color is 'onPrimary'
}
Surface(color = MaterialTheme.colors.error) {
  Icon(...) // default tint is 'onError'
}

LocalContentColor CompositionLocal을 사용하여 현재 배경과 대조되는 색상을 가져올 수 있다.

BottomNavigationItem(
  unselectedContentColor = LocalContentColor.current ...

어떠한 요소의 색상을 설정할 때, 적절한 콘텐츠 색상 CompositionLocal 값을 설정하므로 Surface를 사용하여 이 작업을 수행하는 것이 좋다. 적절한 콘텐츠 색상을 설정하지 않는 방식인, Modifier.background를 직접 호출하는 것에 유의하자.

-Row(Modifier.background(MaterialTheme.colors.primary)) {
+Surface(color = MaterialTheme.colors.primary) {
+  Row(
...

현재 Header 컴포넌트에는 항상 Color.LightGray 배경을 갖고 있다. 이것은 라이트(밝은) 테마에서 괜찮아 보이지만, 다크(어두운) 테마의 배경과 높은 대비를 보인다. 또한 특정 텍스트 색상을 지정하지 않으므로, 배경과 대조되지 않을 수 있는 현재 콘텐츠 색상을 상속한다.

f80956b5e213d88e.png

Home.kt에 있는 Header 컴포저블에서 이 부분을 수정하도록 하자. 하드코딩된 지정한 색상의 background Modifier를 제거한다. 대신에 Surface로 Text를 감싸, 테마가 제공하는 색상 및 primar에 정의된 콘텐츠 색상을 사용하도록 한다.

+ Surface(
+   color = MaterialTheme.colors.onSurface.copy(alpha = 0.1f),
+   contentColor = MaterialTheme.colors.primary,
+   modifier = modifier
+ ) {
  Text(
    text = text,
    modifier = Modifier
      .fillMaxWidth()
-     .background(Color.LightGray)
      .padding(horizontal = 16.dp, vertical = 8.dp)
  )
+ }

Content Alpha

때때로 우리는 내용을 강조하거나 또는 강조하지 않는 방법을 통해 어떠한 중요성을 전달하고, 시각적인 계층 구조를 제공한다. 머티리얼 디자인은 이러한 다양한 중요도 수준을 전달하기 위해 다양한 불투명도 레벨을 사용하기를 권장하고 있다.

Jetpack Compose는 이를 LocalContentAlpha라는 것을 통해 구현한다. CompositionLocal에 대한 값을 제공하여 계층에 대한 콘텐츠 투명도를 지정할 수 있다. 하위 컴포저블들은 이 값을 사용할 수 있다. 예를 들면 기본적인 Text 및 Icon은 조정된 LocalContentColor의 조합을 사용하여 LocalContentAlpha를 사용한다. 머티리얼 몇가지 표준 투명도 값을 정의하고 있다(high, medium, disabled). 이것들은 ContentAlpha 객체에 모델링 되어있다. MaterialTheme이 기본적으로 LocalContentAlpha의 ContentAlpha.high를 사용한다는 것을 알아두자.

// 기본적으로 Icon & Text 둘다 LocalContentColor & LocalContentAlpha 조합을 사용한다.
// 콘텐트 투명도를 다르게 하여 콘텐트를 강조시키지 않도록 한다.
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
    Text(...)
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
    Icon(...)
    Text(...)
}

이를 통해 컴포넌트의 중요성을 쉽고 일관되게 전달할 수 있다.

추천 게시물(featured post)의 정보 계층 구조를 명확히 하기 위해 콘텐츠 알파를 사용할 것이다. Home.kt의 PostMetadata 컴포저블에서 메타데이터 midium을 강조합니다.

+ CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
  Text(
    text = text,
    modifier = modifier
  )
+ }
5f24fbfac3932c26.png

다크 테마

우리가 보았듯이 컴포즈에서 다크 테마를 구현하려면, 테마를 통해 다양한 색상 세트와 쿼리 색상을 제공하기만 하면 된다. 몇 가지 예외사항이 있다.

다음과 같이 라이트 테마에서 실행중인지 확인할 수 있다.

val isLightTheme = MaterialTheme.colors.isLight

이 값은 lightColors / darkColors 빌더 함수에 의해 설정된다.

머티리얼에 다크테마에서 높은 elevation을 갖는 surface는 elevation overlays에 영향을 받는다(배경이 밝아짐). 이는 다크 컬러 팔레트를 사용할 때 자동으로 구현된다.

Surface(
  elevation = 2.dp,
  color = MaterialTheme.colors.surface, // color will be adjusted for elevation
  ...

우리는 TopAppBar와 Card 컴포넌트 사용하고 있는 우리 앱에서 이러한 것들이 자동으로 동작하는 것을 볼 수 있다. 기본적으로 4dp 및 1dp elevation이 있으므로 이 elevation을 더 잘 전달하기 위해 배경이 다크 테마에서 자동으로 밝아진다.

머티리얼 디자인은 다크 테마에서 밝은 색상의 넓은 영역을 구성하는 것은 피하는 것이 좋다. 일반적인 패턴은 라이트 테마의 container primary 색상과 다크 테마의 surface 색상을 지정하는 것이다. 많은 컴포넌트가 기본적으로 이러한 전략을 사용한다. 예를들어 App Bar 및 Bottom Navigation이 있다. 이를 쉽게 구현하기 하기 위해 Colors는 정확히 이 동작을 제공하는 primarySurface 색상을 제공하며, 이러한 컴포넌트들은 기본으로 사용한다.

우리 앱은 현재 AppBar를 기본 색상으로 설정하고 있다. 이 지침에 따라 이를 primarySurface로 전환하거나 기본값인 이 매개변수를 제거하면 된다. AppBar 컴포저블에서 TopAppBar의 backgroundColor 매개변수를 변경하자.

@Composable
private fun AppBar() {
  TopAppBar(
    ...
-   backgroundColor = MaterialTheme.colors.primary
+   backgroundColor = MaterialTheme.colors.primarySurface
  )
}

카테고리: Compose

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.