1、夜间模式怎么实现?

先看一个实现夜间模式的demo。

首先看MainActivity的代码:

public class MainActivity extends Activity {private TextView tv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv = findViewById(R.id.tv);findViewById(R.id.model_btn).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 获取ResourcesResources resources = getResources();DisplayMetrics dm = resources.getDisplayMetrics();// 获取ConfigurationConfiguration config = resources.getConfiguration();// 修改Configuration的夜间模式if ((config.uiMode & Configuration.UI_MODE_NIGHT_YES) == Configuration.UI_MODE_NIGHT_YES) {config.uiMode &= ~Configuration.UI_MODE_NIGHT_MASK;config.uiMode |= Configuration.UI_MODE_NIGHT_NO;} else {config.uiMode &= ~Configuration.UI_MODE_NIGHT_MASK;config.uiMode |= Configuration.UI_MODE_NIGHT_YES;}// 重新将Configuration更新到Resources中getResources().updateConfiguration(config, dm);// 重新设置TextView的颜色,否则不生效tv.setTextColor(getResources().getColor(R.color.colorAccent));}});}
}

布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Hello World!"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"android:textColor="@color/colorAccent"/><Buttonandroid:id="@+id/model_btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"app:layout_constraintTop_toBottomOf="@id/tv"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"android:text="切换模式"/></androidx.constraintlayout.widget.ConstraintLayout>

当让我们需要在res文件夹下设置分别设置values文件夹和values-night文件夹,这样当夜间模式切换之后,通过Resource获取资源时就会根据当前模式获取不同文件夹下的资源,资源文件如下:

下面是效果图,左边是白天模式,右边是夜间模式。

   

上面的demo很简单,布局文件中只有一个TextView和一个Button,点击Button可以修改TextView的颜色。

2、夜间模式切换原理

有一个问题,我明明在xml文件中设置了TextView的字体颜色,但是还是要在Activity中再设置一次或者调用Activity的recreate()方法(这样会闪一下),否则夜间模式切换之后不生效。这是为什么呢?

我们通过Resources.getColor()方法追一下源码:

获取color分两步进行,第一步根据id获取一个TypeValue对象,第二步根据TypeValue和id获取ColorStateList,最后返回ColorStateList中的颜色。

那我想看下到底是怎么获取ColorStateList的?继续追:

这里有一些缓存逻辑,这些逻辑不在我们考虑范围之内,继续追红框内的代码:

从上面代码中可以看到具体的色值是通过解析颜色的xml文件得到的。此时我们并不关系xml文件是怎么解析的,我们只需要知道切换白天模式或夜间模式之后怎样找到相应的文件即可。

从上面代码可以知道文件名是存储在TypeValue.string属性中的,那么我们返回去看一下TypeValue.string属性是怎么设置的。通过上面我们知道了TypeValue对象中的属性是在ResourcesImpl.getValue()方法中设置的,所以追一下这个方法:

最终我们追到了AssetManager.getResourceValue方法,这个方法中调用了一个native方法,在这个方法中设置了TypeValue的值,但是这个native方法我们看不到。

想一下我们的demo,夜间模式切换的时候我们修改了Configuration,然后将新的Configuration重新设置到Resources中,然后获取颜色时色值就变了,所以可以猜想,获取哪个文件中的色值应该跟Configuration有关,那么我们看下Resource.updateConfigration()方法:

可以看到,最终更新后的Configuration的属性被设置到了AssetManager中了,此时又调用了native方法,我们依然看不到更深层次的原理,但是我们可以想到,AssetManager获取资源文件时一定会使用Configuration的uiMode属性,所以我们去看一下这个属性在哪用了。

看这里,惊不惊喜?意不意外?通过Configuration.resourceQualifierString方法可以获取到能被build tools解析的资源限定符。虽然没找到这个方法在哪里被调用了,但是可以猜想到,当夜间模式切换之后,AssetManager获取资源文件时会根据当前的uiMode获取到相应的资源文件。然后Xml解析器就会解析获取到的资源文件中的资源,从而找到相应的颜色。

3、为什么只在xml中设置字体颜色切换夜间模式不生效?

至此,我们了解了切换夜间模式的原理,但是为什么只在xml中设置夜间字体颜色在切换夜间模式时不生效呢?

很显然,因为修改夜间模式时并没有触发重新获取字体颜色的逻辑。但是我很好奇,在xml中设置夜间模式是怎么生效的呢?还是看源码,我们可以知道在xml中使用TextView之后最终会走到TextView的构造方法:

TextView的构造方法会在readTextAppearance()方法中获取字体颜色,这个方法需要传入一个TypeArray对象,这个对象是通过theme获取的。

上图可见,textColor是在TypeArray对象中获取的,接着往下看:

可以看到这个方法最终返回的是mResource.loadColorStateList(value, value.resourceId, mTheme)。从前面分析夜间模式切换原理的时候我们知道,这个方法最终是通过解析xml文件得到具体的色值,而我们关心的是解析哪个文件,所以下面看下getValueAt()方法。

通过上面代码可以发现,TypeValue.string是从Xml解析器的缓存池中获取的,也就是说为了提升性能,会提前把要解析的文件名缓存到解析器中,具体的缓存逻辑就不再往下追了。

4、最终总结

通过上面的分析我们可以知道,要实现夜间模式的切换,就需要实现下面两个步骤:

一、修改Configurations,并将修改后的Configurations对象更新到Resources中;

二、重新给视图组件设置资源,此时我们通过Resources对象获取资源时就会,Resources对象就会根据当前是否是夜间模式获取不同的资源。

