本帖最后由 武汉分校-小舞 于 2016-3-23 09:36 编辑
【武汉校区】独家分享:Android夜间模式
由于Android的设置中并没有夜间模式的开关,对于喜欢睡前玩手机的用户,只能简单的调节手机屏幕亮度来改善体检。当前越来越
多的应用开始把夜间模式加到自家应用中,相信不久google也会把这项功能用到Android系统中吧。
对当前夜间模式的实现,大概有三种方法如下:
1、通过设置不同的theme来实现夜间模式的UI变化
2、通过id获取资源时,将该id转换为夜间模式对应资源id然后再去获取资源
3、更新Resources的Configuration中的uiMode来更新Resources的夜间模式开关
下面我来简短的描述下前两种方案的实现,及第三种方案的实现原理
一、通过设置不同的theme来实现夜间模式的UI变化 首先对不同的主题需要设置不同的属性,先在attrs.xml中定义属性
其次定义不同的theme,在theme中对不的同属性设置不同的值,在styles.xml中定义theme
在布局文件中使用对应的值,通过?attr/属性名,来获取不同theme对应的值
在Activity中的changeTheme方法中,其中isNightMode为一个全局变量用来标记当前是否为夜间模式,在设置完theme后还需要调
用setContentView重新刷新UI
到此即完成了一个简单的夜间模式实现,这种方法比较简单,大多数的应用都是通过setTheme来实现的,但是在比较大的应用,需要随
theme变化的属性都需要定义,缺点是设置theme后需要通过setContentView或restartActivity来切换UI,也可以调用如
下updateTheme方法,将需要改变颜色的view重新设置背影或改变颜色
二、通过id获取资源时,将该id转换为夜间模式对应资源id然后再去获取资源
通过id获取资源时,先将其转为夜间模式对应id,再通过Resources来获取对应的资源
这里是通过HashMap的方法来将非夜间模式的resId和夜间模式的resId来一一对应起来的
这个方法非常简单粗暴,麻烦的地方和第一次方法一样,就是每次添加资源需要将其对应起来,刷新UI的方法同第一种方法一样
三、通过修改uiMode来切换夜间模式 首先统一将获取资源的地方统计起来,使用Application对应的Resources
在Application的onCreate中调用ResourcesManager的init方法将其初始化
public static void init(Context context){ sRes = context.getResources(); }
切换夜间模式时,通过更新uiMode来更新Resources的配置,系统会根据其uiMode读取对应night下的资源,同时在res中给夜间模式的
资源后缀添加-night,比如values-night,drawable-night
至于Android的资源读取我们可以参考老罗的博客Android应用程序资源的查找过程分析来看下资源是怎么被精准找到的
这种方法相对前两种的好处就是资源添加非常简单清晰,但是UI上的更新还是无法做到非常顺滑的切换
下面我描述下,怎么找到上述这种方法的
在Android开发文档中搜索night发现如下,可以通过UiModeManager来实现 Android Document night: Night time notnight: Day time Added in API level 8. This can change during the life of yourapplication if night mode is left in auto mode (default), in which case themode changes based on the time of day. You can enable or disable this modeusing UiModeManager. See Handling Runtime Changes for information about howthis affects your application during runtime.
不幸的是必须在驾驶模式下才有效,那是不是打开驾驶模式再设置呢,实际上是不可行的,驾驶模式下UI有变动,这种情况是不可取的
/** * Sets the night mode. Changes to thenight mode are only effective when * the car or desk mode is enabled on adevice. * * The mode can be one of: * {@link #MODE_NIGHT_NO}- sets the deviceinto notnight * mode. * {@link #MODE_NIGHT_YES} - sets thedevice into night mode. * {@link #MODE_NIGHT_AUTO} - automaticnight/notnight switching * depending on the location and certainother 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. Currentlythere are two fields: * The {@link #UI_MODE_TYPE_MASK} bitsdefine 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} defineswhether the screen * is in a special mode. They may be oneof {@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就可以了
想获取最新传智播客武汉中心分享技术文章请加QQ 1641907557 ,后期会分享更多与实体班同步教程,助你冲击月薪20K!
推荐阅读:
|