어두운 테마 구현

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

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

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

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

테마 및 스타일

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

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

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

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

앱 내에서 테마 변경

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

  • 밝게
  • 어둡게
  • 시스템 기본값 (권장되는 기본 옵션)

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

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

어두운 테마 강제 적용

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

강제로 어둡게 기능은 밝은 테마의 앱의 각 뷰를 분석하고 앱이 화면에 그려지기 전에 어두운 테마를 자동으로 적용합니다. 강제로 어둡게 기능과 네이티브 구현을 함께 사용하면 어두운 테마를 구현하는 데 필요한 시간을 줄일 수 있습니다.

앱은 활동 테마에서 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.
}

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