어두운 테마 구현

Compose 사용해 보기
Jetpack Compose는 Android를 위한 권장 UI 도구 키트입니다. Compose에서 테마 설정을 사용하는 방법을 알아보세요.

그림 1. 어두운 테마

어두운 테마는 Android 10(API 수준 29) 이상에서 사용할 수 있습니다. 이 방법에는 다음과 같은 이점이 있습니다.

  • 기기의 화면 기술에 따라 전력 사용량을 크게 줄입니다.
  • 저시력 사용자 및 밝은 빛에 민감한 사용자에게 편리하도록 가시성이 향상됩니다.
  • 조명이 어두운 환경에서 더 편하게 기기를 사용할 수 있습니다.

어두운 테마는 Android 시스템 UI 및 기기에서 실행 중인 앱에 적용됩니다.

Android 10 이상에서 어두운 테마를 사용 설정하는 방법에는 세 가지가 있습니다.

  • 설정 > 디스플레이 > 테마로 이동하여 시스템 설정을 사용하여 어두운 테마를 사용 설정합니다.
  • 빠른 설정 타일을 사용하여 알림 표시줄에서 테마를 전환합니다(사용 설정된 경우).
  • Pixel 기기에서 절전 모드를 사용 설정하면 어두운 테마도 동시에 사용 설정됩니다. 다른 기기에서는 이 동작이 지원되지 않을 수 있습니다.

WebView 구성요소를 사용하여 웹 기반 콘텐츠에 어두운 테마를 적용하는 방법은 WebView의 웹 콘텐츠 어둡게 만들기를 참고하세요.

앱에서 어두운 테마 지원

어두운 테마를 지원하려면 앱의 테마(일반적으로 res/values/styles.xml에 있음)를 DayNight 테마에서 상속하도록 설정합니다.

<style name="AppTheme" parent="Theme.AppCompat.DayNight">

머티리얼 구성요소 어두운 테마를 사용할 수도 있습니다.

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">

이렇게 하면 앱의 기본 테마가 시스템 제어 야간 모드 플래그와 연결되어 앱에 기본 어두운 테마(사용 설정된 경우)가 제공됩니다.

테마 및 스타일

밝은 테마에서 사용하기 위한 하드 코딩 색상이나 아이콘을 사용하지 마세요. 대신 테마 속성 또는 야간에 적합한 리소스를 사용하세요.

어두운 테마에는 두 가지 테마 속성이 가장 중요합니다.

  • ?android:attr/textColorPrimary: 범용 텍스트 색상입니다. 밝은 테마에서는 검은색에 가깝고 어두운 테마에서는 흰색에 가깝습니다. 사용 안함 상태가 포함되어 있습니다.
  • ?attr/colorControlNormal: 범용 아이콘 색상입니다. 사용 안함 상태가 포함되어 있습니다.

테마 속성 ?attr/colorSurface?attr/colorOnSurface과 같은 색상 테마 설정 시스템을 통해 적절한 색상에 쉽게 액세스할 수 있으므로 Material Design 구성요소를 사용하는 것이 좋습니다. 테마에서 이러한 속성을 맞춤설정할 수 있습니다.

앱 내에서 테마 변경

앱이 실행되는 동안 사용자가 앱의 테마를 변경하도록 허용할 수 있습니다. 다음과 같은 옵션이 권장됩니다.

  • 얕은 수면
  • 어둡게
  • 시스템 기본값 (권장되는 기본 옵션)

다음 옵션은 AppCompat.DayNight 모드에 직접 매핑됩니다.

테마를 전환하려면 다음 단계를 따르세요.

어두운 테마 강제 적용

Android 10에서는 개발자가 DayNight 테마를 명시적으로 설정하지 않고도 어두운 테마를 빠르게 구현할 수 있는 강제로 어둡게 기능을 제공합니다.

어두운 테마 강제 설정은 밝은 테마의 앱에서 각 뷰를 분석하고, 화면에 그리기 전에 어두운 테마를 자동으로 적용합니다. 어두운 테마 강제 설정과 네이티브 구현을 혼합하여 사용하면 어두운 테마를 구현하는 데 필요한 시간을 줄일 수 있습니다.