在项目中重新给视图组件设置资源有两种触发方式:

第一,重新创建Activity(Android原生的深色模式就是使用这种方案),比如监听到夜间模式切换之后调用Activity的recreate()方法,但是这有个问题就是会闪一下,所有有一种方案是让Activity重新调起自己,但是这样的话原来的状态就难以保存了,尤其是当页面比较复杂时,这种方式难以接受。

第二、不重新创建Activity,而是当监听到夜间模式切换后在代码中重新给组件设置资源。这种方式优点在于可以保持当前的页面状态,缺点是需要在Java代码中给组件设置资源,如果页面复杂会增加很多工作量,而且很容易遗漏。

所以我们还应该探索一下有没有更优雅的实现方案。

Android夜间模式原理相关推荐

  1. Android夜间模式最佳实践

    转自:Android夜间模式最佳实践 由于Android的设置中并没有夜间模式的选项,对于喜欢睡前玩手机的用户,只能简单的调节手机屏幕亮度来改善体验.目前越来越多的应用开始把夜间模式加到自家应用中,没 ...

  2. Android夜间模式实践

    前言 由于项目需要,近段时间开发的夜间模式功能.主流的方案如下: 1.通过切换theme实现 2.通过resource id映射实现 3.通过Android Support Library的实现 方案 ...

  3. android自动夜间模式吗,Android夜间模式的实现方案

    原标题:Android夜间模式的实现方案 作者简介 本篇来自 Sunlight1024的投稿,详细地讲解了关于Android应用的夜间模式的实现,希望大家喜欢! Sunlight1024的博客地址: ...

  4. android自动夜间模式,夜晚的故事(android夜间模式实现)

    夜幕降临,他走在马路上,回想着今天发生的一切,他不敢相信事情就这样发生了.他最终还是决定拨打那个电话,掏出手机,解锁屏幕,突然一道强光从屏幕里毫无预兆的发射出来.他一个踉跄倒在了马路中央.而他身后伴随 ...

  5. android 夜间模式 框架,Android 夜间模式的三种实现

    实现夜间模式有很多种方式,经过多次尝试,算是找到了一种性价比较高的方式. 主题方式 这是最正统的方式,但工作量巨大,因为要全局替换 xml 布局中所有硬编码的色值,将其换成主题色.然后通过换主题达到换 ...

  6. android os夜间,Android夜间模式实现

    查看我的全部开源项目[开源实验室] 欢迎加入我的QQ群:[201055521],本博客客户端下载[请点击] 本文原创,转载请注明地址:http://blog.kymjs.com/ 最近在做一个Andr ...

  7. android 夜间模式设置

    夜晚的故事(android夜间模式实现) 字数2003  阅读1393  评论11  喜欢18 夜幕降临,他走在马路上,回想着今天发生的一切,他不敢相信事情就这样发生了.他最终还是决定拨打那个电话,掏 ...

  8. Android 夜间模式的实现(一)

    夜间模式实现 所谓的夜间模式,就是能够根据不同的设定,呈现不同风格的界面给用户,而且晚上看着不伤眼睛,实现方式也就是所谓的换肤(主题切换).对于夜间模式的实现网上流传了很多种方式.也反编译了几个新闻类 ...

  9. android+夜间模式开发,高质量Android开发系列之(一)-Android夜间模式最佳实现

    由于Android的设置中并没有夜间模式的开关,对于喜欢睡前玩手机的用户,只能简单的调节手机屏幕亮度来改善体检.当前越来越多的应用开始把夜间模式加到自家应用中,相信不久google也会把这项功能用到A ...

最新文章

  1. JavaScript的数据类型有哪些?
  2. RandomAccessFile学习笔记
  3. php文件之间相互引用路径问题的一般处理方法
  4. SSL,SSH,OpenSSL,OpenSSH,HTTPS的区别
  5. pigeon hole
  6. RMAN异机复制数据库(相同路径)
  7. Mysql数据库(1.表的约束,以及数据表相关操作和字段名的相关操作)
  8. Fiori navigation logic ( Route )
  9. 十二、sed 命令与正则表达式
  10. delphi 10.4来了
  11. 海康服务器协议,国标流媒体服务器GB28181协议和海康设备的交互过程记录
  12. IOS技术分享| 你画我猜小游戏快速实现
  13. lisp绘制直齿圆柱齿轮_直齿圆柱齿轮的知识及其画法
  14. 教你如何上×××钱:轻松挂机,在家月入千\\万!$
  15. 技巧:如何提高git下载速度
  16. Firefox检测到潜在的安全威胁,并因blog.csdn.net要求安全连接而没有继续
  17. 小程序基础知识整理(组件篇)
  18. 各类工控软件图库(组态王,威纶通,西门子,昆仑通泰等通用
  19. 关于福昕阅读器启动速度慢的问题
  20. 使div水平居中的方式

热门文章

  1. Scylla3.0.4在CentOS7.4上的安装
  2. 通达信版弘历软件指标_弘历软件多空王指标
  3. AWS Route53里使用Godady注册的域名
  4. Unity开发手游常用适配分辨率
  5. 事件起泡 Event bubbling
  6. 词向量与词向量拼接_nlp中的词向量对比:word2vec/glove/fastText/elmo/GPT/bert
  7. 比较线程子进程 占用的内存情况
  8. 【SSM电商项目后台开发】001-数据库建表
  9. 自己动手写CPU之第九阶段(2)——载入存储指令说明2(lwl、lwr)
  10. SpringBoot电商项目之购物车下单(沙箱支付)