【官方推荐方式】【原创】安卓换肤踩坑纯色模式app使用attr轻松实现定制颜色主题和深色主题...

如果在网上搜换肤,方案五花八门,但是根据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

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/defaultThemeColorSecond"/>
</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定义

<attr name="defaultThemeColor" format="reference|color"></attr>
    <attr name="defaultThemeColorSecond" format="reference|color"></attr>

其实现在每一个主题都实现了,

下面附上theme.xml 仔细看 都实现了defaultThemeColor defaultThemeColorSecond 属性,

<style name="Theme.MyApplication.Blue.NoActionBar" parent="Theme.MyApplication.Blue">
        <item name="windowActionBar">false</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:actionMenuTextColor">@color/white_no_night</item>
        <item name="windowNoTitle">true</item>
    </style>


    <style name="Theme.MyApplication.Yellow" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <item name="defaultThemeColor">@color/themeColorYellow</item>
        <item name="defaultThemeColorSecond">@color/purple_200</item>
        <item name="android:buttonStyle">@style/Widget.AppCompat.Button.Small</item>
        <item name="colorButtonNormal">@color/themeColorYellow</item>
        <!--    <style name="Theme.MyApplication" parent="Theme.MaterialComponents.Light.DarkActionBar">-->
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/themeColorYellow</item>
        <item name="colorPrimaryVariant">@color/colorPrimaryVariantYellow</item>
        <item name="colorOnPrimary">@color/colorOnPrimaryYellow</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/colorSecondary</item>
        <!--        <item name="colorSecondary">@color/teal_200</item>-->
        <item name="colorSecondaryVariant">@color/colorSecondaryVariant</item>
        <item name="colorOnSecondary">@color/colorOnSecondary</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor">@color/colorPrimaryVariantYellow</item>
        <item name="controlBackground">@color/themeColorYellow</item>
        <item name="statusBarBackground">@color/themeColorYellow</item>
    </style>


    <style name="Theme.MyApplication.Yellow.NoActionBar" parent="Theme.MyApplication.Yellow">
        <item name="windowActionBar">false</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:actionMenuTextColor">@color/white_no_night</item>
        <item name="windowNoTitle">true</item>
    </style>


    <style name="Theme.MyApplication.GREEN" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <item name="defaultThemeColor">@color/themeColorGreen</item>
        <item name="defaultThemeColorSecond">@color/purple_200</item>
        <item name="android:buttonStyle">@style/Widget.AppCompat.Button.Small</item>
        <item name="colorButtonNormal">@color/themeColorGreen</item>
        <!--    <style name="Theme.MyApplication" parent="Theme.MaterialComponents.Light.DarkActionBar">-->
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/themeColorGreen</item>
        <item name="colorPrimaryVariant">@color/colorPrimaryVariantGreen</item>
        <item name="colorOnPrimary">@color/colorOnPrimaryGreen</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/colorSecondary</item>
        <!--        <item name="colorSecondary">@color/teal_200</item>-->
        <item name="colorSecondaryVariant">@color/colorSecondaryVariant</item>
        <item name="colorOnSecondary">@color/colorOnSecondary</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="lollipop">
            @color/colorPrimaryVariantGreen
        </item>
        <item name="statusBarBackground">@color/themeColorGreen
        </item>

    </style>

    <style name="Theme.MyApplication.GREEN.NoActionBar" parent="Theme.MyApplication.GREEN">
        <item name="windowActionBar">false</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:actionMenuTextColor">@color/white_no_night</item>
        <item name="windowNoTitle">true</item>
    </style>






    <style name="Theme.MyApplication.PURPLE" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <item name="defaultThemeColor">@color/themeColorRed</item>
        <item name="defaultThemeColorSecond">@color/purple_200</item>
        <item name="android:buttonStyle">@style/Widget.AppCompat.Button.Small</item>
        <item name="colorButtonNormal">@color/themeColorRed</item>
        <!--    <style name="Theme.MyApplication" parent="Theme.MaterialComponents.Light.DarkActionBar">-->
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/themeColorRed</item>
        <item name="colorPrimaryVariant">@color/colorPrimaryVariantRed</item>
        <item name="colorOnPrimary">@color/colorOnPrimaryRed</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/colorSecondary</item>
        <!--        <item name="colorSecondary">@color/teal_200</item>-->
        <item name="colorSecondaryVariant">@color/colorSecondaryVariant</item>
        <item name="colorOnSecondary">@color/colorOnSecondary</item>
        <!-- Status bar color. -->
        <item name="statusBarBackground">@color/themeColorRed</item>
        <item name="android:statusBarColor">@color/colorPrimaryVariantRed</item>

    </style>


    <style name="Theme.MyApplication.PURPLE.NoActionBar" parent="Theme.MyApplication.PURPLE">
        <item name="windowActionBar">false</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:actionMenuTextColor">@color/white_no_night</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="Theme.MyApplication.DefaultBlue" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!--    <style name="Theme.MyApplication" parent="Theme.MaterialComponents.Light.DarkActionBar">-->
        <!-- Primary brand color. -->

        <item name="defaultThemeColor">@color/themeColor</item>
        <item name="defaultThemeColorSecond">@color/purple_200</item>
        <!--        style设置没效果代码批量设置-->
        <item name="colorButtonNormal">?attr/defaultThemeColorSecond</item>
        <item name="android:elevation">0dp</item>
        <!--        hide shadow-->
        <item name="android:windowContentOverlay">@null</item>
        <!-- Primary brand color. -->
        <item name="colorPrimary">?attr/defaultThemeColor</item>
        <item name="colorPrimaryVariant">?attr/defaultThemeColor</item>
        <item name="colorOnPrimary">@color/colorOnPrimary</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/colorSecondary</item>
        <item name="colorSecondaryVariant">@color/colorSecondaryVariant</item>
        <item name="colorOnSecondary">@color/colorOnSecondary</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor">?attr/defaultThemeColor</item>
        <item name="statusBarBackground">@color/themeColorBlue</item>
        <!-- Customize your theme here. -->
    </style>
    <style name="Theme.MyApplication.BLACK" parent="Theme.MaterialComponents.Light.DarkActionBar">
        <item name="defaultThemeColor">@color/themeColorBlack</item>
        <item name="defaultThemeColorSecond">@color/purple_200</item>
        <item name="android:buttonStyle">@style/Widget.AppCompat.Button.Small</item>
        <item name="colorButtonNormal">@color/themeColorBlack</item>
        <!--    <style name="Theme.MyApplication" parent="Theme.MaterialComponents.Light.DarkActionBar">-->
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/themeColorSecondBlack</item>
        <item name="colorPrimaryVariant">@color/colorPrimaryVariantBlack</item>
        <item name="colorOnPrimary">@color/colorOnPrimaryBlack</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/colorSecondary</item>
        <!--        <item name="colorSecondary">@color/teal_200</item>-->
        <item name="colorSecondaryVariant">@color/themeColorSecondBlack</item>
        <item name="colorOnSecondary">@color/colorOnSecondary</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="lollipop"> @color/colorPrimaryVariantBlack</item>
        <item name="statusBarBackground">@color/themeColorBlack</item>

    </style>

    <style name="Theme.MyApplication.BLACK.NoActionBar" parent="Theme.MyApplication.BLACK">
        <item name="windowActionBar">false</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:actionMenuTextColor">@color/white_no_night</item>
        <item name="windowNoTitle">true</item>
    </style>














    <style name="Theme.MyApplication.DefaultBlue.NoActionBar" parent="Theme.MyApplication.DefaultBlue">
        <item name="windowActionBar">false</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:actionMenuTextColor">@color/white_no_night</item>
        <item name="windowNoTitle">true</item>
    </style>