앱은 Activity 테마에서 android:forceDarkAllowed="true"를 설정하여 강제로 어둡게 기능을 선택해야 합니다. 이 속성은 Theme.Material.Light와 같이 시스템 및 AndroidX에서 제공하는 모든 밝은 테마에 설정됩니다. 강제로 어둡게 기능을 사용할 때는 앱을 철저히 테스트하고 필요에 따라 뷰를 제외합니다.

앱에서 어두운 테마(예: Theme.Material)를 사용하는 경우 어두운 테마 강제 설정이 적용되지 않습니다. 마찬가지로 앱의 테마를 DayNight 테마에서 상속할 경우 자동 테마 전환으로 인해 어두운 테마 강제 설정이 적용되지 않습니다.

뷰에서 어두운 테마 강제 설정 사용 안함

어두운 테마 강제 설정은 android:forceDarkAllowed 레이아웃 속성이나 setForceDarkAllowed()를 사용하여 특정 뷰에서 제어할 수 있습니다.

웹 콘텐츠

웹 기반 콘텐츠에 어두운 테마를 사용하는 방법에 관한 자세한 내용은 WebView의 웹 콘텐츠 어둡게 만들기를 참고하세요. WebView에 적용된 어두운 테마의 예는 GitHub의 WebView 데모를 참고하세요.

권장사항

다음 섹션에서는 어두운 테마를 구현하기 위한 권장사항을 설명합니다.

알림 및 위젯

기기에 표시되지만 직접 제어하지 않는 UI 노출 영역의 경우 사용하는 모든 뷰에 호스트 앱의 테마가 반영되어야 합니다. 알림과 런처 위젯이 두 가지 예입니다.

알림

시스템에서 제공하는 알림 템플릿(예: MessagingStyle)을 사용합니다. 즉, 시스템에서 올바른 뷰 스타일을 적용해야 합니다.

위젯 및 맞춤 알림 뷰

런처 위젯을 사용하거나 앱이 맞춤 알림 콘텐츠 뷰를 사용하는 경우 밝은 테마와 어두운 테마에서 모두 콘텐츠를 테스트합니다.

일반적으로 다음과 같은 함정에 주의해야 합니다.

  • 배경색이 항상 밝다고 가정합니다.
  • 텍스트 색상을 하드 코딩하는 것
  • 기본 텍스트 색상을 사용하면서 하드 코딩된 배경색을 설정하는 것
  • 정적 색상인 드로어블 아이콘 사용

이러한 모든 경우에 하드코딩된 색상 대신 적절한 테마 속성을 사용하세요.

실행 화면

앱에 맞춤 시작 화면이 있는 경우 선택한 테마를 반영하도록 수정해야 할 수 있습니다.

프로그래매틱 방식으로 흰색으로 설정된 배경 색상과 같은 하드코딩된 색상을 삭제합니다. 대신 ?android:attr/colorBackground 테마 속성을 사용하세요.

구성 변경

앱 테마가 변경되면(시스템 설정이나 AppCompat을 통해) uiMode 구성 변경이 트리거됩니다. 즉, 활동이 자동으로 다시 만들어집니다.

앱이 구성 변경을 처리하도록 해야 하는 경우도 있습니다. 예를 들어 동영상을 재생하는 중이라 구성 변경을 지연하려 할 수 있습니다.

앱은 각 ActivityuiMode 구성 변경을 처리할 수 있다고 선언함으로써 어두운 테마의 구현을 처리할 수 있습니다.

<activity
    android:name=".MyActivity"
    android:configChanges="uiMode" />

Activity가 구성 변경을 처리한다고 선언하면 테마가 변경될 때 onConfigurationChanged() 메서드가 호출됩니다.

현재 테마를 확인하려면 앱에서 다음과 같은 코드를 실행할 수 있습니다.

Kotlin

val currentNightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {
    Configuration.UI_MODE_NIGHT_NO -> {} // Night mode is not active, we're using the light theme.
    Configuration.UI_MODE_NIGHT_YES -> {} // Night mode is active, we're using dark theme.
}

자바

int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
    case Configuration.UI_MODE_NIGHT_NO:
        // Night mode is not active, we're using the light theme
        break;
    case Configuration.UI_MODE_NIGHT_YES:
        // Night mode is active, we're using dark theme
        break;
}