Apply Style to MaterialButton programmatically
A

5

18

I'm trying to create a custom view extending from MaterialButton and apply style in code so I don't need to do it in xml.

class CustomRedButton @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyleAttr: Int = 0
) : MaterialButton(ContextThemeWrapper(context, R.style.ButtonRedStyle), attrs, defStyleAttr) 

Style is:

<style name="ButtonRedStyle" 
    parent="Widget.MaterialComponents.Button.TextButton">
    <item name="backgroundTint">@color/red</item>
    <item name="rippleColor">@color/grey</item>
    <item name="strokeWidth">1dp</item>
    <item name="strokeColor">@color/black</item>
</style>

Everything works fine but backgroundTint property. For some reason background color is not changing, and it has Theme's primary color. However, if I try to apply the style to a MaterialButton in xml it does change the color.

Any idea why that can be happening or how I can achieve it?

Amoebocyte answered 19/9, 2018 at 8:42 Comment(2)
Which AppTheme you're using? if it's AppCompat theme, try changing it to MaterialComponents once.Taliataliaferro
I'm using Theme.MaterialComponents.Light.NoActionBarAmoebocyte
K
10

I'm also facing the same issue. The only workaround I've found so far is to set the tint programmatically like:

button.setBackgroundTintList(ColorStateList.valueOf(Color.RED));
Kilgore answered 12/10, 2018 at 14:54 Comment(2)
Yes, I ended up doing that too. Not the nicest way, but it does work.Amoebocyte
This is not the right answer for the 'Apply Style to MaterialButton programmatically' question, setting a backtuondTintList it's a hack. This should be the right answer https://mcmap.net/q/657991/-apply-style-to-materialbutton-programmaticallyBadoglio
L
18

Using

MaterialButton(ContextThemeWrapper(context, R.style.ButtonRedStyle), attrs, defStyleAttr)

you are applying a themeoverlay to default style, you are not applying a different style.

It means:

<style name="ButtonRedTheme" parent="...">
    <item name="colorPrimary">@color/...</item>
    <item name="colorOnPrimary">@color/...</item>
    <item name="colorSecondary">@color/...</item>
</style>

If you want to apply a different style you have to:

  • Define a custom attribute in attrs.xml
    <attr name="myButtonStyle" format="reference"/>
  • Assing a style to this attribute in your app theme:
   <style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
        <item name="myButtonStyle">@style/CustomButtonStyle</item>
   </style>
  • Define the custom style:
    <style name="CustomButtonStyle" parent="Widget.MaterialComponents.Button.*">
        <item name="backgroundTint">@color/...</item>
        <item name="rippleColor">@color/grey</item>
        <item name="strokeWidth">1dp</item>
        <item name="strokeColor">@color/black</item>
    </style>

Finally use:

val customButton = MaterialButton(context, null, R.attr.myButtonStyle)
Lownecked answered 2/8, 2020 at 21:29 Comment(0)
K
10

I'm also facing the same issue. The only workaround I've found so far is to set the tint programmatically like:

button.setBackgroundTintList(ColorStateList.valueOf(Color.RED));
Kilgore answered 12/10, 2018 at 14:54 Comment(2)
Yes, I ended up doing that too. Not the nicest way, but it does work.Amoebocyte
This is not the right answer for the 'Apply Style to MaterialButton programmatically' question, setting a backtuondTintList it's a hack. This should be the right answer https://mcmap.net/q/657991/-apply-style-to-materialbutton-programmaticallyBadoglio
C
3

For a TextButton there shouldn't be a background (just the text has a color). For a colored button, you should use the default Filled Button style which is Widget.MaterialComponents.Button.

And when applied as a theme, the button uses different attributes. It's described in section Themed Attribute Mapping here: https://material.io/develop/android/components/material-button/

Filled button
+------------------------+-----------------------------------------+
| Component Attribute    | Default Theme Attribute Value           |
+------------------------+-----------------------------------------+
| android:textAppearance | textAppearanceButton                    |
| android:textColor      | colorOnPrimary                          |
| iconTint               | colorOnPrimary                          |
| rippleColor            | colorOnPrimary at 32% opacity (pressed) |
| iconTint               | colorOnPrimary                          |
| backgroundTint         | colorPrimary                            |
| ...                    | ...                                     |
+------------------------+-----------------------------------------+

In your case, the theme should look something like:

<style name="ButtonRedTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <item name="colorPrimary">@color/red</item>
    <item name="colorOnPrimary">@color/white</item>
    <item name="colorOnSurface">@color/black</item>
</style>

You can also change all buttons to a specific style with

<item name="materialButtonStyle">@style/ButtonRedTheme</item>

in your app theme.

Crannog answered 9/10, 2019 at 9:8 Comment(0)
T
1

If you want to change your style for CustomView, you've to pass it to constructor by passing it into third param defStyleAttr like this:

class CustomRedButton @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyleAttr: Int = R.style.ButtonRedStyle // Just default style like this
) : MaterialButton(context, attrs, defStyleAttr)

and you can initialize it like this programmatically,

CustomRedButton(this, null, R.style.ButtonRedStyle) // Initialization, ('this' is context)

For more details refer here

Taliataliaferro answered 19/9, 2018 at 9:11 Comment(3)
That, for some reason, is not working for MaterialButtons. None of the parameters in the style is used. To make it work I needed to use the ContextThemeWrapper.Amoebocyte
The 3rd attribute is a style attribute defined in the app theme, is not a style.Lownecked
This does not work for MaterialButton. Using ContextThemeWrapper does at least partially work.Popery
B
0

I had the same issue for a simple use case, i need to update the button backgroundTint and isEnabled state what i did: i created a custom class that extends from MaterialButton

class MyCustomMaterialButton @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : MaterialButton(context, attrs)

then i added an extension to this class to update button styling attributes:

fun MyCustomMaterialButton.updateEnabledState(enabled: Boolean){
    apply {
        if(enabled){
            isEnabled = true
            setBackgroundColor(ContextCompat.getColor(context, R.color.primary))
        }
        else{
            isEnabled = false
            setBackgroundColor(ContextCompat.getColor(context, R.color.primary_warm_grey_five))
        }
    }
}

this is how it looks like in Xml:

   <com.karny.branding.KarnyMaterialButton
        android:id="@+id/smsAuthButton"
        style="@style/PrimaryButtonDisabled"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="94dp"
        android:layout_marginBottom="24dp"
        android:text="@string/sms_auth_check"
        app:layout_constraintEnd_toStartOf="@+id/left_middle_guide_line"
        app:layout_constraintStart_toEndOf="@+id/right_middle_guide_line"
        app:layout_constraintTop_toBottomOf="@+id/smsAuthNumberContainer" />
Bedspread answered 4/2, 2022 at 14:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.