Nicolas Goy
Nicolas Goy

Reputation: 1430

Set MaterialButton style to TextButton programmatically

I'd like to create a MaterialButton programmatically and use the TextButton style on it.

I tried the following but the resulting button has a background color and is not a TextButton.

val newContext = ContextThemeWrapper(this, R.style.Widget_MaterialComponents_Button_TextButton)
val errorBtn = MaterialButton(newContext, null, R.style.Widget_MaterialComponents_Button_TextButton)

I tried many variants, (without using the 3 parameters constructor or without context wrapper).

I found this question: Apply Style to MaterialButton programmatically

But the answer is not satisfying as the setting background tint do not produce good results (there is a shadow being the button).

Upvotes: 7

Views: 2723

Answers (1)

Nicolas Goy
Nicolas Goy

Reputation: 1430

After reading the material design source, I discovered the following. The third parameter to MaterialButton is not a style resource but a style attribute. The parameter is called defStyleAttr and not defStyleRes, only the second one is equivalent to passing style in the XML file. And MaterialButton do not implement the 4 parameters constructor which takes this.

To clarify, a style resource is a "standalone" style definition, a style attribute is the name of the attribute in the context style. It is a bit weird that you are supposed to style a whole widget with one attribute, but this is how it works.

This means, we have to create and attribute that references the whole widget style and pass it when creating our button.

To do this, we have to:

  1. Create an attr resource in the app resources, by adding <attr name="myAttr" format="reference"/> to styles.xml.
  2. Still in styles.xml, modify the app theme to use the attribute, like this:
<style name="AppTheme" parent="Theme.MaterialComponents.NoActionBar">
    <item name="myAttr">@style/Widget.MaterialComponents.Button.TextButton</item>
</style>
  1. Use the attribute when creating the button, like so:
val errorBtn = MaterialButton(this, null, R.attr.myAttr)

This will work only if this (current context, usually an activity) has AppTheme applied to it (which should generally be the case). If this is not the case, or if you want to change the theme, it can be applied with ContextThemeWrapper, like so:

val ctx = ContextThemeWrapper(this, R.style.AppTheme)
val errorBtn = MaterialButton(ctx, null, R.attr.myAttr)

Upvotes: 7

Related Questions