getTheme().applyStyle(…) multiple times without ov

2019-08-07 04:32发布

问题:

When I apply extra attributes to the AppTheme multiple times it overwrites the previous one. This is the code I am using:

MainActivity:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {

    getTheme().applyStyle(R.style.AppTheme_OverlayPrimaryRed);

    // If you change `false` to `true`, the overlay above is overwritten.
    if (false) {
        getTheme().applyStyle(R.style.AppTheme_OverlayAccentRed);
    }

    super.onCreate(savedInstanceState);

    ...
}

AppTheme.OverlayPrimaryRed:

<style name="AppTheme.OverlayPrimaryRed">
    <item name="colorPrimary">@color/material_red_500</item>
    <item name="colorPrimaryDark">@color/material_red_700</item>
</style>

AppTheme.OverlayAccentRed:

<style name="AppTheme.OverlayAccentRed">
    <item name="colorAccent">@color/material_red_A200</item>
</style>

Any ideas how I can solve this issue?

回答1:

Your style definitions AppTheme.OverlayPrimaryRed and AppTheme.OverlayAccentRed implicitly inherit from AppTheme. Since AppTheme probably also contains a definition for colorPrimary and colorPrimaryDark the second applyStyle statement will set these attributes as well, undoing the first applyStyle call.

For this reason I did not use any dots in the style overlay names in my answer to this question.

If you want to keep the dots for esthetical reasons you can define an empty parent style for overlays like so:

<style name="Overlay">
</style>

<style name="Overlay.PrimaryRed">
    <item name="colorPrimary">@color/material_red_500</item>
    <item name="colorPrimaryDark">@color/material_red_700</item>
</style>

<style name="Overlay.AccentRed">
    <item name="colorAccent">@color/material_red_A200</item>
</style>


回答2:

Edit 2: This is a failed attempt, applying styles again removes previous styles set through programming.

Every style in Android can have parent styles. So, the child style will inherit styles of its parent and apply its own styles too. Also, a child can override its parent's styles or properties.

<!-- This is a parent style -->
<style name="AppTheme.OverlayPrimaryRed">
    <item name="colorPrimary">@color/material_red_500</item>
    <item name="colorPrimaryDark">@color/material_red_700</item>
</style>

<!-- This is a child of above style -->
<style name="AppTheme.OverlayAccentRed" parent="AppTheme.OverlayPrimaryRed">
    <item name="colorAccent">@color/material_red_A200</item>
</style>

Read Defining Styles at Android Developer's Resource. Also, if you don't want to use parent attribute :

If you want to inherit from styles that you've defined yourself, you do not have to use the parent attribute. Instead, just prefix the name of the style you want to inherit to the name of your new style, separated by a period. For example, to create a new style that inherits the MyTextStyle style defined, but make the color red, you can author the new style like this:

<style name="MyTextStyle">
    <item name="android:textAllCaps">false</item>
    <item name="android:textColor">#FFFFFF</item>  <!-- white text (default) -->
    <item name="android:textStyle">bold</item>
    <item name="android:textSize">12dp</item>
</style>

<!-- red text -->
<style name="MyTextStyle.RED">
    <item name="android:textColor">#FF0000</item>
</style>

<!-- green text -->
<style name="MyTextStyle.GREEN">
    <item name="android:textColor">#00FF00</item>
</style>

<!-- blue text -->
<style name="MyTextStyle.BLUE">
    <item name="android:textColor">#0000FF</item>
</style>

Notice that there is no parent attribute in the tag, but because the name attribute begins with the MyTextStyle style name (which is a style that you have created), this style inherits all style properties from that style. This style can override the android:textColor property to make the text red. You can reference this new style as @style/MyTextStyle.RED.

You can continue inheriting like this as many times as you'd like, by chaining names with periods. For example, you can extend MyTextStyle.RED to be bigger, with:

<style name="MyTextStyle.RED.Big">
    <item name="android:textSize">30sp</item>
</style>

Edit 1:

<!-- this is your root style -->
<style name="AppTheme.Overlay">
    <!-- default styles for primary, primaryDark (you can add accent too) -->
</style>

<!-- 1. add Custom Primary Color to root style -->
<style name="AppTheme.Overlay.PrimaryRed">
    <item name="colorPrimary">@color/material_red_500</item>
    <item name="colorPrimaryDark">@color/material_red_700</item>
</style>

<!-- 1. add Custom Accent Color to root style -->    
<style name="AppTheme.Overlay.AccentRed">
    <item name="colorAccent">@color/material_red_A200</item>
</style>

<!-- 2. add Custom Primary Color to root style -->
<style name="AppTheme.Overlay.PrimaryBlue">
    <item name="colorPrimary">@color/material_blue_500</item>
    <item name="colorPrimaryDark">@color/material_blue_700</item>
</style>

<!-- 2. add Custom Accent Color to root style -->    
<style name="AppTheme.Overlay.AccentBlue">
    <item name="colorAccent">@color/material_blue_A200</item>
</style>

<!-- add 10 for each...... -->

Make 10 styles for your Primary Color, and 10 styles for your Accent Color.

Then, in your code:

// if root style has some styles to add (default)
getTheme().applyStyle(R.style.AppTheme_Overlay);

// first color selection
getTheme().applyStyle(R.style.AppTheme_Overlay_PrimaryRed);
getTheme().applyStyle(R.style.AppTheme_Overlay_AccentRed);

// when you want blue color
getTheme().applyStyle(R.style.AppTheme_Overlay_PrimaryBlue);
getTheme().applyStyle(R.style.AppTheme_Overlay_AccentBlue);

// when you want bluePrimary, but redAccent color (bad choice)
getTheme().applyStyle(R.style.AppTheme_Overlay_PrimaryBlue);
getTheme().applyStyle(R.style.AppTheme_Overlay_AccentRed);

Here, AppTheme_Overlay_PrimaryBlue will override AppTheme_Overlay_PrimaryRed. And, so on.

You just added one root style, ten primary color styles and ten accent color styles = 21 styles.