Применить стиль к MaterialButton программно

Я пытаюсь создать собственное представление, расширяющееся от MaterialButton и применить стиль в коде, поэтому мне не нужно делать это в XML.

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

Стиль это:

<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>

Все работает нормально но backgroundTint имущество. По какой-то причине цвет фона не меняется, и он имеет основной цвет Темы. Однако, если я попытаюсь применить стиль к MaterialButton в XML это меняет цвет.

Любая идея, почему это может происходить или как я могу этого достичь?

5 ответов

Решение

Я тоже сталкиваюсь с той же проблемой. Единственный обходной путь, который я нашел, - это программно установить оттенок:

button.setBackgroundTintList(ColorStateList.valueOf(Color.RED));

С помощью

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

вы применяете наложение темы к стилю по умолчанию, вы не применяете другой стиль.

Это значит:

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

Если вы хотите применить другой стиль, вам необходимо:

  • Определите настраиваемый атрибут в attrs.xml
    <attr name="myButtonStyle" format="reference"/>
  • Назначение стиля этому атрибуту в теме вашего приложения:
   <style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
        <item name="myButtonStyle">@style/CustomButtonStyle</item>
   </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>

Наконец используйте:

val customButton = MaterialButton(context, null, R.attr.myButtonStyle)

Для TextButtonфона быть не должно (просто текст имеет цвет). Для цветной кнопки следует использовать стиль кнопки с заливкой по умолчанию, которыйWidget.MaterialComponents.Button.

При применении в качестве темы кнопка использует разные атрибуты. Это описано в разделе " Тематическое сопоставление атрибутов" здесь: 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                            |
| ...                    | ...                                     |
+------------------------+-----------------------------------------+

В вашем случае тема должна выглядеть примерно так:

<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>

Вы также можете изменить все кнопки на определенный стиль с помощью

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

в теме вашего приложения.

Если вы хотите изменить свой стиль для CustomView, вы должны передать его конструктору, передав его в третий параметр defStyleAttr как это:

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

и вы можете инициализировать это программно,

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

Для более подробной информации обратитесь сюда

У меня была такая же проблема для простого варианта использования, мне нужно обновить кнопку backgroundTintа также isEnabledукажите, что я сделал: я создал собственный класс, который расширяется от MaterialButton

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

затем я добавил расширение к этому классу для обновления атрибутов стиля кнопки:

      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))
        }
    }
}

вот как это выглядит в 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" />