Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

어두운 테마

어두운 테마는 Android 10 (API 레벨 29) 이상에서 제공됩니다. 어두운 테마는 다음과 같은 여러 가지 장점이 있습니다.

  • 전력 사용량을 상당히 절약할 수 있습니다(기기 화면 기술에 따라 다름).
  • 시력이 낮은 사용자와 밝은 빛에 민감한 사용자를 위한 가시성을 개선합니다.
  • 누구나 어두운 환경에서 쉽게 기기를 사용할 수 있습니다.

어두운 테마는 Android 시스템 UI와 기기에서 실행되는 앱에 모두 적용됩니다.

Android 10 (API 레벨 29) 이상에서 어두운 테마를 활성화하는 방법은 세 가지가 있습니다.

  • 시스템 설정(설정 -> 디스플레이 -> 테마)을 사용하여 어두운 테마를 활성화합니다.
  • 빠른 설정 타일을 사용하여 알림 표시줄에서 테마를 전환합니다(활성화된 경우).
  • Pixel 기기에서 절전 모드를 선택하면 어두운 테마가 동시에 활성화됩니다. 다른 OEM은 이 동작을 지원하거나 지원하지 않을 수 있습니다.

앱에서 어두운 테마 지원하기

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

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

또한 MaterialComponents의 어두운 테마 설정을 사용할 수도 있습니다.

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

앱의 메인 테마를 시스템에서 관리하는 야간 모드 플래그와 연결하고, 앱에 기본으로 설정된 어두운 테마를 적용합니다(활성화된 경우).

테마와 스타일

테마와 스타일에서는 밝은 테마에서 사용하기 위한 하드 코딩 색상이나 아이콘을 사용하지 말아야 합니다. 대신 테마 특성(권장)이나 야간에 적용되는 리소스를 사용해야 합니다.

알아두어야 할 가장 중요한 테마 특성 두 가지는 다음과 같습니다.

  • ?android:attr/textColorPrimary 범용 텍스트 색상입니다. 밝은 테마에서는 거의 검은색이고 어두운 테마에서는 거의 흰색입니다. 비활성화된 상태가 포함됩니다.
  • ?attr/colorControlNormal 범용 아이콘 색상입니다. 비활성화된 상태가 포함됩니다.

머티리얼 디자인 구성요소를 사용하는 것이 좋습니다. 색상 테마 설정 시스템(예: 테마 특성인 ?attr/colorSurface?attr/colorOnSurface)을 통해 적절한 색상에 간편하게 액세스할 수 있기 때문입니다. 물론 테마에서 이런 특성을 맞춤 설정할 수 있습니다.

인앱 테마 변경

사용자가 앱을 실행하는 동안 앱의 테마를 변경하도록 할 수 있습니다. 앱에서 사용자가 테마를 선택하게 할 수 있습니다.

Android 9 이하가 실행되는 기기에 권장되는 테마 옵션은 다음과 같습니다.

  • 밝은 테마
  • 어두운 테마
  • 절전 모드에서 설정(권장 기본 옵션)

Android 10 (API 레벨 29) 이상에서 실행할 때 권장하는 옵션은 다르며, 사용자가 시스템 기본값을 재정의할 수 있습니다.

  • 밝은 테마
  • 어두운 테마
  • 시스템 기본값(권장 기본 옵션)

사용자가 밝은 테마를 선택하면 절전 모드에서 그 설정을 변경하지 않습니다.

각 옵션은 AppCompat.DayNight 모드 중 하나로 직접 매핑됩니다.

테마를 전환하려면 AppCompatDelegate.setDefaultNightMode()를 호출하세요.

어두운 테마 강제 설정

Android 10은 어두운 테마 강제 설정을 제공합니다. 개발자가 위에서 설명한 DayNight 테마를 명시적으로 설정하지 않고 신속히 어두운 테마를 구현할 수 있는 기능입니다.

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

앱은 테마에서 android:forceDarkAllowed="true"로 설정하여 어두운 테마 강제 설정을 옵트인해야 합니다. 이 특성은 모든 시스템과 AndroidX가 제공하는 밝은 테마(예: Theme.Material.Light)에 설정됩니다. 어두운 테마 강제 설정을 사용할 때는 앱을 철저하게 테스트하고 필요에 따라 뷰를 제외해야 합니다.

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

뷰에서 어두운 테마 강제 설정 비활성화

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

권장사항

알림과 위젯

기기에 표시하지만 직접 제어하지 않는 UI 표면의 경우, 사용하는 모든 뷰에 호스트 앱의 테마를 적용하는 것이 중요합니다. 모범적인 예시로는 알림과 런처 위젯, 이렇게 두 가지가 있습니다.

알림

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

위젯과 맞춤 알림 뷰

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

주의해야 할 일반적인 함정:

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

위의 사례에서는 모두 하드코딩된 색 대신 적절한 테마 특성을 사용해야 합니다.

시작 화면

앱에 맞춤 시작 화면이 있다면 선택된 테마를 적용하도록 수정해야 할 수 있습니다.

하드코딩된 색상은 삭제합니다(예: 흰색일 수 있는 배경색). 그 대신 ?android:attr/colorBackground 테마 특성을 사용하세요.

어두운 테마의 android:windowBackground 드로어블은 Android Q에서만 작동합니다.

구성 변경사항

(시스템 설정이나 AppCompat을 통해) 앱의 테마가 변경되면 uiMode 구성 변경이 트리거됩니다. 그러면 Activity가 자동으로 다시 생성됩니다.

경우에 따라서 앱이 구성 변경을 처리하게 하고 싶을 수도 있습니다. 예를 들어 동영상이 재생되고 있어서 구성 변경을 미루고 싶을 경우가 있습니다.

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

<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
}

Java

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;
}