2

I have an Android app that supports the api 14 and targets api 21. I have some styles that need to be overided by api 21. What happens is that I need to duplicate all style items just to change one item.

For example:

<!-- values/styles.xml -->

<style name="Widget.HelloWorld.Button">
    <item name="android:textSize">24sp</item>
    <item name="android:drawableLeft">@android:drawable/ic_menu_delete</item>
</style>

If I want to override just the attribute android:textSize in the api 21 I need to duplicate the item android:drawableLeft too.

So to avoid this I came to a solution. In this example I have three buttons (foo, bar and fancy) with some styles (Widget.HelloWorld.Button.Foo, Widget.HelloWorld.Button.Bar, Widget.HelloWorld.Button.Fancy )

<!-- values/styles.xml -->

<resources>
    <style name="v0.Widget.HelloWorld.Button" parent="Widget.AppCompat.Button">
        <item name="android:textSize">24sp</item>
        <item name="android:drawableLeft">@android:drawable/ic_menu_delete</item>
    </style>

    <style name="v0.Widget.HelloWorld.Button.Foo" parent="Widget.HelloWorld.Button">
        <item name="android:textColor">#f00</item>
    </style>

    <style name="v0.Widget.HelloWorld.Button.Bar" parent="Widget.HelloWorld.Button">
        <item name="android:textColor">#ff0</item>
    </style>

    <style name="v0.Widget.HelloWorld.Button.Fancy" parent="Widget.HelloWorld.Button">
        <item name="android:textColor">#f0f</item>
    </style>

    <!-- Alias -->
    <style name="Widget.HelloWorld.Button" parent="v0.Widget.HelloWorld.Button" />
    <style name="Widget.HelloWorld.Button.Foo" parent="v0.Widget.HelloWorld.Button.Foo" />
    <style name="Widget.HelloWorld.Button.Bar" parent="v0.Widget.HelloWorld.Button.Bar" />
    <style name="Widget.HelloWorld.Button.Fancy" parent="v0.Widget.HelloWorld.Button.Fancy" />
</resources>

I have prefixed all styles names with the version number of the api that will have impact. Since the values/styles.xml is the default, i prefixed with v0.

Now in the values-v21/styles.xml i can override just the item that i want without duplicating code.

<!-- values-v21/styles.xml -->

<resources>
    <style name="v21.Widget.HelloWorld.Button" parent="v0.Widget.HelloWorld.Button">
        <item name="android:textSize">36sp</item>
    </style>

    <style name="v21.Widget.HelloWorld.Button.Fancy" parent="v0.Widget.HelloWorld.Button.Fancy">
        <item name="android:colorPrimary">@color/primary</item>
    </style>

    <!-- Alias -->
    <style name="Widget.HelloWorld.Button" parent="v21.Widget.HelloWorld.Button" />
    <style name="Widget.HelloWorld.Button.Fancy" parent="v21.Widget.HelloWorld.Button.Fancy" />
</resources>

I want to know what is the cons of this approach.

André Abreu
  • 737
  • 7
  • 13

2 Answers2

2

You can fix the original duplication problem using dimension resources that switch on the version qualifier, then referencing it in the style:

<!-- values/dimens.xml -->
<resource>
    <dimen name="hello_text_size">24sp</dimen>
</resource>

<!-- values-v21/dimens.xml -->
<resource>
    <dimen name="hello_text_size">36sp</dimen>
</resource>

<!-- values/styles.xml -->
<style name="Widget.HelloWorld.Button">
    <item name="android:textSize">@dimen/hello_text_size</item>
    <item name="android:drawableLeft">@android:drawable/ic_menu_delete</item>
</style>
Dan Lew
  • 85,990
  • 32
  • 182
  • 176
  • 1
    But if I want to add `@color/primary` in `values-v21/styles.xml` how can I avoid code duplication? Can I go with my approach without problems? – André Abreu Aug 05 '15 at 14:50
2

As @DanielLew answered here, overriding only the dimen/color/drawable value in the xml value files is best way to change some style item for different Android versions or screen sizes.

The style becomes more complex and hard for support when you have to override it for new Android version to use new View property (android:layout_marginStart for v17). In this case I would suggest to extract base style and use it as parent to the styles in the default values and new version values (v17 in example) folder.

<!-- values/dimens.xml -->
<resource>
    <dimen name="button_text_size">24sp</dimen>
    <dimen name="margin_size">10dp</dimen>
</resource>

<!-- values/styles.xml -->
<resources>
    <!-- Parent style - add all common properties -->
    <style name="Widget.HelloWorld.Button" parent="Widget.AppCompat.Button">
        <item name="android:textSize">@dimen/button_text_size</item>
        <item name="android:drawableLeft">@android:drawable/ic_menu_delete</item>
    </style>

    <!-- Child style - add version specific properties (minimum version) -->
    <style name="Widget.HelloWorld.Button.Foo">
        <item name="android:layout_marginLeft">@dimen/margin_size</item>
    </style>
<resources>

<!-- values-v17/styles.xml - minimum version for the new property -->
<resources>
    <!-- Child style - add version specific properties (v17) -->
    <style name="Widget.HelloWorld.Button.Foo">
        <item name="android:layout_marginStart">@dimen/margin_size</item>
    </style>
<resources>

Please read Styling Views on Android. You set the parent twice (explicitly and implicitly) which adds additional confusion. Read Implicit vs. Explicit Parenting section from the link.

Community
  • 1
  • 1
Mario Kutlev
  • 4,897
  • 7
  • 44
  • 62
  • So yah that dots are quite confusing really just simple names as WidgetHelloWorldButton are anough so its obvious – Renetik Jul 16 '22 at 06:35