Android夜间模式 - DayNight Mode的使用

2016-10-06  本文已影响2290人  许宏川

以前怎么实现的?

一般是搞多套资源切换呗。
我之前在我自己的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使用方法其实很多。但是内容都不全面,大概内容就是说出下面这个大概的使用方法。

使用方法:

Theme.AppCompat.NoActionBar

改为

Theme.AppCompat.DayNight.NoActionBar
static {
        AppCompatDelegate.setDefaultNightMode(
                AppCompatDelegate.MODE_NIGHT_AUTO);
 }
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的界面。


那么问题来了:

这些问题的答案其实是同一个解,资源细分,values细分出values-night,drawable细分出drawable-night。这个和根据Android版本细分的values-v21是一个道理。

资源细分

具体怎么用呢?Material Design有一张经典的颜色设置指导图:


所以你会在colors.xml设置好一些颜色,然后在styles.xml这样设置:

<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
    <item name="colorPrimary">@color/primary</item>
    <item name="colorPrimaryDark">@color/primary_dark</item>
    <item name="colorAccent">@color/accent</item>
</style>

那就创建一个新的values-night把再新建一个styles.xml,把夜间模式的颜色在这里设置下。这样再切换到夜间模式时就会使用values-night的资源。你不用把整个styles.xml的内容都复制过来,例如你只想改colorPrimary,你就设置一个colorPrimary就行了。


当然了,不只是styles.xml,colors.xml,dimens.xml,strings.xml都可以这么做。意思这些资源调用都是就近原则,切换到夜间模式时values-night有就使用values-night的,没有还是使用values的。

比如说我要改侧滑菜单NavigationView头部的颜色,官方的实现方法就是这样吧:

<android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:menu="@menu/activity_main_drawer" />

然后你找到了nav_header_main,点进去发现背景颜色是 android:background="@drawable/side_nav_bar"

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:angle="135"
        android:centerColor="#4CAF50"
        android:endColor="#2E7D32"
        android:startColor="#81C784"
        android:type="linear" />
</shape>

那就按我上面说的,新建一个drawable-night,然后取一个名字一模一样的资源文件side_nav_bar。
然后我改下颜色:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:angle="135"
        android:centerColor="#4F4F4F"
        android:endColor="#727272"
        android:startColor="#9E9E9E"
        android:type="linear" />
</shape>

图标也是一个道理,建drawable-night-hdpi,drawable-night-xhdpi什么的,把图标放进去,记得取一样的名字。
看一下效果(颜色暂时随便设置的,有点丑):


所以关键是资源细分,这样你就可以解决我上面提的这些问题。
还有个对话框,对话框自然也是有DayNight的Theme的。
在styles.xml加一个theme

<style name="Dialog.Alert" parent="Theme.AppCompat.DayNight.Dialog.Alert"/>

然后在创建的时候用自己的引用下

<style name="Dialog.Alert" parent="Theme.AppCompat.DayNight.Dialog.Alert"/>

Tips

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);
}
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);
...

总结

所以,现在实现夜间模式的方法就是

上一篇 下一篇

猜你喜欢

热点阅读