昨晚发过这篇,其中我关于“掘金获得谷歌国内最佳Material Design应用提名”的说法有问题,被谷歌的朋友提醒后,决定还是删了重发,非常抱歉之前帮忙转发的朋友,即便如此,也并不影响我对掘金的推荐,确实是非常棒的一款产品,技术干货超多,大家可以多关注 http://gold.xitu.io
今天这篇译文就来自稀土掘金Android工程师 NeXT 同学,NeXT非常热衷分享,掘金上大量的好文章都分享自他。
原文来自Android大神Chris Banes介绍Android夜间模式新特性的文章,我之前给大家也推过关于Android夜间模式最佳实践的文章,以前Android官方对夜间模式的支持不太给力,因此大家也想出了各种方案,今后也许可以都采用这个方案实现吧,当然它也有一些限制,比如仅支持API 14及以上、仍然需要recreate Activity, 不过我个人以为现在新开发Android应用已经无须再顾及API 14以下的用户了,而且recreate Activity其实也没什么大不了,各位不要太纠结(主要是PM不要太纠结)。
如果看过了 Support Library 23.2.0 博客(http://android-developers.blogspot.com/2016/02/android-support-library-232.html),你就会知道 AppCompat 现在有个新的主题:Theme.AppCompat.DayNight.
这个主题可以根据系统时间切换 Theme.AppCompat(暗色) 和 Theme.AppCompat.Light(亮色) 两种主题。这将会对应用的用户特别有用,特别是阅读类应用(这已经成为了阅读软件的标配)。需要注意的是,这个特性只支持 API v14 及以上的 Android 设备,在 API v14 以下的设备则会默认使用亮色的主题。
DayNight 主题使用起来很简单,只需要把你的主题继承 DayNight 主题,然后应用。例如:
<style name="MyTheme" parent="Theme.AppCompat.DayNight">
-- Blah blah -->
style>
然后在程序中进行主题的初始化。你需要调用 AppCompatDelegate.setDefaultNightMode() ,它有四个参数:
MODE_NIGHT_NO. 使用亮色(light)主题
MODE_NIGHT_YES. 使用暗色(dark)主题
MODE_NIGHT_AUTO. 根据当前时间自动切换 亮色(light)/暗色(dark)主题
MODE_NIGHT_FOLLOW_SYSTEM(默认选项). 设置为跟随系统,通常为 MODE_NIGHT_NO
你可以在任何时候调用这个方法,因为这个方法是静态的。你设置的值不是一直存在的,所以你需要在每次程序开启进程时重新设置。我建议你在 application 类或者 Activity 中添加一个静态代码块来进行设置,比如:
static {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_...);
}
public class MyApplication extends Application {
你可以通过调用 AppCompatDelegate 的 setLocalNightMode() 方法来重写每个组件的默认值。如果你知道有一些组件是使用 DayNight 方法的时候,这是很好用的。对与开发者来说,你不用等到太阳下山就可以测试夜间模式了呢。
请注意,这个方法并不会实时更新布局。你需要调用 recreate() 来通知页面更新。使用方式如下:
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
//Set the local night mode to some value
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_...);
//调用recreate()使设置生效
recreate();
}
}
}
只需要检测资源配置:
int currentNightMode = getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
// Night mode is not active, we're in day time
case Configuration.UI_MODE_NIGHT_YES:
// Night mode is active, we're at night!
case Configuration.UI_MODE_NIGHT_UNDEFINED:
// We don't know what mode we're in, assume notnight
}
艾玛,我看不见字了(黑色文字黑色背景)。艾玛,我的图标怎么这么丑。
当这个功能更改了程序主题时,你需要确定你的 布局/样式/图片 都适配了亮色(light)和暗色(dark)主题。
这种资源适配的经验法则就是:尽可能的使用主题属性(theme attributes)。下面是一些需要重点了解的:
?android:attr/textColorPrimary. 系统默认的文字颜色。在亮色(light)主题下,颜色接近黑色,在暗色(dark)主题下,颜色接近白色。Contains a disabled state.
?attr/colorControlNormal. 系统默认的图标颜色
需要重点注意 WebView. WebView 不能使用主题属性(theme attributes),并且我们很难控制网页内容的样式,所以很有可能你的 WebView 跟程序其他主题颜色不符。所以,请确认你的 WebView 页面的主题尽量跟应用当前主题相同。
想要精确的自动切换日夜间模式,需要获取系统的位置。这可能会让开发者感到菊花一紧,不过不用害怕,如果你的程序被授予了坐标权限(location permission),AppCompat 会试着获取 LocationManager 中上次保存的坐标,根据坐标计算日出日落时间。并不需要程序请求任何权限。
如果程序没有位置权限(或者 LocationManager 没有存储上次坐标的信息),那么,系统会默认设置为早上6点钟为日出,下午10点钟为日落,如果用户调整系统时间,当前的主题也会随之改变。
让我们假设一下应用场景,帮你更清晰的思考这个问题:
更改主题,使它继承自 Theme.AppCompat.DayNight
添加用户切换主题的设置项。把用户设置的信息存储到本地(比如 SharedPreference)。根据用户选择的参数调用 setDefaultNightMode() 方法。
下次启动程序的时候,读取本地文件然后根据用户设置,调用 setDefaultNightMode() 方法。
重点来了:我们不希望用户在设定主题后,主题还会根据当前的时间突然改变。请记得,默认的主题是 MODE_NIGHT_FOLLOW_SYSTEM, 如果我们添加一个用户可以设置主题的功能,AppCompat 会默认使用
使用自定义资源,只需要在 res 目录下创建对应的 values-night 文件夹并创建对应的 themes.xml 文件:
res/values/themes.xml
<style name="Theme.AppCompat.DayNight"
parent="Theme.AppCompat.Light" />
res/values-night/themes.xml
<style name="Theme.AppCompat.DayNight"
parent="Theme.AppCompat" />
只要在对应的资源文件夹后添加 -night 后缀,比如:drawable-night、values-night, 等等…
我们不保证这种用法适用于全部应用,但是如果你在合适的时机使用,还是会很有帮助的。
译者注:如果你想了解更多 AppCompat v23.2 的新特性,推荐你看一下秋百万的android-support-23.2-sample
https://github.com/liaohuqiu/android-support-23.2-sample