应用程式中夜间模式的Android DayNight主题

时间:2020-02-23 14:28:53  来源:igfitidea点击:

在本教程中,我们将在应用程序中讨论和使用Android DayNight主题。
如果您的应用程序带有阅读材料,那么夜间模式有助于缓解视线。

Android DayNight主题

Android发布了一个新主题:支持库" 23.2.0"的" Theme.AppCompat.DayNight"。

由于有了这个主题,我们现在可以在应用程序的亮模式和暗模式之间切换。
我们可以手动执行操作,也可以让Android通过您的手机隐式检测一天中的时间。

通过用较暗的背景替换白色的闪烁背景,该主题可在夜间提高应用程序的可读性和可用性。
许多阅读器应用程序已经在其应用程序中部署了此主题。

让我们从创建一个空活动的新Android Studio项目开始实施。

将主题添加到我们的styles.xml

让我们用DayNight one替换应用程序中的当前主题。

<style name="AppTheme" parent="Theme.AppCompat.DayNight">
      <!-- Customize your theme here. -->
      <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 -使用系统设置确定一天中的时间,并相应地切换NightMode。
    这是默认参数。

  • " MODE_NIGHT_AUTO" –尝试从设备位置API自动检测时间。
    如果未授予位置服务的运行时权限,则会使用系统时间。

onCreate()方法中添加以下代码。

@Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); //For night mode theme
      //AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); //For day mode theme
      setContentView(R.layout.activity_main);
  }

应该始终在调用setContentView方法之前设置主题。

什么是AppCompatDelegate?

" AppCompatDelegate"是一个类,代表一个委托,您可以用来将AppCompat的支持扩展到任何Activity。
让我们看看我们的活动屏幕在白天和夜晚模式下分别启用的样子。

TextView在夜间模式下将其颜色更改为白色。
这是因为TextView隐式包含名为?attr/colorPrimary的默认样式,该样式根据明暗应用主题切换颜色。
如果您在TextView上设置了自定义颜色" @ color/red",则在白天/夜晚模式之间不会更改。

白天模式下的工具列文字颜色为黑色。
如何在" styles.xml"本身中将其设置为白色?

<style name="AppTheme" parent="Theme.AppCompat.DayNight">
      <!-- Customize your theme here. -->
      <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切换DayNight主题

  • 查看夜间模式下各种UI小部件的外观。

Android DayNight主题示例代码

下面给出了activity_main.xml类文件的代码。

<?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="Welcome to this tutorial."
      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="Night Mode"
      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="CLICK ME"
      android:textColor="@color/daynight_textColor" 
</RelativeLayout>
  • 我们在ImageView上设置了自定义文本颜色和可绘制的颜色。

  • 要为昼夜主题设置不同的颜色和可绘制对象,我们需要为资源创建单独的文件夹。

  • 日主题资源位于默认目录中。

  • 夜间主题资源位于名称后带有-night的文件夹中。

  • 因此,我们在项目中创建了" values-night"和" drawable-night"文件夹。

  • 您要在DayNight主题中切换的资源的两个目录中的可绘制文件名,颜色,样式名称必须相同。

  • 如果以上内容仅在一个目录中定义,则白天和黑夜主题中都将使用相同的内容。

值和values-night文件夹中的styles.xml代码如下。

<resources>

  <!-- Base application theme. -->
  <style name="AppTheme" parent="Theme.AppCompat.DayNight">
      <!-- Customize your theme here. -->
      <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>
  <!-- Base application theme. values-night.xml -->
  <style name="AppTheme" parent="Theme.AppCompat.DayNight">
      <!-- Customize your theme here. -->
      <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>

上面定义的样式用于设置自定义标准DayNight主题。

如下所示定义了colors.xml的相应内容。
values-night文件夹中的colors.xml

来自values文件夹的colors.xml

下面给出了MainActivity.java类的代码。

package com.theitroad.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("Title")
                      .setMessage("Message")
                      .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);
              }

          }
      });

  }
}

在上面的代码中,我们使用Switch在应用程序中的白天和夜晚模式主题之间进行切换。
我们将当前模式保存在SharedPreferences对象中。

为什么?

活动的主题只能设置一次。
因此,当切换开关时,我们需要将新模式保存在SharedPreference对象中。
我们将Singleton Pattern用于Application类。
这样,可以在整个应用程序中使用Application类的相同实例。

下面给出了InitApplication.java类的代码。

package com.theitroad.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();
  }
}

其中我们可以从"共享首选项"中更新和检索夜间模式类型。