对于app换肤,这是一个常见而又常用的功能。虽然我做的项目中还没涉及到换肤,但是还是想研究下。
于是,下载了鸿洋大神的换肤demo来研究。
先看效果图:(尊重鸿洋大神的代码,效果图上原创)
鸿洋大神的换肤有两种:
1,ChangeSkin;侵入式换肤
2,AndroidChangeSkin;tag换肤
这两种我都分析过。各有各的好处。不过我还是推荐使用第一种。
博文后面我会比较这两种换肤的差异,以及为什么推荐使用第一种tag换肤的原因。
这两种换肤的核心方法其实是一样的,只不过怎么获取需要换肤的资源的方法不同而已。
先来分析关键代码:
先看项目目录:
我们可以从资源的命名看出来,资源的差异在于后缀名的不同,这是app内部换肤的关键,根据后缀名不同来区分。
而插件式换肤,也就是获得sd卡里的apk文件,然后获得其中的资源文件进行换肤的资源命名也是遵循这个规则。
1,接口
ISkinCallback
/**
* 设置皮肤状态回调
*/
public interface ISkinCallback {
void onStart();//开始
void onError(Exception e);//错误
void onComplete();//完成
}
ISkinListener
/**
* 通知皮肤设置的接口
*/
public interface ISkinListener {
//这个接口用于通知应该改变皮肤资源了
void onSkinChanged();
}
执行换肤的时候其实就是调用的ISkinListener接口而已。所有继承它的view都能得到换肤的通知。
2,保存用户的皮肤喜好设置
SkinSharedPreferencesUtils 类
/**
* 保存用户对该app的皮肤设置
*/
public class SkinSharedPreferencesUtils {
private SharedPreferences sharedPreferences;//键值对实例
public SkinSharedPreferencesUtils(Context context) {
sharedPreferences = context.getSharedPreferences(SkinContacts.PREF_NAME, Context.MODE_PRIVATE);
}
//获得插件路径
public String getPluginPath() {
if (sharedPreferences == null) return "";
return sharedPreferences.getString(SkinContacts.KEY_PLUGIN_PATH, "");
}
//添加插件路径
public void setPluginPath(String pluginPath) {
if (sharedPreferences == null) return;
sharedPreferences.edit().putString(SkinContacts.KEY_PLUGIN_PATH, pluginPath).apply();
}
//获得资源后缀
public String getAttrSuffix() {
if (sharedPreferences == null) return "";
return sharedPreferences.getString(SkinContacts.KEY_PLUGIN_SUFFIX, "");
}
//添加资源后缀
public void setAttrSuffix(String attrSuffix) {
if (sharedPreferences == null) return;
sharedPreferences.edit().putString(SkinContacts.KEY_PLUGIN_SUFFIX, attrSuffix).apply();
}
//获得插件的包名
public String getPluginPackage() {
if (sharedPreferences == null) return "";
return sharedPreferences.getString(SkinContacts.KEY_PLUGIN_PACKAGE, "");
}
//添加插件的包名
public void setPluginPackage(String pluginPackage) {
if (sharedPreferences == null) return;
sharedPreferences.edit().putString(SkinContacts.KEY_PLUGIN_PACKAGE, pluginPackage).apply();
}
//清理当前的sharedPreferences
public boolean clear() {
if (sharedPreferences == null) return false;
return sharedPreferences.edit().clear().commit();
}
}
这没啥好说的,键值对保存在app中。
3,定义换肤常量
SkinContacts类
/**
* 皮肤常量
*/
public class SkinContacts {
public static final String PREF_NAME = "skin_plugin_name";//插件工厂名
public static final String KEY_PLUGIN_PATH = "key_plugin_path";//插件路径
public static final String KEY_PLUGIN_PACKAGE = "key_plugin_package";//插件包名
public static final String KEY_PLUGIN_SUFFIX = "key_plugin_suffix";//插件后缀
public static final String ATTR_PREFIX = "skin:";//资源验证的前缀,只有带有skin的前缀才是要被修改的前缀
public static final int SKIN_TAG = 0x5201314;//表填验证,获得当前view的所有名字
}
4,重点方法1:皮肤资源管理器
ResourceManager类
/**
* 皮肤资源管理器
*/
public class ResourceManager {
private Resources mResources;//资源对象
private String mPluginPackageName;//插件包名
private String mSuffix;//皮肤区别的后缀名
//默认图片和颜色类型
private static final String DEFTYPE_DRAWABLE = "drawable";
private static final String DEFTYPE_COLOR = "color";
public ResourceManager(Resources mResources, String mPluginPackageName, String mSuffix) {
this.mResources = mResources;
this.mPluginPackageName = mPluginPackageName;
this.mSuffix = TextUtils.isEmpty(mSuffix) ? "" : mSuffix;
}
//将资源名添加后缀,用于app内部的皮肤修改
private String appendSuffix(String name) {
//如果设置了皮肤的后缀名,则在资源名称的