. 我们来探讨一下夜间模式。
问: Android夜间模式的实现有几种?
答: 有好多种。(:◎)≡
首先我想到的是,通过下载额外的apk文件,然后在获取该apk文件中的资源文件,然后替换之。当然这种方式就不仅限于夜间模式了,可以有各种模式,也就是更换主题了。采用这种方式的,我猜有,微博和QQ。(:◎)≡
想要了解,可以参考http://www.eoeandroid.com/thread-102060-1-1.html
把所有用到的view全部自定义一套,根据color和drawable命名规范,比如加_night后缀。根据当前设置的日夜间模式,在onDraw之前获取对应的color或者drawable,然后再进行绘制。采用这种方式的,我猜有,网易新闻。(:)≡
这种方式,也是我在之前项目中采取的方式。可以感受一下。 采用这种方式,你需要这样 (colors.xml)
<color name="color_a">#ffffff</color> <color name="color_a_night">#000000</color>然后这样 (attrs.xml)
<attr name="colorA" format="color"/>再这样 (styles.xml)
<style name="Theme.A"> <item name="colorA">@color/color_a</item> </style><style name="Theme.A.Night"> <item name="colorA">@color/color_a_night</item> </style>这样 (activity_xxx.xml)
<View android:id="@+id/xxx" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/colorA"/>如果此处你设置的background是一个drawable文件,你需要创建两个drawable文件(一个日间文件,一个带_night的夜间文件)。如果你>的这个drawable文件是个<selector> </selector>,不好意思,根据其中<item> </item>的种类数量,你的每种<item> </item>需要再double一份。
最后再这样 (xxxActivity.Java)
@Override protected void onCreate(Bundle savedInstanceState) { ... if(!isNightMode()) { setTheme(R.style.Theme_A); } else { setTheme(R.style.Theme_A_Night); } setContentView(R.layout.activity_xxx); ... }如果需要切换日夜间,先保存切换的模式,然后通过recreate()重启activity
如果需要在代码中去进行颜色设置,你可以这样
int[] attrs = { R.attr.colorA }; TypedArray ta = context.getTheme().obtainStyledAttributes(attrs); int color = ta.getColor(ArrayUtils.indexOf(attrs, R.attr.colorA), 0); ta.recycle(); textView.setTextColor(color);基本的流程就是这样。不管你受没受够,反正我受够了。我就感觉自己挖了一个坑,当color和drawable越来越多,我就越陷越深,这是什么样的一种感受,没错,就是绝望。
这种方式离我即将要说的方式越来越近了,你仅需要在res目录下,再创建一个带-night后缀的values文件夹values-night,再创建一个colors.xml文件,然后创建颜色<colorname="color_a">#000000</color>,注意,这里颜色命名不用添加_night后缀,与values文件夹中的颜色命名相同即可,只是这里的色值需要更换成夜间的。 然后在xxxActivity.java中
private NightModeHelper mNightModeHelper; @Override protected void onCreate(Bundle savedInstanceState) { ... mNightModeHelper = new NightModeHelper(this, R.style.Theme_A); setContentView(R.layout.activity_xxx); ... } ... //需要切换的地方调用该方法 mNightModeHelper.toggle(); ...NightModeHelper在这里https://gist.github.com/slightfoot/c508cdc8828a478572e0
这样就成功了,节省了很多繁冗的步骤。曾经我一度以为这是我最后的救命稻草,直到我遇到了她。。。
重要的事情说三遍!恩,就是这个家伙,不知道什么时候(我是使用的是v23.2.0 Support包),这个家伙里面有了一个这个方法setDefaultNightMode(xxx)。这个方法可以设置四个值:
MODE_NIGHT_NO 日间模式MODE_NIGHT_YES 夜间模式MODE_NIGHT_AUTO 根据时间自动切换日夜间模式MODE_NIGHT_FOLLOW_SYSTEM 默认模式,就是跟随系统的设置,据说有可能以后会在android系统设置中添加日夜间模式的设置,此时如果你的app是默认模式,会根据系统设置变化日夜间模式只要你的activity继承AppCompatActivity,app的sdk最低版本在14以上,你在任何地方都可以调用AppCompatDelegate.setDefaultNightMode(xxx),因为这是个静态方法,设置完之后新开启的页面,都会采用新的模式。你需要在每次切换模式之后,把当前模式保存在本地,然后在下次打开app的时候,获取当前模式并调用这个方法设置一下,就可以使app保持之前的模式。
方法与之前的NightModeHelper类似,创建带-night后缀的文件夹(比如:values-night),然后添加你的资源文件,资源文件需要相同的命名(比如:colors.xml),这样就把夜间资源和日间资源关联起来了。这里注意一下,如果是drawable-xxhdpi中的资源需要夜间模式,这些夜间的资源就应该放在drawable-night-xxhdpi文件夹中。这里其实灵活性很大的,比如你可以在values-night中创建一个strings.xml,实现日夜间显示不同的文本。甚至你可以在layout-night中创建一个同样名字的布局文件,实现日夜间显示不同的布局。
官方为了方便开发者,在最新的v23.2.0 Support包增加了一个Theme.AppCompat.DayNight主题,如果继承这个主题,会有一些配置好的属性,如果你喜欢官方的配色,可以考虑继承这个主题,也会省下不少功夫。
还有个方法也提一下,就是setLocalNightMode(xxx),同样可以设置上述提到的四个值。但是这个方法只能通过getDelegate().setLocalNightMode(xxx)调用,这个方法只对当前activity有效果,并且设置完之后需要recreate()才能使其生效。
还没完,夜间模式还有个让人头疼的问题就是,当前页面和已经开启的页面的实时更新。之前说的这么多夜间模式方案,都是让之后新的页面获取新的资源,而旧的页面怎么办?
如果你的页面不是很复杂,那就把所有的View再重新设置一遍颜色或者图片。可以直接setTextColor(R.color.xxx) setBackground(R.drawable.xxx)。也可以自定View,重新获取一下资源文件再设置一下。这里还有个前提是,需要更新的activity都得调用getDelegate().setLocalNightMode(xxx)而不是AppCompatDelegate.setDefaultNightMode(xxx),因为后者只是更改了一个全局变量,所以并不会使资源文件立即变化。
如果你的页面很复杂,那就 . . . . . . recreate()吧!(:◎)≡(如果你有好的方案,也特别希望你能告知我一下!)
原文地址