原生默认支持黑夜白天主题自适应,只要应用主题设置DayNight即可
若存在场景,需要做类似于换肤功能,需要自行获取资源,原生无法支持完全
基于原生night适配
Android主题机制
可自动改变样式的前提是使用DayNight主题
当系统主题切换时,会去自动去XXXX/XXXX-night目录寻找资源。
如果只使用Light
或Dark
主题,则无法进行适配,只会去对应资源目录找资源。
TODO:源码分析主题切换原理
Android原生获取资源代码
可以看到,我们常规用的获取资源方法,直接传入id
即使getDrawable(@DrawableRes int id)
已经废弃,我们使用新方法,第二个参数也可以直接传入null进行获取资源。
我们可以关注这些2入参方法,第二个入参Theme
public final Drawable getDrawable(@DrawableRes int id) {
return getResources().getDrawable(id, getTheme());
}
public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) throws NotFoundException {
return getDrawableForDensity(id, 0, theme);
}
public final int getColor(@ColorRes int id) {
return getResources().getColor(id, getTheme());
}
public final ColorStateList getColorStateList(@ColorRes int id) {
return getResources().getColorStateList(id, getTheme());
}
手动获取主题资源的方式
1. 设置主题,获取资源
基于上一步中的方法,默认重载方法的第二个参数传入getTheme()
,即获取当前主题,然后去取当前主题的资源显示。
基于此我们可以先进行主题设置,再获取资源。
setTheme(androidx.appcompat.R.style.Base_Theme_AppCompat_Light);
getDrawable(R.drawable.bg_seek_bar);
setTheme(androidx.appcompat.R.style.Base_Theme_AppCompat_Dark);
getDrawable(R.drawable.bg_seek_bar);
设置为Light主题,将自动获取drawable
目录下的资源
设置为Dark主题,自动获取drawable-night
目录资源,若获取不到,仍然会返回drawable
目录同名资源
缺陷:
必须要更换当前主题才可获取到对应的资源。
2. 构建主题对象,通过主题对象获取
因为允许接受其他Theme
对象,不一定非要使用getTheme
去做,可以根据主题手动构建对象,去获取资源。
Resources.Theme theme = getResources().newTheme();
theme.applyStyle(androidx.appcompat.R.style.Base_Theme_AppCompat_Light, true);
getDrawable(R.drawable.bg_seek_bar,theme);
通过getResources().newTheme()
创建一个空的theme对象,之后应用主题style,便可以通过手动传入主题的方式获取对应主题的资源。
拼接资源后缀适配
原生night主题有着最大的限制就是无法适配2个以上的主题样式。如果需要适配多个主题,可使用该方案
Android资源包限制
虽然原生Android支持设置多个res目录,但是仍然无法重名
因为在打包时仍然会把多个res打包在一起,这就导致该方式无法像原生黑夜主题一样使用同名方式去适配。
拼接后缀获取资源
1. 定义多个res目录
虽然最终会打包在一起,但是为了方便维护与迭代,创建多个res目录还是有必要的
先创建一个资源包用于放置特定主题资源
gradle文件定义sourceSets
android {
...
sourceSets {
main.res.srcDirs += 'src/main/res_green'
}
...
}
2. 资源拼接后缀
如常规drawable
的按钮资源名叫switch_enable``switch_disable
在res_green
的文件名统一调整为switch_enable_g``switch_disable_g
所有的资源名按照XXX_g
的方式命名
3. 获取资源
int id = R.drawable.bg_seek_bar;
// 获取文件名,getResourceName方法获取的名称包含包名和类型名,此处不适用
String name = getResources().getResourceEntryName(id);
if (isGreenTheme) {
name+="_g";
}
// 第二个参数需要指定类型
int greenId = getResources().getIdentifier(name,"drawable", getPackageName());
if (greenId > 0) {
id =greenId;
}
缺点:
- 资源需要改命名比较繁琐
- 资源id和名称多次转换,效率不高
三方主题包
三方有很多Skin相关的sdk,通过资源打包成文件的方式引入
后续补充三方资源
缺点:
依赖三方,额外引入资源
本文由 bt 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。