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是什么?

android夜间模式
<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日夜主题示例项目

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>
android daynight 日间模式颜色

在上面的代码中,我们使用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 daynight theme output

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

bannerAds