庫(kù)源碼: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滑进、新建一個(gè)項(xiàng)目模塊(只包含有資源文件,例如本項(xiàng)目的 skinpackage 模塊),其中包含的資源文件的 name 一定要和原項(xiàng)目中有換膚需求的 View 所使用的資源name一致募谎。
6扶关、拿到上一步生成的文件( ×××.apk ),改名為 ×××.skin数冬,放入 assets 中的 skin 目錄下( skin 目錄是自己新建的)
7节槐、調(diào)用換膚
-
在 <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(); } } );
-
皮膚來(lái)源于網(wǎng)絡(luò)
SkinManager.getInstance().loadSkinFromUrl(skinUrl, new ILoaderListener() { @Override public void onStart() { Log.i("ILoaderListener", "正在切換中"); dialog.setContent("正在從網(wǎng)絡(luò)下載皮膚文件"); 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); } });
詳細(xì)的使用,請(qǐng)到示例項(xiàng)目中查看
2.換膚屬性的擴(kuò)展
本開源庫(kù)默認(rèn)支持 textColor 和 background 的換膚拐纱。如果你還需要對(duì)其他屬性進(jìn)行換膚铜异,那么就需要去自定義了。
那么如何自定義呢秸架?看下面這個(gè)例子:
TabLayout大家應(yīng)該都用過(guò)吧揍庄。它下面會(huì)有一個(gè)指示器,當(dāng)我們換膚的時(shí)候也希望這個(gè)指示器的顏色也跟著更改东抹。
新建一個(gè) TabLayoutIndicatorAttr 繼承于 SkinAttr蚂子,然后重寫 apply 方法。apply 方法在換膚的時(shí)候就會(huì)被調(diào)用
代碼的詳細(xì)實(shí)現(xiàn)
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 是用來(lái)獲取皮膚包里的資源食茎,這里設(shè)置color或者drawable一定要使用本工具類。
當(dāng)上面的工作完成之后馏谨,就到我們自己的 Application 的 onCreate 方法中加入<code> SkinConfig.addSupportAttr("tabLayoutIndicator", new TabLayoutIndicatorAttr());</code>
最后我們就可以正常使用了别渔,<code>dynamicAddSkinEnableView(tablayout, "tabLayoutIndicator", R.color.colorPrimaryDark);</code>
3. 關(guān)于字體切換
還是遵守本項(xiàng)目的約定大于配置的原則,所有的字體都放到 assets/fonts 文件夾下
如何切換字體:
<code> SkinManager.getInstance().loadFont("xx.ttf")</code>
關(guān)于切換字體需要配置的東西:
如果只是單純的想要字體切換這個(gè)功能惧互。只需<code>集成步驟</code>中的前三步就行了哎媚。
注:字體切換功能默認(rèn)不開啟,需要字體切換功能請(qǐng)?jiān)谀愕腁pplication中加入<code>SkinConfig.setCanChangeFont(true);</code>
4. 其他一些重要的api
SkinConfig.isDefaultSkin(context):判斷當(dāng)前皮膚是否是默認(rèn)皮膚
SkinManager.getInstance().restoreDefaultTheme(): 重置默認(rèn)皮膚
dynamicAddView:當(dāng)動(dòng)態(tài)創(chuàng)建的View也需要換膚的時(shí)候,就可以調(diào)用 dynamicAddView
5. 使用注意事項(xiàng):
換膚默認(rèn)只支持 android 的常用控件壹哺,對(duì)于支持庫(kù)的控件和自定義控件的換膚需要?jiǎng)討B(tài)添加(例如: <code>dynamicAddSkinEnableView(toolbar, "background", R.color.colorPrimaryDark);</code>)抄伍,在布局文件中使用<code>skin:enable="true"</code>是無(wú)效的。
默認(rèn)不支持狀態(tài)欄顏色的更改管宵,如果需要換膚的同時(shí)也要更改狀態(tài)欄顏色截珍,請(qǐng)到您的Application文件中加入<code>SkinConfig.setCanChangeStatusColor(true);</code>,狀態(tài)欄的顏色值來(lái)源于<code>colorPrimaryDark</code>
本開源庫(kù)使用的 Activity 是 AppCompatActivity箩朴,使用的 Fragment 是 android.support.v4.app.Fragment
有換膚需求 View 所使用的資源一定要是引用值岗喉,例如:@color/red,而不是 #ff0000
補(bǔ)充:支持庫(kù)控件的皮膚修改
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 修改點(diǎn)擊時(shí)的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);