如果在网上搜换肤,方案五花八门,但是根据app的需求,以及无设计师的情况下,基本上简约风格app,这种风格下只需要几个颜色就行了,根本不需要动态从磁盘加载皮肤apk,而且通过反射操作侵入性太强,因此attr大法才是最适合目前的我所做的app实现。
网上的换肤方法侵入性太强,而纯色app不需要各种花式的皮肤,基本上2三套颜色就行了,主色,次色,而其他则非黑即白。深色模式实现就更简单了,用着色tint就实现了。
经过了几天的研究发现,动态修改setTheme是有bug的, bug就是状态栏颜色和actionbar在未在activity定义attr背景的情况下实现修改actionbar的就有这个bug,2015年在stackoverflow网站上就有人提出解决方法,这个解决方法是没有办法的方法,需要在baseactivity进行判断,如果有actionbar就设置actionbar颜色,
以及设置状态栏颜色。
达到的效果在颜色选择器中是可以直接使用实现的属性?attr
而在drawable,则需要包裹一层drawable引用color,直接使用color属性也是会报错的,
drawable要使用颜色属性只能包裹一层shape
第三方自定义ui 有的是只支持颜色不支持attr的。则需要进行额外适配处理,
其处理方式就是根据属性查找对应的颜色 或者id (colorres)
颜色属性找颜色
public static int attrFetchColor(int attrColor) {
int[] attribute = new int[]{attrColor};
TypedArray array = AppContext.getInstance().getTheme().obtainStyledAttributes(attribute);
int color = array.getColor(0, AppContext.getInstance().getResources().getColor(R.color.themeColor));
array.recycle();
return color;
}
查找id
public static int attrFetchColorId(int attrColor) {
int[] attribute = new int[]{attrColor};
TypedArray array = AppContext.getInstance().getTheme().obtainStyledAttributes(attribute);
int colorID = array.getResourceId(0, R.color.themeColor);
array.recycle();
return colorID;
}
在baseactivity onCreate中修改状态栏颜色
public static void updateActionBarAndStatusColor(Activity activity) {
if (activity instanceof AppCompatActivity) {
AppCompatActivity appCompatActivity = (AppCompatActivity) activity;
ActionBar supportActionBar = appCompatActivity.getSupportActionBar();
if (supportActionBar != null) {
int i = AppUtils.attrFetchColor(R.attr.defaultThemeColor);
supportActionBar.setBackgroundDrawable(new ColorDrawable(i));
}
}
android.app.ActionBar actionBar = activity.getActionBar();
if (actionBar != null) {
int color = AppUtils.attrFetchColor(R.attr.defaultThemeColor);
actionBar.setBackgroundDrawable(new ColorDrawable(color));
}
if (Build.VERSION.SDK_INT >= 21) {
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
int statusBarBackground = AppUtils.attrFetchColor(R.attr.statusBarBackground);
Window window = activity.getWindow();
// window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// window.setStatusBarColor(backgroundColor);
window.setStatusBarColor(statusBarBackground);
}
}
适配起来挺快的,通过各种批量替换
但是需要注意的是,第三方对颜色适配做的很烂,哪怕大家众所周知的smartrefresh也不支持属性引用颜色。 因此主要的适配就是处理第三方框架的问题,
这个适配工作基本上一天就搞定了。
另外花的实际是纠结研究为啥actionbar和状态栏颜色通过setTheme无效花费了很多时间,在attach里面替换,以及研究theme的各种方法,以及引用再引用的方式也解决不了问题,
最后既然无效我只能另外走方法了,强制设置。
attr.xml定义
其实现在每一个主题都实现了,
下面附上theme.xml 仔细看 都实现了defaultThemeColor defaultThemeColorSecond 属性,
color实现
@color/light_blue_A200 @color/purple_200 @color/purple_200 @color/black_overlay @color/black @color/teal_200 @color/teal_200 @color/black @color/light_blue_A200 @color/purple_200 @color/purple_200 @color/black_overlay @color/black @color/light_blue_A200 @color/purple_200 @color/purple_200 @color/black_overlay @color/black @color/light_blue_A200 @color/purple_200 @color/purple_200 @color/black_overlay @color/black @color/light_blue_A200 @color/purple_200 @color/purple_200 @color/black_overlay @color/black @color/light_blue_A200 @color/purple_200 @color/purple_200 @color/black_overlay @color/black
深色主题values-night themes.xml
把正常颜色的拷贝过来然后指定颜色就行了,
修改主题的代码 baseactivityonCreate调用在setContentView之前
fun applySetting(context: Context?):Int {
var themeId = 0;
// 检查主题
val themeModeInt = getSpfThemeMode()
val themeMode = ThemeMode.parseOfInt(themeModeInt)
var containActionBar: Boolean = false;
var actName: String = "";
if (context is AppCompatActivity) {
var act: AppCompatActivity = (context as AppCompatActivity);
if (act.supportActionBar is WindowDecorActionBar) {
containActionBar = true;
}
actName =
context.javaClass.simpleName + " ,是否已支持actionBar " + containActionBar;
}
if (themeMode == ThemeMode.BLUE_THEME) {
if (containActionBar) {
themeId = R.style.Theme_MyApplication_Blue
} else {
themeId = R.style.Theme_MyApplication_Blue_NoActionBar
}
Log.w("ThemeStyle", "蓝色主题 $actName");
} else if (themeMode == ThemeMode.YELLOW_THEME) {
if (containActionBar) {
themeId = R.style.Theme_MyApplication_Yellow
} else {
themeId = R.style.Theme_MyApplication_Yellow_NoActionBar
}
Log.w("ThemeStyle", "黄色主题 $actName");
} else if (themeMode == ThemeMode.PURPLE_THEME) {
Log.w("ThemeStyle", "紫色主题 $actName");
if (containActionBar) {
themeId = R.style.Theme_MyApplication_PURPLE
} else {
themeId = R.style.Theme_MyApplication_PURPLE_NoActionBar
}
} else if (themeMode == ThemeMode.GREEN_THEME) {
Log.w("ThemeStyle", "绿色主题 $actName");
if (containActionBar) {
themeId = R.style.Theme_MyApplication_GREEN
} else {
themeId = R.style.Theme_MyApplication_GREEN_NoActionBar;
}
} else if (themeMode == ThemeMode.BLACK_THEME) {
Log.w("ThemeStyle", "黑色主题 $actName");
if (containActionBar) {
themeId = R.style.Theme_MyApplication_BLACK;
} else {
themeId = R.style.Theme_MyApplication_BLACK_NoActionBar;
}
} else {
Log.w("ThemeStyle", "其他主题模式 $actName");
}
if(themeId!=0){
context?.setTheme(themeId)
// return themeId;
}
AppCompatDelegate.setDefaultNightMode(
when (themeMode) {
ThemeMode.MODE_ALWAYS_ON -> AppCompatDelegate.MODE_NIGHT_YES
ThemeMode.MODE_ALWAYS_OFF -> AppCompatDelegate.MODE_NIGHT_NO
ThemeMode.MODE_FOLLOW_SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
else -> {
AppCompatDelegate.MODE_NIGHT_NO
}
}
)
return themeId
}
如果不为0,
if (color != 0) {
// getTheme().applyStyle(color, true);
AppUtils.updateActionBarAndStatusColor(this);
}
另外补充一点,按钮这些一般我都没有修改在每一个activity xml设置的,遵循material design以及theme的写法,全局就可以进行操作的,在
主要的适配工作还是处理selector的颜色以及第三方框架引用了颜色的情况,另外处理深色兼容的适配。



