Activityを対象としたNight Modeの切り替えは、AppCompatDelegateで行う方法があります。
ここでは、AppCompatDelegateを使った方法を説明します。
※Night Modeの切り替えの全体像は「Dark ThemeとNight Modeの関係」を参照
※環境:Android Studio Jellyfish | 2023.3.1
Kotlin 1.9.0
Compose Compiler 1.5.1
androidx.appcompat:appcompat 1.6.1
目次
切り替えの方法
AppCompatDelegate経由で行います。
// YES/NO AppCompatDelegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO) AppCompatDelegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES) // AUTO_TIME(非推奨) AppCompatDelegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_TIME) // AUTO_BATTERY(使用条件あり) AppCompatDelegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY) // FOLLOW_SYSTEM AppCompatDelegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) // UNSPECIFIED(指定は不可能) AppCompatDelegate.setLocalNightMode(AppCompatDelegate.MODE_NIGHT_UNSPECIFIED)
AppCompatDelegateはAppCompatActivityがメンバーに持つクラスです。
アプリの下位互換性を維持するために、上位APIの代理を努めるメンバー(関数、メソッドやプロパティ)が定義されています。
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
private static final String DELEGATE_TAG = "androidx:appcompat";
private AppCompatDelegate mDelegate;
private Resources mResources;
...
}
そして、AppCompatActivityは画面をViewシステムで構築する場合にActivityへ継承されます。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
}
...
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { /* Composable関数群 */ }
...
}
...
}
つまり、AppCompatDelegate経由で切り替えを行いたければ、Viewシステムを用いる必要があります。
切り替えのタイプ
指定できる切り替えタイプは5つです。
| 切り替えのタイプ(AppCompatDelegate.XXX) | 結果 | |||
|---|---|---|---|---|
| 手動 | MODE_NIGHT_YES | スイッチ | Dark On | Night |
| MODE_NIGHT_NO | Dark Off | Day | ||
| 自動 | MODE_NIGHT_AUTO_TIME ※非推奨 | 暦に従う | 日の入時刻 (22:00) | Day⇒Night |
| 日の出時刻 (06:00) | Night⇒Day | |||
| MODE_NIGHT_AUTO_BATTERY ※使用条件あり | バッテリーセーブ連動 | セーブOn | Night | |
| セーブOff | Day | |||
| その他 | MODE_NIGHT_FOLLOW_SYSTEM | システムに従う | ||
| MODE_NIGHT_UNSPECIFIED | 上書きしない | |||
| ※isSystemInDarkTheme()はUiModeManagerに切り替えられた結果を返す true:Night Mode、false:Day Mode |
||||
MODE_NIGHT_UNSPECIFIEDは初期値です。
タイプ:YES/NO
手動で行われるOn/Offのスイッチ動作をします。
YESの時刻でDay⇒Nightへ、NOの時刻でNight⇒Dayへ遷移します。

タイプ:AUTO_TIME(※非推奨)
現在地の「日の入」/「日の出」時刻に切り替えを行います。
...
@Deprecated
public static final int MODE_NIGHT_AUTO_TIME = 0;
...
【ドキュメントより】
Automatic switching of dark/light based on the current time is deprecated. Considering using an explicit setting, or MODE_NIGHT_AUTO_BATTERY.
-----
現在時刻をもとにしたdark/lightの自動切換えは非推奨になりました。YES/NOまたはAUTO_BATTERYの使用を考慮してください。
Android端末は常に自身の地図上の位置を把握するように努めています。位置はGPSやネットワーク、電話のアクセスポイントなどから得ます。現在地は最後に確定した地図上の位置です。
現在地の確認は、Google Mapアプリを開き、現在値ボタンを押下すれば分かります。地図の中心が現在地です。

日の入(Sunset)の時刻でDay⇒Nightへ、日の出(Sunrise)の時刻でNight⇒Dayへ遷移します。

