Default Style Resource pre API Level 21
B

1

0

I am looking to create a custom ViewGroup to be used in a library; which contains a few ImageButton objects. I would like to be able to apply a style each ImageButton; but I cannot figure out how to apply a style programmatically other than by applying a attribute resource to the defStyleAttr parameter; like so:

mImageButton = new ImageButton(
        getContext(),                    // context
        null,                            // attrs
        R.attr.customImageButtonStyle);  // defStyleAttr

The issue with this is that the only way to change the style of each ImageButton would be by applying a style to this attribute in a parent theme. But I would like to be able to set a default style, without having to manually set this attribute for each project that uses this library.

There is a parameter that does exactly what I am looking for; defStyleRes, which can be used like so:

mImageButton = new ImageButton(
        getContext(),                    // context
        null,                            // attrs
        R.attr.customImageButtonStyle,   // defStyleAttr
        R.style.customImageButtonStyle); // defStyleRes

This parameter is only available at API Level 21 and above, but my projects target API Level 16 and above. So how can I set the defStyleRes, or apply a default style, without access to this parameter?


I applied my style using a ContextThemeWrapper, as suggested by @EugenPechanec, which seems to work well, but each ImageButton now has the default ImageButton background, even though my style applies <item name="android:background">@null</item>.

Here is the style I am using:

<style name="Widget.Custom.Icon" parent="android:Widget">
    <item name="android:background">@null</item>
    <item name="android:minWidth">56dp</item>
    <item name="android:minHeight">48dp</item>
    <item name="android:tint">@color/selector_light</item>
</style>

And this is how I am applying it:

ContextThemeWrapper wrapper = new ContextThemeWrapper(getContext(), R.style.Widget_Custom_Icon);
mImageButton = new AppCompatImageButton(wrapper);

On the left is what I am getting, and on the right is what I would like it to look like:

enter image description hereenter image description here

Bosson answered 9/3, 2017 at 21:39 Comment(0)
T
1

defStyleAttr is for resolving default widget style from theme attribute.

Example: AppCompatCheckBox asks for R.attr.checkBoxStyle. Your theme defines <item name="checkBoxStyle">@style/Widget.AppCompat.CheckBox</item>.

If that attribute is not defined in your theme the widget would pickup its defStyleRes e.g. R.style.Widget_AppCompat_CheckBox.

Note that these are not actual values used by the widget.

I have not seen defStyleRes constructor parameter used outside of the framework. All of these parameters (plus defaults) are however used when asking TypedArray for resources.

How to actually solve your problem

So the four parameter constructor is not available on all platforms. You need to find a way to feed in your default style. Consider a style you'd like to apply:

<style name="MyImageButtonStyle" parent=""> ... </style>

You need a way to convert it to a defStyleAttr parameter. Define the default style on a theme overlay:

<style name="MyImageButtonThemeOverlay" parent="">
    <!-- AppCompat widgets don't use the android: prefix. -->
    <item name="imageButtonStyle">@style/MyImageButtonStyle</item>
</style>

Now you can create your ImageButton using this theme overlay:

// When creating manually you have to include the AppCompat prefix.
mImageButton = new AppCompatImageButton(
    new ContextThemeWrapper(getContext(), R.style.MyImageButtonThemeOverlay)
);

You don't need to specify any other parameters as AppCompatImageButton will pickup R.attr.imageButtonStyle by default.


If that looks hacky you can always inflate your custom view hierarchy or individual widgets from XML where you specified the style="@style/MyImageButtonStyle" attribute.

Tighten answered 9/3, 2017 at 22:2 Comment(8)
ContextThemeWrapper seems like the correct way to go. I have come across it before, but it completely escaped my mind. Unfortunately it is causing another issue; for some reason it is adding a background resource on each ImageButton. I can remove the background after the fact by setting mImageButton.setBackgroundResource(0) but I cannot do this in my style resource with <item name="android:background">@null</item>, even though I can change other attributes. Any idea of what could be causing this?Bosson
@Bosson ok, post what changed at the end of your question, I'll look into it.Tighten
@Bosson You're mixing up styles and themes (theme overlays). Don't skip the theme overlay. Don't use style resource IDs with ContextThemeWrapper. Don't define style attributes in themes and vice versa. This may help you chris.banes.me/2014/11/12/theme-vs-styleTighten
Ah, I see the issue, thank you. Though, I wan't to be able to apply styles dynamically, so if the ContextThemeWrapper applies a theme to the ImageButton; is there also a way to apply a style to an ImageButton upon creation, other than doing so in a theme overlay?Bosson
@Bosson in that case this approach (a set of corresponding theme overlays and styles) may not be the best option. You may want to style the widget after constructor in Java.Tighten
Let us continue this discussion in chat.Bosson
It seemed a little strange that I couldn't apply the style through a ContextThemeWrapper, so I did a little more searching. I found (based on this comment) that you have to use the three-argument constructor, or the defAttrAttr will be applied after the style defined in the ContextThemeWrapper. Now it works as expected, without the default ImageButton background.Bosson
The one argument constructor invokes the two arg ctor, the two arg ctor invokes the three arg ctor which reads (in case of AppCompatImageButton) R.attr.imageButtonStyle from current theme. Which in the theme overlay points to the style you wanted to use. Apparently you managed to get it working using a different setup. Maybe you'd like to post an answer yourself.Tighten

© 2022 - 2024 — McMap. All rights reserved.