Search code examples
androidandroid-themeandroid-stylesandroid-attributes

Changing Android styles by using theme


I have an app with a few different Activities. The different activities have different styled buttons, texts, etc... I've set up all the components to have various styles based on their location/Activity. (Eg. style="@style/MainMenuActionBarTitle, or style="@style/MainMenuActionBarTagLine). These styles set the background (Drawable, MipMap, or Color), textColor, etc...

The app will be offering some theme packs which change the colors of these various components throughout the application, and I was hoping there was a way to have Styles with the same name, but different values based on the Theme of the app. This way I can just change the Theme whenever the Activity is loaded to whatever Theme the user has chosen.

There's some good info here on how to change the standard widget look & feel using Themes, but that changes the look and feel for the standard-un-styled widgets.

Is there a way to accomplish this using Themes, or is this the wrong direction altogether? Is there a better/easier route?

Edit: After doing more research and more fiddling, I've realized what I want to do isn't far off from how I can accomplish this. What I want to do is to actually change component Styles when I set the Theme of the Activity.


Solution

  • One solution I've discovered is to use attributes which the Theme can point to different Styles.

    attrs.xml

    <resources>
       <!-- Attributes referencing whatever style the theme needs to set up. -->
       <attr name="main_menu_button_style_play" format="reference" />
    </resources>
    

    themes.xml

    <resources>
       <!-- Base application theme. -->
       <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
           <!-- App specific attributes -->
           <item name="@attr/main_menu_button_style_play">@style/MainMenu.Button.Play</item>
       </style>
    
       <!-- Blue application theme. -->
       <style name="AppTheme.Blue" parent="AppTheme">
          <!-- App specific attributes -->
          <item name="@attr/main_menu_button_style_play">@style/MainMenu.Button.Play.Blue</item>
       </style>
    </resources>
    

    styles.xml

    <style name="MainMenu.Button.Play">
        <item name="android:background">#f76d3c</item>
        <item name="android:text">PLAY</item>
    </style>
    
    <style name="MainMenu.Button.Play.Blue">
        <item name="android:background">#2ca8c2</item>
    </style>
    

    activity.xml

    <Button android:id="@+id/main_play_button"
            style="?attr/main_menu_button_style_play"/>
    

    This works really well, and allows me to set the Theme in the Activity.onCreate() method.

    The only annoying problem I have with this solution is that Android Studio complains that the Button is missing the layout_width and layout_height even though they're defined in the Style. I guess it doesn't follow the attribute reference back through the chosen Theme.

    Another approach which is what I ended up using was to more heavily use the attributes. Creating attributes for all the properties values I want to change between themes. So, instead of main_menu_button_style_play which defines the style reference, I used main_menu_button_play_background. This approach is the same amount of work as simply specifying a style because themes can inherit, but the IDE understands it.