I am trying to programmatically create some Buttons to add them to an existing ViewGroup. This works fine, but I am not able to init the buttons with the right style.
I am aware, that it is not possible to set/change the style of view once it has been created. But all solutions I found so far say, that it should be no problem to create a view with a custom style (from a API level 11 up I think and I am using 14+):
Button button = new Button (getActivity(), null, R.style.MyButtonStyle);
The only effect is, that the button is created without any style. No background, so selector, no margin/padding just the plain text. I thought MyButtonStyle would be broken, but creating the button in XML using MyButtonStyle it no problem.
Why is this not working?
I finally found the solution! The question was, why the following is not working:
Button button = new Button (getActivity(), null, R.style.MyButtonStyle);
Turned out, that the problem is, that R.style.xxx is being used instead of R.attr.xxx. It seems that this is a bug in the implementation of View(Context context, AttributeSet attrs, int defStyleAttr) or at least in the documentation of this contstructor: The third paramater MUST be R.attr for the constructor to work and to apply the style correctly.
So in order to get the constructor working one has to add some more code to /res/values/attr.xml and /res/values/styles.xml:
<!-- /res/values/attr.xml -->
<declare-styleable name="AppTheme">
<attr name="specialButtonStyle" format="reference"/>
</declare-styleable>
<!-- /res/values/styles.xml -->
<style name="AppTheme" parent="AppBaseTheme">
...
<item name="specialButtonStyle">@style/MyButtonStyle</item>
</style>
<style name="MyButtonStyle" parent="@android:style/Widget.Button">
...
</style>
// In Java we can now use R.attr.specialButtonStyle instead of R.style.MyButtonStyle
// getActivity() is used because I am working in a Fragment. Within an
// Activity this can be used instead...
Button button = new Button(getActivity(), null, R.attr.specialButtonStyle);
It is quite annoying, that the documentation has not been updated about this problem. The first bug report about this issue was filed back in 2011...
Also one has to write some extra code to get this running this solution is much faster and (from my point of view) much better than inflating a XML layout as usually proposed.
you probably can achieve that by using a ContextThemeWrapper
ContextThemeWrapper ctw = new ContextThemeWrapper(this, R.style.MyButtonStyle);
Button = new Button(ctw);
edit:
I just checked the source code and the reason your code (and I guess mine code) doesn't work is clear now.
public View(Context context, AttributeSet attrs, int defStyleAttr) {
this(context);
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,
defStyleAttr, 0);
with the attrs being null, the TypedArray doesn't have any values to use.
My code I used before for Dialogs and I thought it would work the same.
You can check it on the View source code.
so probably an update answer that should work is if you pass the AttributeSet on the constructor. I'm not sure how to do that, but looking at the docs (HERE) you probably can do something like that:
// the docs says `myResource` so probably the style ID
XmlPullParser parser = resources.getXml(R.style.MyButtonStyle);
AttributeSet attributes = Xml.asAttributeSet(parser);
Button button = new Button (getActivity(), attributes, R.style.MyButtonStyle);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With