美文网首页
Android夜间模式 - DayNight Mode的使用

Android夜间模式 - DayNight Mode的使用

作者: 许宏川 | 来源:发表于2016-10-06 17:01 被阅读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使用方法其实很多。但是内容都不全面,大概内容就是说出下面这个大概的使用方法。

使用方法:

  • 继承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这样设置:

<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

  • 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和图标,刷新界面的代码。

相关文章

网友评论

      本文标题:Android夜间模式 - DayNight Mode的使用

      本文链接:https://www.haomeiwen.com/subject/ulmsyttx.html