このタイプは、アプリに位置情報のアクセス許可が必要です。
許可がない場合はハードコード(ソースコードに直接記述された時刻)値が用いられます。※ハードコード値:日の入 22:00、日の出 06:00
タイプ:AUTO_BATTERY(※使用条件あり)
バッテリーセーバーに連動して切り替えを行います。
バッテリーセーバーのOnでDay⇒Nightへ、OffでNight⇒Dayへ遷移します。

バッテリーセーバー機能にNight Modeの切り替えを含んでいるデバイスがあります。
例えば、エミュレータ(API34を使用)はバッテリーセーバOnでDark Themeに移行します。

【ドキュメントより】 Please note: this mode should only be used when running on devices which do not provide a similar device-wide setting. ----- 注意:このモードは同様の機能をシステム全体に提供しないデバイスのみに使用してください。
「同様の機能をシステム全体に提供しないデバイスのみに使用」という条件があります。
タイプ:FOLLOW_SYSTEM
システムを対象としたNight Modeの切り替えに従います。
※システムを対象としたNight Modeの切り替えについては以下をを参照
「システムのNight Modeの切り替え(方法:Settingsアプリ、API≧29)」
「システムのNight Modeの切り替え(方法:UiModeManager、API<29)」
setLocalNightModeによる上書き
setDefaultNightModeは、setLocalNightModeによって上書きされます。
| setDefaultNightModeの指定 | setLocalNightModeの指定 | 上書き後のタイプ |
|---|---|---|
| XXX | YES | YES |
| NO | NO | |
| AUTO_TIME | AUTO_TIME | |
| BATTERY | BATTERY | |
| FOLLOW_SYSEM | FOLLOW_SYSEM | |
| UNSPECIFIED | XXX | |
| ※「NIGHT_MODE_」は省略 ※XXX:タイプ中の一つが入る |
||
setDefaultNightModeの指定を有効にするには、setLocalNightModeの指定をMODE_NIGHT_UNSPECIFIEDにします。
MODE_NIGHT_UNSPECIFIEDは初期値です。
※setDefaultNightModeについては「アプリのNight Modeの切り替え(方法:AppCompatDelegate)」を参照
切り替えの反映
切り替えは直ちに反映されます。
Settingsアプリで行った場合と異なるので注意してください。。

切り替えは構成の変更を伴って行われます。ですので、Activityは再作成されます。
構成の変更
NightとDay Modeの切り替えが行われると、構成の変更が発生し、Activityが再作成されます。これにより、DarkとLight Themeが切り替わります。
Activityの再作成を行いたくなければ、Manifestファイルへ下記のように追記してください。
Activityの再作成は抑制され、onConfigurationChanged()がコールパックされます。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application ...>
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.DarkMode"
android:configChanges="uiMode">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
class MainActivity : ComponentActivity() {
...
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val _nightMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
when(_nightMode) {
Configuration.UI_MODE_NIGHT_NO -> {/* 処理 */} // Night Mode
Configuration.UI_MODE_NIGHT_YES -> {/* 処理 */} // Day Mode
Configuration.UI_MODE_NIGHT_UNDEFINED -> {/* 処理 */}
else -> {}
}
}
...
}
Viewシステムにおいて、Activityの再作成を行わない場合は、LightとDark Themeが切り替わりません。
切り替えは、onConfigureChanged( )内で行われる処理に委ねられます。
付録:エミュレータで現在地の設定
エミュレータで現在地の設定を行う場合は、Extended Controlsパネルを使います。
パネルを開きます。

地図を目的の場所へ移動させて、ダブルクリックで場所を決定します。

“Set Location”の押下で、現在地として登録されます。
付録:ViewシステムのActivityを作成
Android Studioは「Jetpack Compose」ならびに「Viewシステム」を用いたアプリの開発が可能です。
開発の手法がJecpack Composeに移行した現状においても、Viewシステムが残されています。
ViewシステムのActivityを作成したければ、図のように行います。


関連記事:
