어두운 테마 구현

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 구성 변경이 트리거됩니다. 즉, 활동이 자동으로 다시 생성됩니다.

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

앱은 각 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.
}

자바

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