Android夜间模式最佳实现

jopen 9年前

 

Android夜间模式最佳实现目前用户量达到一定量后的应用都会有夜间模式的功能,目前网上主要有两种实现方式:1、比较简单的实现可以定义一组theme来设置不同的颜色值等; 2、需要在assets中定义同样的一组资源,这种实现方式对于要求有一整套夜间模式的资源,同时在代码中也需要做较大的处理,缺点在 android5.0开始对selector也不支持,同时assets下的.9图片不能直接使用,需要先编译后放入到assets。

在android开发文档中搜索night发现如下,可以通过UiModeManager来实现

Configuration Qualifier Values Description
Night mode night Notnight night: Night timenotnight: Day timeAdded in API level 8.This can change during the life of your application if night mode is left in auto mode (default), in which case the mode changes based on the time of day. You can enable or disable this mode usingUiModeManager. See Handling Runtime Changes for information about how this affects your application during runtime.

不幸的是必须在驾驶模式下才有效,那是不是打开驾驶模式再设置呢,实际上是不可行的,驾驶模式下UI有变动,这种情况是不可取的

/**  * Sets the night mode. Changes to the night mode are only effective when  * the car or desk mode is enabled on a device.  *  * The mode can be one of:  * {@link #MODE_NIGHT_NO}- sets the device into notnight  * mode.  * {@link #MODE_NIGHT_YES} - sets the device into night mode.  * {@link #MODE_NIGHT_AUTO} - automatic night/notnight switching  * depending on the location and certain other sensors.  */  public void setNightMode(int mode)

从源码开始看起UiModeManagerService.java的setNightMode方法中

if (isDoingNightModeLocked() && mNightMode != mode) {  Settings.Secure.putInt(getContext().getContentResolver(),s  Settings.Secure.UI_NIGHT_MODE, mode);  mNightMode = mode;  updateLocked(0, 0);  }  boolean isDoingNightModeLocked() {      return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED;  }

在 isDoingNightModeLocked中判断了DockState和mCardMode的状态,如果满足条件实际上只修改了mNightMode 的值,继续跟踪updateLocked方法,可以看到在updateConfigurationLocked中更新了Configuration的 uiMode

让我们转向Configuration的uiMode的描述

/**  * Bit mask of the ui mode. Currently there are two fields:  *    The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the  * device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},  * {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},  * {@link #UI_MODE_TYPE_CAR}, {@link #UI_MODE_TYPE_TELEVISION},  * {@link #UI_MODE_TYPE_APPLIANCE}, or {@link #UI_MODE_TYPE_WATCH}.  *  *    The {@link #UI_MODE_NIGHT_MASK} defines whether the screen  * is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},  * {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.  */  public int uiMode;

uiMode为public可以直接设置,既然UiModeManager设置nightMode只改了Configuration的uiMode,那我们是不是可以直接改其uiMode呢
实际上只需要以下一小段代码就可以实现了,但是如果不去查看UiModeManager的夜间模式的实现不会想到只需要更新Configuration的uiMode就可以了

public static void updateNightMode(boolean on) {  DisplayMetrics dm = sRes.getDisplayMetrics();  Configuration config = sRes.getConfiguration();  config.uiMode &= ~Configuration.UI_MODE_NIGHT_MASK;  config.uiMode |= on ? Configuration.UI_MODE_NIGHT_YES : Configuration.UI_MODE_NIGHT_NO;  sRes.updateConfiguration(config, dm);  }

这里最好是将获取资源的Resource由Application来初始化,所有资源统一获取,
别忘了在res中给夜间模式的资源后缀添加-night,比如values-night,drawable-night