库源码:https://github.com/burgessjp/ThemeSkinning
参考:https://github.com/burgessjp/MaterialDesignDemo
Demo:https://github.com/huangshuyuan/SkinDemo/
1.集成步骤:
1、添加依赖 compile 'com.solid.skin:skinlibrary:1.4.3' 参考源码版本
2、让你的 Application 继承于 SkinBaseApplication
3、让你的 Activity 继承于 SkinBaseActivity,如果使用了 Fragment 则继承于 SkinBaseFragment
4、在需要换肤的根布局上添加 xmlns:skin="http://schemas.android.com/android/skin"
,然后在需要换肤的View上加上skin:enable="true"
5、新建一个项目模块(只包含有资源文件,例如本项目的 skinpackage 模块),其中包含的资源文件的 name 一定要和原项目中有换肤需求的 View 所使用的资源name一致。
6、拿到上一步生成的文件( ×××.apk ),改名为 ×××.skin,放入 assets 中的 skin 目录下( skin 目录是自己新建的)
7、调用换肤
-
在 <code>assets/skin</code> 文件夹中的皮肤
SkinManager.getInstance().loadSkin("Your skin file name in assets(eg:theme.skin)", new ILoaderListener() { @Override public void onStart() { Toast.makeText(getApplicationContext(), "正在切换中", Toast.LENGTH_SHORT).show(); } @Override public void onSuccess() { Toast.makeText(getApplicationContext(), "切换成功", Toast.LENGTH_SHORT).show(); } @Override public void onFailed() { Toast.makeText(getApplicationContext(), "切换失败", Toast.LENGTH_SHORT).show(); } } );
-
皮肤来源于网络
SkinManager.getInstance().loadSkinFromUrl(skinUrl, new ILoaderListener() { @Override public void onStart() { Log.i("ILoaderListener", "正在切换中"); dialog.setContent("正在从网络下载皮肤文件"); dialog.show(); } @Override public void onSuccess() { Log.i("ILoaderListener", "切换成功"); dialog.dismiss(); } @Override public void onFailed(String errMsg) { Log.i("ILoaderListener", "切换失败:" + errMsg); dialog.setContent("换肤失败:" + errMsg); } @Override public void onProgress(int progress) { Log.i("ILoaderListener", "皮肤文件下载中:" + progress); dialog.setProgress(progress); } });
详细的使用,请到示例项目中查看
2.换肤属性的扩展
本开源库默认支持 textColor 和 background 的换肤。如果你还需要对其他属性进行换肤,那么就需要去自定义了。
那么如何自定义呢?看下面这个例子:
TabLayout大家应该都用过吧。它下面会有一个指示器,当我们换肤的时候也希望这个指示器的颜色也跟着更改。
新建一个 TabLayoutIndicatorAttr 继承于 SkinAttr,然后重写 apply 方法。apply 方法在换肤的时候就会被调用
代码的详细实现
public class TabLayoutIndicatorAttr extends SkinAttr {
@Override
public void apply(View view) {
if (view instanceof TabLayout) {
TabLayout tl = (TabLayout) view;
if (RES_TYPE_NAME_COLOR.equals(attrValueTypeName)) {
int color = SkinResourcesUtils.getColor(attrValueRefId);
tl.setSelectedTabIndicatorColor(color);
}
}
}
}
注:attrValueRefId:就是资源 id。SkinResourcesUtils 是用来获取皮肤包里的资源,这里设置color或者drawable一定要使用本工具类。
当上面的工作完成之后,就到我们自己的 Application 的 onCreate 方法中加入<code> SkinConfig.addSupportAttr("tabLayoutIndicator", new TabLayoutIndicatorAttr());</code>
最后我们就可以正常使用了,<code>dynamicAddSkinEnableView(tablayout, "tabLayoutIndicator", R.color.colorPrimaryDark);</code>
3. 关于字体切换
还是遵守本项目的约定大于配置的原则,所有的字体都放到 assets/fonts 文件夹下
如何切换字体:
<code> SkinManager.getInstance().loadFont("xx.ttf")</code>
关于切换字体需要配置的东西:
如果只是单纯的想要字体切换这个功能。只需<code>集成步骤</code>中的前三步就行了。
注:字体切换功能默认不开启,需要字体切换功能请在你的Application中加入<code>SkinConfig.setCanChangeFont(true);</code>
4. 其他一些重要的api
SkinConfig.isDefaultSkin(context):判断当前皮肤是否是默认皮肤
SkinManager.getInstance().restoreDefaultTheme(): 重置默认皮肤
dynamicAddView:当动态创建的View也需要换肤的时候,就可以调用 dynamicAddView
5. 使用注意事项:
换肤默认只支持 android 的常用控件,对于支持库的控件和自定义控件的换肤需要动态添加(例如: <code>dynamicAddSkinEnableView(toolbar, "background", R.color.colorPrimaryDark);</code>),在布局文件中使用<code>skin:enable="true"</code>是无效的。
默认不支持状态栏颜色的更改,如果需要换肤的同时也要更改状态栏颜色,请到您的Application文件中加入<code>SkinConfig.setCanChangeStatusColor(true);</code>,状态栏的颜色值来源于<code>colorPrimaryDark</code>
本开源库使用的 Activity 是 AppCompatActivity,使用的 Fragment 是 android.support.v4.app.Fragment
有换肤需求 View 所使用的资源一定要是引用值,例如:@color/red,而不是 #ff0000
补充:支持库控件的皮肤修改
1.FloatingActionButton修改backgroundTint
需要自定义:
public class FabButtonAttr extends SkinAttr {
@Override
public void apply(View view) {
if (view instanceof FloatingActionButton) {
FloatingActionButton fb = (FloatingActionButton) view;
if (RES_TYPE_NAME_COLOR.equals(attrValueTypeName)) {
int color = SkinManager.getInstance().getColor(attrValueRefId);
fb.setBackgroundTintList(ColorStateList.valueOf(color));
} else if (RES_TYPE_NAME_DRAWABLE.equals(attrValueTypeName)) {
// tv.setDivider(SkinManager.getInstance().getDrawable(attrValueRefId));
}
}
}
}
Application中添加:
SkinConfig.addSupportAttr("backgroundTint",new FabButtonAttr());
使用:
//添加改变的组件
dynamicAddView(fab, "backgroundTint", R.color.colorPrimary);
2.NavigationView 修改点击时的item颜色
需要自定义:
public class NavigationViewAttr extends SkinAttr {
@Override
public void apply(View view) {
if (view instanceof NavigationView) {
Log.i("TabLayoutAttr", "apply");
NavigationView nv = (NavigationView) view;
if (RES_TYPE_NAME_COLOR.equals(attrValueTypeName)) {
Log.i("TabLayoutAttr", "apply color");
int color = SkinManager.getInstance().getColor(attrValueRefId);
nv.setItemTextColor(createSelector(color));
nv.setItemIconTintList(createSelector(color));
} else if (RES_TYPE_NAME_DRAWABLE.equals(attrValueTypeName)) {
Log.i("TabLayoutAttr", "apply drawable");
// tv.setDivider(SkinManager.getInstance().getDrawable(attrValueRefId));
}
}
}
private ColorStateList createSelector(int color) {
int statePressed = android.R.attr.state_checked;
int stateChecked = android.R.attr.state_checked;
int[][] state = {{statePressed}, {-statePressed}, {stateChecked}, {-stateChecked}};
int color1 = color;
int color2 = Color.parseColor("#6E6E6E");
int color3 = color;
int color4 = Color.parseColor("#6E6E6E");
int[] colors = {color1, color2, color3, color4};
ColorStateList colorStateList = new ColorStateList(state, colors);
return colorStateList;
}
}
Application中添加:
SkinConfig.addSupportAttr("navigationViewMenu",new NavigationViewAttr());
使用:
//添加改变的组件
dynamicAddView(navigationView.getHeaderView(0), "background", R.color.colorPrimary);
dynamicAddView(navigationView, "navigationViewMenu", R.color.colorPrimary);