如果在网上搜换肤,方案五花八门,但是根据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的颜色以及第三方框架引用了颜色的情况,另外处理深色兼容的适配。