Android Dark theme 적용

^--^·2022년 8월 19일
0

방법 1. DayNight 테마 적용

res/values/styles.xml 에 있는 앱의 테마를 DayNight 테마에서 상속하도록 설정이 필요

MaterialComponents의 다크 테마도 존재

<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
  • 기본적으로 Light 대신 DayNight 테마만 적용해도 다크모드 적용 됨

  • values-night/theme.xml 파일 없더라도 자동으로 분석해서 그려줌


방법2. 강제 설정

안드로이드10에서 처음 제공된 기능

→ 이전 버전에선 적용 안됨

시스템에서 다크모드로 설정이 되어있으면 앱도 다크 테마를 사용하도록 강제하는 방식

→ 시스템이 라이트 모드라면 앱에서 다크테마 적용되지 않음

밝은 테마의 각 뷰를 분석하여 뷰를 그리기 전에 다크 테마를 자동으로 적용하여 뷰를 그려주는 방식

Light 테마 계열인 경우만 적용됨(Theme.AppCompat.Light, Theme.Material.Light)

→ 만약, 이미 Theme.Material이나 DayNight 같은 다크 테마가 이미 적용되어 있다면 강제 설정은 무시됨

<style name="AppTheme" parent="Theme.MaterialComponents.Light...">
     ...
     <item name="android:forceDarkAllowed" tools:targetApi="q">true</item>
</style>

-> 이걸 적용하면 values-night/theme.xml 이 적용이 안되나?

특정 뷰에 대해서 다크 테마 비활성화 시킬 수 있으나 조심해서 사용

방법 1
View.setForceDarkAllowed(false)

방법 2
<View
	...
	android:forceDarkAllowed="false" />

어떤 기준으로 색상 변경이 이루어지는 지 명확하지 않아 실사용하려면 테스트가 많이 필요할 듯


방법3. -night qualifier 지정

color, drawable resource 를 values/~, values-night/~ 디렉토리 각각에서 관리

라이트 테마일 때 → values/~ 적용

다크 테마 → values-night/~ 적용

색상은 values-night/colors.xml 파일을 만들어 색상 값 정해주면 됨. 해당 파일안의 name은 values/colors.xml에 정의되어있는 name이어야 함

이미지는 drawable-night 하위에 넣어주면 됨


인앱 테마 변경 방법

AppCompat.DayNight 모드 값에 따라서 동적으로 테마 전환 가능

[모드들]

MODE_NIGHT_NO - 라이트 테마

MODE_NIGHT_YES - 다크 테마

MODE_NIGHT_AUTO_BATTERY - 배터리 절전 모드일 때 다크 테마 적용(Android Q 미만)

MODE_NIGHT_FOLLOW_SYSTEM - 시스템 테마 설정에 따름(Android Q 이상)

→ “사용자가 라이트 테마 적용 시 절전 모드에서 그 설정을 변경하지 않음” 이라고 문서에 적혀있는데 아래 확인한 내용 섹션 참고

테마 변경은 AppCompatDelegate.setDefaultNightMode 으로 가능. 아래는 참고용 코드

/* 참고용 코드 */
object ThemeHelper {
    public const val LIGHT_MODE = "light"
    public const val DARK_MODE = "dark"
    public const val DEFAULT_MODE = "default"

    fun applyTheme(@NonNull themePref: String) {
        when (themePref) {
            LIGHT_MODE -> {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
            }

            DARK_MODE -> {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            }

            else -> {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
                } else {
                    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
                }
            }
        }
    }
}

AppCompat.DayNight 지속성

지속 안됨. 앱 프로세스가 생성될 때 마다 테마를 설정해줘야 함

예로 SharedPreferences에 저장하여 관리할 수 있겠음

@HiltAndroidApp
class AccountApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        val themePref = sharedPreferences.getString("themePref", ThemeHelper.DEFAULT_MODE)

        applyTheme(themePref ?: ThemeHelper.DEFAULT_MODE)
    }
}

구성 변경

시스템 설정 혹은 AppCompat을 통해 앱의 테마가 변경되면 uiMode 구성 변경이 트리거되어 Activity가 자동으로 다시 생성됨

앱 테마가 변경되기 전 처리할 게 있거나 수동으로 뷰를 변경하고 싶은 경우

// in AndoridManifest.xml

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

속성을 추가하면 테마가 변경될 때 onConfigurationChanged() 메서드가 호출됨.

이 옵션을 사용할 땐 예를 들어, 현재 하던 작업(동영상 시청같은) 을 마치고 그 뒤에 activity가 재실행 되도록 하고싶을 때 사용할 수 있음.

현재 테마가 무엇인지 확인하고 싶다면,

// in onConfigurationChanged()

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
}

위의 코드로 확인 가능


야간모드 설정을 무시하려면?

Theme style 이름에 DayNight이 들어가지 않음

리소스 폴더 이름 중 -night qualifier 존재하지 않음

을 둘다 만족하면 야간모드 적용X


확인한 내용

  • Q 버전 미만에서 절전모드 하고 default로 설정했을 때 다크모드 되는지 확인
    앱에서 default 모드 → 절전모드 설정/해제 → 앱 테마 바로바로 바뀜

    앱에서 Light/Dark 모드 → 절전모드 설정/해제 → 앱 테마 Light/Dark 모드로 유지됨

  • ForceDarkAllowed 속성 true 했을 때 -night qualifier 붙은 파일들 적용되겠지?
  • android:configChanges="uiMode" 하고 액티비티에서 onConfigurationChanged를 override했을 때 아무처리도 하지 않으면 테마변경 안됨?
    진짜 안됨.

    액티비티 재생성을 딜레이해야하는 처리가 필요하다면, 처리 후 recreate() 호출 해줘야 할듯
    AppCompatDelegate.setDefaultNightMode 도 내부에서 액티비티 recreate() 호출하고 있긴 함

  • DayNight 테마를 사용하지 않고 values/values-night 디렉토리만 나눠도 values-night 하위 내용 적용 되나?
    일반 Light 테마 사용하고 디렉토리만 나눠줘도 시스템 설정 or 절전모드 여부 or AppCompatDelegate.setDefaultNightMode에 따라 적용됨

    안드로이드 29 이상, 미만 버전 둘다 확인

profile
안드로이드 개발자

0개의 댓글