Android主题主动适配方案

Android主题主动适配方案

原生默认支持黑夜白天主题自适应,只要应用主题设置DayNight即可

若存在场景,需要做类似于换肤功能,需要自行获取资源,原生无法支持完全

基于原生night适配

Android主题机制

可自动改变样式的前提是使用DayNight主题

当系统主题切换时,会去自动XXXX/XXXX-night目录寻找资源。

如果只使LightDark主题,则无法进行适配,只会去对应资源目录找资源。

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;

}

缺点:

1. 资源需要改命名比较繁琐

2. 资源id和名称多次转换,效率不高

三方主题包

三方有很多Skin相关的sdk,通过资源打包成文件的方式引入

后续补充三方资源

缺点:

依赖三方,额外引入资源

Android常用命令 2026-01-08

评论区