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 파일 없더라도 자동으로 분석해서 그려줌
안드로이드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" />
어떤 기준으로 색상 변경이 이루어지는 지 명확하지 않아 실사용하려면 테스트가 많이 필요할 듯
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 모드로 유지됨
android:configChanges="uiMode" 하고 액티비티에서 onConfigurationChanged를 override했을 때 아무처리도 하지 않으면 테마변경 안됨?
진짜 안됨.
액티비티 재생성을 딜레이해야하는 처리가 필요하다면, 처리 후 recreate() 호출 해줘야 할듯
AppCompatDelegate.setDefaultNightMode 도 내부에서 액티비티 recreate() 호출하고 있긴 함
DayNight 테마를 사용하지 않고 values/values-night 디렉토리만 나눠도 values-night 하위 내용 적용 되나?
일반 Light 테마 사용하고 디렉토리만 나눠줘도 시스템 설정 or 절전모드 여부 or AppCompatDelegate.setDefaultNightMode에 따라 적용됨
안드로이드 29 이상, 미만 버전 둘다 확인