以编程方式将样式应用于MaterialButton

17
我试图创建一个扩展自MaterialButton的自定义视图,并在代码中应用样式,以便我不需要在xml中进行操作。

我想要创建一个继承自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属性不起作用。由于某种原因,背景颜色没有改变,而且它具有主题的主要颜色。但是,如果我尝试在xml中将样式应用于MaterialButton,它会改变颜色。

有什么想法为什么会出现这种情况或者我应该如何解决它?


你正在使用哪个“AppTheme”?如果是“AppCompat”主题,请尝试更改为“MaterialComponents”。 - Jeel Vankhede
我正在使用 Theme.MaterialComponents.Light.NoActionBar - evaristokbza
5个回答

18

使用

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

您正在将 ThemeOverlay 应用于默认样式,而不是应用不同的样式。

这意味着:

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

10

我也遇到了同样的问题。目前我找到的唯一解决方法是通过编程设置色调,例如:

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

1
是的,我最终也这样做了。虽然不是最好的方式,但确实可行。 - evaristokbza
3
这不是“如何在MaterialButton中程序化地应用样式”的正确答案,设置backtuondTintList只是一个hack。这应该是正确的答案 https://dev59.com/C1QK5IYBdhLWcg3wJMgm#63221217 - saulmm

2
对于,不应该有背景(只有文本有颜色)。对于彩色按钮,您应使用默认的填充按钮样式,即。
当作为主题应用时,按钮使用不同的属性。在此处的主题属性映射部分中描述: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>

在你的应用主题中。

1
如果您想要为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)

更多详情请参考这里


1
由于某些原因,这对MaterialButtons不起作用。样式中的任何参数都没有被使用。为了使其工作,我需要使用ContextThemeWrapper - evaristokbza
2
第三个属性是应用程序主题中定义的样式属性,不是样式。 - Gabriele Mariotti
这对于MaterialButton无效。使用“ContextThemeWrapper”至少部分有效。 - P. Ent

0
我在一个简单的用例中遇到了同样的问题,需要更新按钮的backgroundTintisEnabled状态。我的解决方法是:创建一个自定义类,继承自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" />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接