Adding a child to *all* theme styles in styles.xml to avoid duplicate code

330 views Asked by At

I want to remove lots of duplicate entries in styles.xml just because it's annoying to repeat the same code over and over. Let's say I have a main theme:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="color_1">#000000</item>
    <item name="color_2">#424242</item>
    <item name="myMinTextSize">14sp</item>
    <item name="myNormalTextSize">18sp</item>
    <item name="myMaxTextSize">22sp</item>
    <!-- lots of other stuff -->
</style>

These are the size attributes in attr.xml:

<attr name="myMinTextSize" format="dimension" />
<attr name="myNormalTextSize" format="dimension" />
<attr name="myMaxTextSize" format="dimension" />

Now I have some theme children that will change the colors like this:

<style name="AppTheme.1">
    <item name="color_1">#131313</item>
    <item name="color_2">#BBBBBB</item>
</style>

I also want to be able to change the text sizes but to do so I will have to make a child for each theme like this:

<style name="AppTheme.Small">
    <item name="myMinTextSize">12sp</item>
    <item name="myNormalTextSize">14sp</item>
    <item name="myMaxTextSize">16sp</item>
</style>

<style name="AppTheme.1.Small">
    <item name="myMinTextSize">12sp</item>
    <item name="myNormalTextSize">14sp</item>
    <item name="myMaxTextSize">16sp</item>
</style>

<!-- and all the other theme children with .Small suffix will have *the exact same thing* -->

I have lots of children for colors and lots of children's children for text sizes. What I want to be able to do (without having to do it programmatically) is to not repeat the same thing over and over in styles.xml file. Is there a way to do it? For example something in the lines of:

<style name="*.Small">
    <item name="myMinTextSize">12sp</item>
    <item name="myNormalTextSize">16sp</item>
    <item name="myMaxTextSize">20sp</item>
</style>

<style name="*.Large">
    <item name="myMinTextSize">16sp</item>
    <item name="myNormalTextSize">20sp</item>
    <item name="myMaxTextSize">24sp</item>
</style>

I choose the theme in the program based on the options the user picks like this and then apply them to activities (I'm sure the variable names are obvious enough to get my point across):

mThemeResId = when (mCurrentTheme) {
    THEME_0 -> when (mCurrentFontSize) {
        SMALL -> R.style.AppTheme_Small
        LARGE -> R.style.AppTheme_Large
        else -> R.style.AppTheme
    }
    THEME_1 -> when (mCurrentFontSize) {
        SMALL -> R.style.AppTheme_1_Small
        LARGE -> R.style.AppTheme_1_Large
        else -> R.style.AppTheme_1
    }
    // and lots of others
}
1

There are 1 answers

1
Gabriele Mariotti On BEST ANSWER

You can do something different.

Instead of defining different app themes you can define them as theme overlays dividing them in categories.

Something like:

<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="color_1">#000000</item>
    <item name="color_2">#424242</item>

    <item name="myMinTextSize">14sp</item>
    <item name="myNormalTextSize">18sp</item>
    <item name="myMaxTextSize">22sp</item>
</style>

<style name="ThemeOverlay.Color.Red" parent="">
    <item name="colorPrimary">@color/red600</item>
    <item name="color_1">@color/red600Light</item>
    <item name="color_2">@color/red600Dard</item>
</style>

<style name="ThemeOverlay.Color.Green" parent="">
    <item name="colorPrimary">@color/green500</item>
    <item name="color_1">@color/green500Light</item>
    <item name="color_2">@color/green500Dard</item>
</style>

<style name="ThemeOverlay.Text.Small" parent="">
    <item name="myMinTextSize">12sp</item>
    <item name="myNormalTextSize">16sp</item>
    <item name="myMaxTextSize">20sp</item>
</style>

<style name="ThemeOverlay.Text.Medieum" parent="">
    <item name="myMinTextSize">16sp</item>
    <item name="myNormalTextSize">20sp</item>
    <item name="myMaxTextSize">24sp</item>
</style>

Then your Activity that uses the AppTheme by default just apply all the theme overlays you need.

For example:

override fun onCreate(savedInstanceState: Bundle?) {
    setTheme(R.style.ThemeOverlay_Color_Red)
    setTheme(R.style.ThemeOverlay_Text_Small)
    super.onCreate(savedInstanceState)
    //..
}