Android DayNight主题实现应用夜间模式教程
在本教程中,我们将讨论如何在我们的应用程序中使用Android的日夜主题。如果您的应用程序包含阅读材料,那么具备夜间模式有助于减轻视觉负担。
Android 日夜主题
Android发布了一个新的主题:Theme.AppCompat.DayNight,支持库版本为23.2.0。有了这个主题,我们现在可以在我们的应用程序的浅色和深色模式之间切换。我们可以手动切换,也可以让Android根据手机自动检测日期和时间来切换。这个主题通过将明亮的白色背景替换为较暗的背景,提升了应用程序在夜间的可读性和可用性。许多阅读应用程序已经在他们的应用中使用了这个主题。让我们通过创建一个新的空活动的Android Studio项目来开始我们的实现。
将主题添加到我们的styles.xml文件中
让我们将应用程序中当前的主题替换为 DayNight 主题。
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- 在此处自定义您的主题 -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
为了在我们的应用程序中设置DayNight主题,我们使用方法:AppCompatDelegate.setDefaultNightMode()。以下是上述方法中允许的参数。
- MODE_NIGHT_YES – 手动启用夜间模式。
- MODE_NIGHT_NO – 手动禁用夜间模式。
- MODE_NIGHT_FOLLOW_SYSTEM – 使用系统设置来确定一天中的时间并相应地切换夜间模式。这是默认参数。
- MODE_NIGHT_AUTO – 这会尝试从设备位置API自动检测时间。如果未授予位置服务的运行时权限,则使用系统时间。
在onCreate()方法中添加以下代码。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); //用于夜间模式主题
//AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); //用于日间模式主题
setContentView(R.layout.activity_main);
}
在调用setContentView方法之前,应该始终设置主题。
AppCompatDelegate是什么?

<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- 在此处自定义您的主题 -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorPrimary">@android:color/white</item>
<item name="android:textColorSecondary">@android:color/white</item>
</style>
要获取当前夜间模式,我们使用AppCompatDelegate.getDefaultNightMode()方法,该方法分别返回之前讨论过的各种类型的整数值。有了基本的概念后,让我们制作一个应用程序,它将:
- 自定义日间/夜间模式的资源和样式。
- 从用户界面切换日夜主题。
- 查看各种UI小部件在夜间模式下的外观。
Android夜间模式项目结构

Android 日夜主题示例代码
以下是activity_main.xml类文件的代码:
这是文章《在应用中使用Android DayNight主题进行夜间模式》的第2部分(共3部分)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_margin="@android:dimen/app_icon_size"
android:text="欢迎使用本教程"
android:textColor="@color/daynight_textColor"
android:textSize="18sp" />
<ImageView
android:id="@+id/imageView"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:src="@drawable/placeholder" />
<TextView
android:id="@+id/txtNightMode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/switchCompat"
android:layout_centerHorizontal="true"
android:paddingRight="8dp"
android:text="夜间模式"
android:textColor="@color/daynight_textColor" />
<android.support.v7.widget.SwitchCompat
android:id="@+id/switchCompat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@android:dimen/app_icon_size"
android:layout_toRightOf="@+id/txtNightMode"
android:checked="false"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/imageView"
android:layout_alignLeft="@+id/txtNightMode"
android:layout_alignStart="@+id/txtNightMode"
android:text="点击我"
android:textColor="@color/daynight_textColor" />
</RelativeLayout>
- 我们在ImageView上设置了自定义文本颜色和可绘制资源。
- 为了为日间和夜间主题设置不同的颜色和可绘制资源,我们需要为这些资源创建单独的文件夹。
- 日间主题资源位于默认目录中。
- 夜间主题资源位于名称后缀为-night的文件夹中。
- 因此,我们在项目中创建了values-night和drawable-night文件夹。
- 对于希望在DayNight主题中切换的资源,可绘制文件名、颜色、样式名称在两个目录中必须相同。
- 如果上述内容仅在一个目录中定义,则日间和夜间主题将使用相同的资源。
以下是values和values-night文件夹中styles.xml中的代码。
<resources>
<!-- 基础应用主题。 -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- 在此处自定义您的主题。 -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorPrimary">@android:color/white</item>
</style>
<style name="MyDialog" parent="Theme.AppCompat.Light.Dialog.Alert"/>
<style name="MySwitch">
<item name="colorControlActivated">@color/switchColor</item>
</style>
</resources>
<resources>
<!-- 基础应用主题。values-night.xml -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- 在此处自定义您的主题。 -->
<item name="colorPrimary">@color/orange</item>
<item name="colorPrimaryDark">@color/orangeDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorPrimary">@android:color/white</item>
</style>
<style name="MyDialog" parent="Theme.AppCompat.DayNight.Dialog.Alert"/>
<style name="MySwitch">
<item name="colorControlActivated">@color/switchColor</item>
</style>
</resources>

在上面的代码中,我们使用Switch组件在应用程序中切换白天和夜间模式主题。我们将当前模式保存在SharedPreferences对象中。为什么呢?因为一个活动的主题只能设置一次。因此,当切换开关时,我们需要将新的模式保存在SharedPreferences对象中。我们使用单例模式来创建Application类。这样,相同的Application类实例可以在整个应用程序中使用。InitApplication.java类的代码如下所示:
package com.Olivia.daynightmode;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.SwitchCompat;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (InitApplication.getInstance().isNightModeEnabled()) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
setContentView(R.layout.activity_main);
SwitchCompat switchCompat = findViewById(R.id.switchCompat);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(MainActivity.this, R.style.MyDialog)
.setTitle("标题")
.setMessage("消息")
.show();
}
});
if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES)
switchCompat.setChecked(true);
switchCompat.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
InitApplication.getInstance().setIsNightModeEnabled(true);
Intent intent = getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
startActivity(intent);
} else {
InitApplication.getInstance().setIsNightModeEnabled(false);
Intent intent = getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
startActivity(intent);
}
}
});
}
}
我们在这里从Shared Preferences中更新和检索夜间模式类型。
package com.Olivia.daynightmode;
import android.app.Application;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class InitApplication extends Application {
public static final String NIGHT_MODE = "NIGHT_MODE";
private boolean isNightModeEnabled = false;
private static InitApplication singleton = null;
public static InitApplication getInstance() {
if(singleton == null)
{
singleton = new InitApplication();
}
return singleton;
}
@Override
public void onCreate() {
super.onCreate();
singleton = this;
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
this.isNightModeEnabled = mPrefs.getBoolean(NIGHT_MODE, false);
}
public boolean isNightModeEnabled() {
return isNightModeEnabled;
}
public void setIsNightModeEnabled(boolean isNightModeEnabled) {
this.isNightModeEnabled = isNightModeEnabled;
SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(NIGHT_MODE, isNightModeEnabled);
editor.apply();
}
}
安卓夜间模式支持应用程序的输出效果

下载Android日夜主题示例项目。