1自定义的方式:新增一套night的文件,根据UI设计对drawable和colors ,style 等进行个性化适配。
优点:可根据需要达到想要的效果
缺点:可能就是复杂一点,对于老项目,尤其是颜色命名不规范或者是组件特别多情况很麻烦。更甚者依赖的第三方厂商提供的构件。
2. 自动适配 Force Dark。
优点:简单,效果还是挺不错的。
缺点:不利于个性化适配。
1.新增一套与相关资源文件对应的文件,看下图:
比如:values的colors.xml和values-night的colors.xml 颜色命名都保持一致的,但是同样的名字在这两个资源里对应的颜色值是不同的。其他的如drawable style等也是如此。只是在AppTheme的主题需要改成:
2.监听系统的暗黑模式开关:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int mSysThemeConfig = newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (mSysThemeConfig) {
// 亮色主题
case Configuration.UI_MODE_NIGHT_NO:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
recreate();
break;
// 深色主题
case Configuration.UI_MODE_NIGHT_YES:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
recreate();
break;
}
}
注意:一定要在配置文件中给Activity配置属性:android:configChanges="uiMode"
此时当你切换APP的系统暗黑模式开关的时候,APP会这个页面会recreate(),然后根据你代码里面设置的模式去匹配是加载night对应的资源文件还是正常模式的资源文件,从而达到暗黑适配的目的。
小心得:1.colors的命名可以直接和颜色挂钩比如:
在多人共同适配的时候,为了避免可能存在的冲突,特殊的适配可以单独放在自己的个性化区域书写。
2.一定要注意代码里面动态使用的颜色以及图片资源,不能忘记适配。
3.项目中的组件。如果组件中的color,资源文件等命名有前缀啥的,这样不会和主工程发生冲突,此时只要在主项目中night下的colors.xml以及style,drawable对应的night进行适配就行了。但是如果命名不一致,就会存在适配的冲突,此时需要修改组件或者主项目的冲突的地方。当然还可以和主工程一样在组件里面也弄一套night文件,冲突该咋改还是咋改(当组件的资源和主项目的资源命名一样的时候,在打包合并资源的时候会以主项目为准)
4.对于三方厂商的构件,(一般不让修改),如果他本身的颜色命名和主项目的颜色命名冲突了,自然会造成组件内的有的暗黑有的浅色,非常不好,此时需要和第三方协调进行修改。一般的三方厂商的构件咱们是不做适配的,但是对主项目里面的Activity继承场上构件的这种情况可能需要动态的适配。
也可以这样:在主项目的values中新增一个xml,也可以在里面定义style和color,此时他是可以使用主项目的某个已经适配了的颜色名字。只要让你想要的适配构建的某个颜色或者style的名字和这个新增的文件里的colors名字和style一样,便也进行了适配。如下图:
1.style中Theme修改为:parent="Theme.AppCompat.Light.NoActionBar"
2.
然后就可以了。当切换模式开关的时候,系统自动适配,效果不错。
这个既对原生进行适配,也会对webview网页进行适配。这点很重要。
以上内容基本上就满足了,Android客户端对暗黑模式的适配了,但是,但是,在开发的时候可能会有很多其他的要求,这些要求会导致。我遇到了两个问题
1.网页配置了prefers-color-scheme 这个属性,在 @media
中判断后进行匹配对应的css样式,如下图,以此达到Android网页的暗黑适配。(ios可以,但是Android很多手机适配不了)
body {
color: #333; /* 在 light mode 时,页面中的文字颜色 */
}
@media screen and (prefers-color-scheme: dark) {
body {
color: #fff; /* 如果系统切换到 dark mode 时,页面中的文字颜色改变 */
}
}
但是这个属性要求内核浏览器的的Chromum版本>=81,而目前很多手机都低于这个版本,导致这个实现不了。
所以我找了到了下面的方法:
1.在客户端根据暗黑开关的逻辑去动态注入css样式,可以参考https://blog.csdn.net/anhenzhufeng/article/details/78931586?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-3 这篇文章。
缺点:1.首先你需要对html有一定的基础吧,因为你需要对特殊的地方做适配 2.另外网页上的控件还存在碎片化,导致有处理起来特别麻烦
2.通过Js交互将手机模式状态传递给网页,让其进行处理
3.用咱们的第二种方式,仅去适配WebviewActivity ,给这个Activity去单独适配style就可以了。其他的原生界面可以使用自定义的方式适配(我用的是这种)
2.需求:客户端在某个二级或者三级界面的有一个手动控制暗黑模式的开关,手机是否展示暗黑模式受到手机系统的暗黑开关和此开关的共同约束,并且还要求界面必须停留在当前界面,不允许跳到首页(MainActiivty这种类似的一级界面)。
如果是允许跳到首页,那么很容易,因为清空了任务栈,重新加载的每一个界面都将是和首页一致的(微信就是这样,不管你在哪个界面去改动模式,他都会跳转到首页,并且闪了一下)。 如果不允许,那么当你动态的在当前界面强制设置为某个模式的时候,任务栈中的已经加载的界面是不会改变的。那如何去解决这种需求呢?
很麻烦,能实现,但是客户体验不是很好。 下面我只说思路
1,创建一个全局的缓存,在BaseActivity中每加载一个界面都去将此界面的当前的模式状态存入缓存中
2.当在某个界面切换模式,并且需要改变APP状态的时候,当前界面是可以直接改变的。对于已经加载入栈的其他Activity在onResume里面获取缓存中的模式和当前应该显示的模式进行对比,如果一致,则不需要修改,否则根据逻辑去动态设置。
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);//动态设置浅色
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);//动态设置暗色
并且重新修改缓存中此界面的状态,当destroy时候,移除该界面的存储
3.由于是涉及到老项目的改造,还会遇到这样的问题:setContentView()放在super.onCreate()之前的,(因为可能涉及到逻辑的处理)导致修改模式状态的时候,recreate()之后没渲染成应该成为的。针对这种情况我的处理是在onRestoreInstanceState里面针对这种情况重新加载一次。
@Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (IMApp.getInstance().isChangeConfigMode) { String flag = MeUtils.getString(SystemParamManager.getSystemParam(SystemParam.BIG_TYPE_CUSTOMER_PARAM, SystemParam.SMALL_TYPE_ME_SETTING_DARK_SWITCH)); IMApp.getInstance().isChangeConfigMode = false; if (flag.equals("1") && !isOncreateFirst) { Intent intent = getIntent(); thisActivity.finish(); overridePendingTransition(0, 0); startActivity(intent); } } else { IMApp.getInstance().isChangeConfigMode = false; } }
这些都是在Base里面统一处理的,只要条件设置好即可。
总结:在对多年的复杂老项目进行适配的时候可能会遇到各式各样的问题,这需要我们不断的取尝试,不断的去优化。
未解决的:
华为P30 mate30Pro 这些当前算是新款的手机,只会受到系统控制,暗黑的效果与你自定义的没啥关系,这个弄不了。
第一次安装APP从Splash界面根据 :系统为黑+自己的开关为关 = 我强制把APP设置为亮色,在进入MainActivty的时候,里面第一个加载的Fragment会出现渲染问题,有些线或者图标出现白色,渲染不出来。但是其他的Fragment没事,其他的Activity也没事。退出重新进入,也都没问题了,以后也都没问题了,这个不太懂。