该文章接上篇 Android实现夜间模式的方法(一)
三.夜间模式的实现方案——单纯夜间模式
1.通过切换主题实现
这是通过创建一套夜间模式的主题,然后在设置时进行切换。在Activity中有一个方法叫setTheme(),可以设置Activity的Theme,当然Application类中也有相同的方法,也可以在Application中设置当前应用的Theme。
首先在attrs.xml中,为需要随theme变化的内容设置自定义属性
<resources>
<attr name="textColor" format="color|reference" />
<attr name="mainBackground" format="color|reference" />
</resources>
然后定义自己的夜间模式的风格,示例如下:
<!-- 夜间 -->
<style name="ThemeNight" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="mainBackground">#000000</item>
<item name="textColor">#ffffff</item>
</style>
接下来只要在布局文件中使用对应的值,通过?attr/属性名的方法就可以实现根据风格设置不同的属性的目的。
这种方法是是Android官方推荐的方式,较为简单常用但这种方式有一些不足,规模较大的应用,需要随theme变化的属性会很多,都需要逐一定义。
2.通过修改资源id映射方式实现
这种方法的思路是通过id获取资源时,先将其转换为夜间模式对应id,再通过Resources来获取对应的资源。
public static Drawable getDrawable(Context context, int id) {
return context.getResources().getDrawable(getResId(id));
}
public static int getResId(int defaultResId) {
if (!isNightMode()) {
return defaultResId;
}
if (sResourceMap == null) {
buildResourceMap();
}
int themedResId = sResourceMap.get(defaultResId);
return themedResId == 0 ? defaultResId : themedResId;
}
可以通过HashMap将白天模式的resId和夜间模式的resId来一一对应起来
private static void buildResourceMap() {
sResourceMap = new SparseIntArray();
sResourceMap.put(R.drawable.common_background, R.drawable.common_background_night);
// ...
}
3.通过使用系统提供的夜间模式主题实现
在Support Library 23.2.0添加了一条新的变化 AppCompat 现在有个新的主题:Theme.AppCompat.DayNight. 这个主题可以根据系统时间切换 Theme.AppCompat(暗色) 和 Theme.AppCompat.Light(亮色) 两种主题。这将会对应用的用户特别有用,特别是阅读类应用。需要注意的是,这个特性只支持 API v14 及以上的 Android 设备,在 API v14 以下的设备则会默认使用亮色的主题。而且activity必须继承要AppCompatActivity。
首先得先在你的 style.xml 文件里,让主题继承 DayNight 主题,例如:
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
然后在 Application 添加一个静态代码块来进行初始化全局设置:
static {
AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_AUTO);
}
然后使用自定义资源,只需要在 res 目录下创建对应的 values-night 文件夹并创建对应的 themes.xml 文件,重新定义夜间模式的主题风格,例如:
res/values-night/colors.xml
<color name="colorPrimary">#201D45</color>
<color name="colorPrimaryDark">#201D45</color>
同理其他资源你只需要在文件的末尾添加 -night 系统就会自动加载对应的文件了。
实现的效果如下示例:
4.通过使用开源框架实现——Android-Skin-Loader
Android-Skin-Loader是一个通过动态加载本地皮肤包进行换肤的皮肤框架
使用方法:
- 在Application中进行初始化
public class SkinApplication extends Application {
public void onCreate() {
super.onCreate();
// Must call init first
SkinManager.getInstance().init(this);
SkinManager.getInstance().load();
}
}
- 在布局文件中标识需要换肤的View
xmlns:skin="http://schemas.android.com/android/skin"
- 继承BaseActivity或者BaseFragmentActivity作为BaseActivity进行开发
- 从.skin文件中设置皮肤
SkinManager.getInstance().load(skin.getAbsolutePath(),
new ILoaderListener() {
//重写该类下的方法
});
皮肤包(后缀名为.skin)的本质是一个apk文件,该apk文件不包含代码,只包含资源文件
在皮肤包工程中(示例工程为skin/BlackFantacy)添加需要换肤的同名的资源文件,直接编译生成apk文件,再更改后缀名为.skin即可(防止用户点击安装)
使用gradle的同学,buildandroid-skin-loader-skin工程后即可在skin-package目录下取皮肤包(修改脚本中def skinName = "BlackFantacy.skin"换成自己想要的皮肤名)
Load包下的两个类
SkinInflaterFactory: 搜集需要的换肤的控件,并创建相应的换肤控件,并把需要换肤的空间及其相应支持的换肤属性存储起来。
SkinManager: 其内部通过反射调用AssetManager.addAssetPath()把外部的皮肤资源加载到AssetManager中,并通过该AssetManager创建相应的Resource。当执行换肤操作的时候,就可以设置需要换肤View的相关属性为Resource中相应的资源。
5.通过使用开源框架实现——NightModel
NightModel是个方便切换夜间模式的库,利用官方夜间模式,同时不用重启Activity
使用方法
1.在appication中初始化
2.只需要在需要刷新的activity中调用attach、detach方法。其它activity不需要调用此方法
3.切换时调用appleyDayModel\appleyNightModel进行夜间模式切换
使用要求
官方包support appcompat 23.2.0 或以上版本
activity 需要继承自 AppCompatActivity
应用按照官方的夜间模式实现
6.通过使用开源框架实现——MultipleTheme
MultipleTheme是一个支持无缝换肤/夜间模式的Android框架,配合theme和换肤控件框架可以做到无缝切换换肤(无需重启应用和当前页面)。需要在换肤/切换夜间模式的界面只需要使用框架里的自封装控件,其他界面的控件使用原生android控件即可。
第一步:在项目的attr.xml声明自定义属性(各种模式都会用到的属性)
第二步:在项目的style.xml指定各种模式主题下的自定义属性值
第三步:在页面布局文件里使用自定义属性值
第四步:在基类的onCreate方法里添加切换主题模式的逻辑代码
第五步:调用工具类方法切换主题模式
第六步:针对切换主题模式时需要立即更新页面ui的页面,需要使用框架里的封装控件