color实现

<color name="themeColor" >@color/light_blue_A200</color>
    <color name="themeColorSecond" >@color/purple_200</color>
    <color name="colorPrimary">@color/purple_200</color>
    <color name="colorPrimaryVariant">@color/black_overlay</color>
    <color name="colorOnPrimary">@color/black</color>
    <color name="colorSecondary">@color/teal_200</color>
    <color name="colorSecondaryVariant">@color/teal_200</color>
    <color name="colorOnSecondary">@color/black</color>







    <color name="themeColorRed" >@color/light_blue_A200</color>
    <color name="themeColorSecondRed" >@color/purple_200</color>
    <color name="colorPrimaryRed">@color/purple_200</color>
    <color name="colorPrimaryVariantRed">@color/black_overlay</color>
    <color name="colorOnPrimaryRed">@color/black</color>





    <color name="themeColorYellow" >@color/light_blue_A200</color>
    <color name="themeColorSecondYellow" >@color/purple_200</color>
    <color name="colorPrimaryYellow">@color/purple_200</color>
    <color name="colorPrimaryVariantYellow">@color/black_overlay</color>
    <color name="colorOnPrimaryYellow">@color/black</color>



    <color name="themeColorGreen" >@color/light_blue_A200</color>
    <color name="themeColorSecondGreen" >@color/purple_200</color>
    <color name="colorPrimaryGreen">@color/purple_200</color>
    <color name="colorPrimaryVariantGreen">@color/black_overlay</color>
    <color name="colorOnPrimaryGreen">@color/black</color>




    <color name="themeColorBlue" >@color/light_blue_A200</color>
    <color name="themeColorSecondBlue" >@color/purple_200</color>
    <color name="colorPrimaryBlue">@color/purple_200</color>
    <color name="colorPrimaryVariantBlue">@color/black_overlay</color>
    <color name="colorOnPrimaryBlue">@color/black</color>


    <color name="themeColorBlack" >@color/light_blue_A200</color>
    <color name="themeColorSecondBlack" >@color/purple_200</color>
    <color name="colorPrimaryBlack">@color/purple_200</color>
    <color name="colorPrimaryVariantBlack">@color/black_overlay</color>
    <color name="colorOnPrimaryBlack">@color/black</color>

深色主题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的写法,全局就可以进行操作的,在<item name="colorPrimary">就完成了适配,
主要的适配工作还是处理selector的颜色以及第三方框架引用了颜色的情况,另外处理深色兼容的适配。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值