以前怎么实现的?
一般是搞多套资源切换呗。
我之前在我自己的App斧头便签实现夜间模式的方法是使用Prism框架。
// 主题切换框架
// https://github.com/StylingAndroid/Prism
// https://blog.leancloud.cn/3612/
compile 'com.stylingandroid.prism:prism:1.0.1'
使用方法上面的注释可以找到资料,大概是创建Prism对象,然后要在每个界面把要改变颜色的View都add进去。然后图标是动态set的。
新的实现方式
Android现在有官方的夜间模式方案,Support包从23.2版本开始支持夜间模式,也就是DayNight Mode。
已经出来一段时间了,所以网上关于DayNight使用方法其实很多。但是内容都不全面,大概内容就是说出下面这个大概的使用方法。
使用方法:
- 继承AppCompatActivity
- 使用Theme.AppCompat.DayNight这个Theme,例如如果你之前的祖宗
Theme
Theme.AppCompat.NoActionBar
改为
Theme.AppCompat.DayNight.NoActionBar
- 在Application中设置初始化一下Theme。
例如像这样:
static {
AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_AUTO);
}
- 然后改变Theme的方法就是
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
recreate(); // 这个是刷新,不然不起作用
- 有四个模式可以选
MODE_NIGHT_NO // 日间模式,使用light theme
MODE_NIGHT_YES // 夜间模式,使用dark theme
MODE_NIGHT_AUTO // 根据系统时间自动切换
MODE_NIGHT_FOLLOW_SYSTEM // 跟随系统设置,这个是默认的
over,大致使用方法就是以上这样,你可以尝试新建一个新app改一下上述这几点就可以看到效果了。网上一些文章也就到此为止了。
具体问题
但是其实问题还有很多,例如这是一个很常见的app的界面。
那么问题来了:
- 怎么设置切换时的具体颜色?
- Toolbar怎么设置?
- 悬浮按钮FloatingActionButton怎么设置?
- 侧滑菜单NavigationView怎么设置?
- 图标怎么设置?
- RecycleView的每个item怎么设置?
- alertdialog怎么设置?
这些问题的答案其实是同一个解,资源细分,values细分出values-night,drawable细分出drawable-night。这个和根据Android版本细分的values-v21是一个道理。
资源细分
具体怎么用呢?Material Design有一张经典的颜色设置指导图:
所以你会在colors.xml设置好一些颜色,然后在styles.xml这样设置:
那就创建一个新的values-night把再新建一个styles.xml,把夜间模式的颜色在这里设置下。这样再切换到夜间模式时就会使用values-night的资源。你不用把整个styles.xml的内容都复制过来,例如你只想改colorPrimary,你就设置一个colorPrimary就行了。
当然了,不只是styles.xml,colors.xml,dimens.xml,strings.xml都可以这么做。意思这些资源调用都是就近原则,切换到夜间模式时values-night有就使用values-night的,没有还是使用values的。
比如说我要改侧滑菜单NavigationView头部的颜色,官方的实现方法就是这样吧:
然后你找到了nav_header_main,点进去发现背景颜色是 android:background="@drawable/side_nav_bar"
那就按我上面说的,新建一个drawable-night,然后取一个名字一模一样的资源文件side_nav_bar。
然后我改下颜色:
图标也是一个道理,建drawable-night-hdpi,drawable-night-xhdpi什么的,把图标放进去,记得取一样的名字。
看一下效果(颜色暂时随便设置的,有点丑):
所以关键是资源细分,这样你就可以解决我上面提的这些问题。
还有个对话框,对话框自然也是有DayNight的Theme的。
在styles.xml加一个theme
然后在创建的时候用自己的引用下
Tips
- Tip 1:
在Application里初始化theme不是必须的,如果你要在Application初始化且你本地保留了夜间模式的状态,进入app时需要判断,可以写在Application的onCreate里:
boolean isNightMode = GlobalConfig.getInstance().isNightMode(getApplication());
if (isNightMode) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
isNightMode是一个静态全局的获取本地数据的方法:
public boolean isNightMode(Context context) {
SharedPreferences sp = context.getSharedPreferences(GlobalValue.SHARED_PRE_FILE_NAME,
Context.MODE_PRIVATE);
return sp.getBoolean(SP_KEY_NIGHT_MODE, false);
}
- Tip 2:
recreate()会重建Activity,所以不可以在create()时调用这个方法,否则生命周期会进入死循环。
那我每次进入Activity都要设置下模式怎么办?那就把setLocalNightMode写在super.onCreate()之前。
boolean isNightMode = GlobalConfig.getInstance().isNightMode(BaseActivity.this);
if (isNightMode) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
super.onCreate(savedInstanceState);
...
总结
所以,现在实现夜间模式的方法就是
- 准备好多套资源
- 使用版本23.2及以上的support包
- 继承AppCompatActivity
- 用DayNight这个Theme。
- 然后自己动态设置mode,recreate一下就行了。相比以前还是要自己准备多套资源,但是省去了修改theme和图标,刷新界面的代码。