具有背景色的按钮上的材料效果

253

我正在使用Android v21的支持库。

我创建了一个带有自定义背景颜色的按钮。但是,当我使用这个背景颜色时,材料设计效果如波纹、揭示消失了(除了在点击时的高程效果)。

 <Button
 style="?android:attr/buttonStyleSmall"
 android:background="?attr/colorPrimary"
 android:textColor="@color/white"
 android:textAllCaps="true"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Button1"
 />

背景 以下是一个普通按钮,效果正常。

<Button
 style="?android:attr/buttonStyleSmall"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:textAllCaps="true"
 android:text="Button1"
/>

默认按钮


9
这是因为你用纯色替换了所有美观的Material效果作为背景。至于如何在保留Material效果的同时正确着色按钮,目前还没有人能够解决这个问题。 - Nathan Walters
2
@SweetWisher 当我改变按钮的背景颜色时,涟漪和其他效果未应用于按钮。 - Sathesh
2
@NathanWalters 但是默认的灰色很丑。 :-( - Sathesh
6
相信我,我知道……无论谁能够正确地解决这个问题,我们都将欠他们极大的债。 - Nathan Walters
1
真棒的答案 https://dev59.com/R18d5IYBdhLWcg3wt0EQ#32238489 - Pratik Butani
显示剩余3条评论
17个回答

437
当您使用android:background时,您将使用空白颜色替换了大部分按钮的样式和外观。
更新:从AppCompat23.0.0版本发布开始,有一个新的Widget.AppCompat.Button.Colored样式,它使用您主题的colorButtonNormal作为禁用颜色,colorAccent作为启用颜色。
这使您可以直接将其应用于按钮。
<Button
  ...
  style="@style/Widget.AppCompat.Button.Colored" />

如果您需要自定义colorButtonNormalcolorAccent,您可以使用一个ThemeOverlay,如这个专业提示中所解释的,并在按钮上使用android:theme之前的答案 您可以在v21目录中使用drawable作为背景,例如:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?attr/colorControlHighlight">
    <item android:drawable="?attr/colorPrimary"/>
</ripple>

这将确保您的背景颜色为?attr/colorPrimary,并使用默认的?attr/colorControlHighlight进行默认涟漪动画(如果需要,您也可以在主题中进行设置)。
注意:对于低于v21的版本,您将需要创建自定义选择器。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/primaryPressed" android:state_pressed="true"/>
    <item android:drawable="@color/primaryFocused" android:state_focused="true"/>
    <item android:drawable="@color/primary"/>
</selector>

假设您有一些颜色希望用于默认、按下和聚焦状态。个人而言,我截取了一个涟漪在被选择并拉出主/聚焦状态的过程中的屏幕截图。

13
好的回答!正是我在寻找的,不过在项目决定识别drawable-v21之前,我需要清理和重建才能识别drawable。 - user1354603
4
@ianhanniballake 我想知道如何将这种工作技巧与不丢失按钮圆角自定义相结合。 - Joaquin Iurchuk
3
@ianhanniballake 只是一个评论,<item android:drawable ... /> 下的涟漪可绘制部分不能是透明颜色。在这种情况下,涟漪不会显示出来。我花了一些时间才意识到这一点。 - Ivan Kušt
2
使用appcompat 23.1.1和API 22,禁用和启用状态都采用主题的colorButtonNormal颜色。 - Rémy DAVID
1
@PrashanthDebbadwar - 这不是主题的工作方式。听起来你应该用你的代码提出一个新问题。 - ianhanniballake
显示剩余16条评论

95

在保持 "材料" 效果的同时为 "Flat" 按钮提供自定义背景的另一个简单解决方案是:

  1. 将您的按钮放置在设置了所需背景的 ViewGroup 中
  2. 将当前主题中的 selectableItemBackground(API >=11)设置为您的按钮的背景

例如:

<FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/blue">
    <Button
        style="?android:attr/buttonStyleSmall"
        android:background="?android:attr/selectableItemBackground"
        android:textColor="@android:color/white"
        android:textAllCaps="true"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Button1"
        />
</FrameLayout>

可以用于 平面 按钮,它适用于 API >=11,并且在 >=21 设备上会产生涟漪效果,在 AppCompat 更新以支持涟漪之前,常规按钮保留在 21 之前的设备上。

对于仅限于 >=21 的按钮,您还可以使用selectableItemBackgroundBorderless


你真的很有创意 :-) 虽然这是一种方法,但它可能会增加代码耦合性,使用样式表是推荐的方式。 - Sathesh
3
这将创建一个带有背景颜色的扁平按钮,是的。 但是创建的按钮看起来像一个正方形,没有圆角和阴影效果。 - Sanket Berde
7
@SanketBerde 没错,因为这就是“扁平”按钮的意思。 - kiruwka
2
原始问题没有提到平面设计。 顺便说一下,如果您的解决方案中有一个<CardView>父级而不是<FrameLayout>,则生成的按钮将会凸起并带有阴影。这甚至适用于早期的Lollipop版本。 - Sanket Berde
1
@SanketBerde很高兴了解到CardView的方法,感谢分享! - kiruwka

90

这是一种简单且向后兼容的方法,可以将涟漪效果应用于带有自定义背景的凸起按钮。

您的布局应该如下所示:

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/my_custom_background"
    android:foreground="?android:attr/selectableItemBackground"/>

1
API 21+但不起作用。应用于具有clickable-true、背景-不同颜色、前景-指定的线性布局。 - Alex
@Alex,你能提供一小段代码让我理解你的问题吗? - Bolein95
3
由于 Button 是 TextView 而不是 FrameLayout,所以在 23 版本之前 foreground 属性没有效果。 - androidguy
如果您正在使用TextView,请不要忘记将clickable属性设置为true。 - Muhammad Saqib
需要API级别23! - Sumit Shukla
显示剩余2条评论

25

今天我在尝试使用v22库时遇到了这个问题。

假设你正在使用样式,你可以设置colorButtonNormal属性,按钮将默认使用该颜色。

<style name="AppTheme" parent="BaseTheme">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/primaryColor</item>
    <item name="colorPrimaryDark">@color/primaryColorDark</item>
    <item name="colorAccent">@color/accentColor</item>
    <item name="colorButtonNormal">@color/primaryColor</item>
</style>

除此之外,如果您需要多种颜色的按钮,则仍然可以为该按钮创建一种样式,然后对每个按钮使用该样式(尚未测试,只是推测)。

记得在您的v21 style中,在项目名称之前添加android:


请查看Robert的另一个答案,以获取彩色按钮的混合选择:https://dev59.com/Bl8d5IYBdhLWcg3wkS0V#31825439 - ban-geoengineering
现在设置了背景颜色,那么在上面的代码块中是否还有一般属性可以用来指定按钮文本颜色? - ban-geoengineering

25

@ianhanniballake的回答是绝对正确和简单的。但我花了几天才明白。对于那些不理解他的回答的人,这里提供更详细的实现。

<Button
        android:id="@+id/btn"
        style="@style/MaterialButton"
        ... />


<style name="MaterialButton" parent="Widget.AppCompat.Button.Colored">
    <item name="android:theme">@style/Theme.MaterialButton</item>
   ...
</style>


<style name="Theme.MaterialButton" parent="YourTheme">
    <item name="colorAccent">@color/yourAccentColor</item>
    <item name="colorButtonNormal">@color/yourButtonNormalColor</item>
</style>

===或者===

<Button
        android:id="@+id/btn"
        style="@style/Widget.AppCompat.Button.Colored"
        android:theme="@style/Theme.MaterialButton" />

<style name="Theme.MaterialButton" parent="YourTheme">
    <item name="colorAccent">@color/yourAccentColor</item>
    <item name="colorButtonNormal">@color/yourButtonNormalColor</item>
</style>

5
你的回答对我来说是最后一块拼图。人们还可以在基础主题@style/AppTheme中编写 colorButtonNormal,这样就可以为AppCompat提供默认颜色以相应着色按钮。然后可以使用你的回答单独为一个视图设置主题。 - Siddharth Pant
1
android:theme="Theme.MaterialButton" 还是 android:theme="@style/Theme.MaterialButton" - osrl
1
@osrl 这是 android:theme="@style/Theme.MaterialButton"。感谢您的正确性。 - Frank Nguyen

24

使用AppCompat(22.1.1+)可以添加以下样式:

<style name="MyGreenButton">
    <item name="colorButtonNormal">#009900</item>
</style>

只需应用样式即可使用:

<android.support.v7.widget.AppCompatButton
    style="@style/MyGreenButton"
    android:layout_width="match_width"
    android:layout_height="wrap_content"
    android:text="A Green Button"
    />

我发现,在程序中修改颜色,更新颜色的唯一方法(在API 15或16上)是使用“背景色调列表”。但这不会移除API 21设备上的漂亮的径向动画:

ColorStateList colorStateList = new ColorStateList(new int[][] {{0}}, new int[] {0xFF009900}); // 0xAARRGGBB
button.setSupportBackgroundTintList(colorStateList);

因为在API 15和16上,button.setBackground(...)button.getBackground().mutate().setColorFilter(...)不像在API 21上那样改变按钮的颜色。


1
非常感谢,它对我完美地起作用了(即使在xml布局中使用Button,AppCompat inflater也会将其替换为AppCompatButton)。 - Louis CAD
1
使用 ColorStateList.valueOf( ... ) 构建一个只有一种颜色的简单 ColorStateList - Taig
colorAccent和rippleColor在样式中不起作用。 - Yar
感谢您的这篇文章。 对我有用的是:ViewCompat.setBackgroundTintList(myButton, ColorStateList.valueOf(myColor); setSupportBackgroundTintList 的 Javadoc 表明应该以这种方式调用。 - JerabekJakub

12

我使用了backgroundTint和foreground:

<Button
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:backgroundTint="@color/colorAccent"
    android:foreground="?android:attr/selectableItemBackground"
    android:textColor="@android:color/white"
    android:textSize="10sp"/>

据我所知,前景属性并非必需,因为背景色不会覆盖标准波纹效果。 - Julian Os

7

我来到这篇帖子是为了寻找一种方法来设置我的ListView项的背景颜色,同时保留涟漪效果。

我只需将我的颜色作为背景添加,然后将selectableItemBackground作为前景即可:

<style name="my_list_item">
    <item name="android:background">@color/white</item>
    <item name="android:foreground">?attr/selectableItemBackground</item>
    <item name="android:layout_height">@dimen/my_list_item_height</item>
</style>

它的效果非常好。我猜按钮也可以使用同样的技术。祝你好运 :)


6
请使用`backgroundTint`代替`background`。

对于早期的Lollipop版本,您应该在代码中设置色调:https://dev59.com/114c5IYBdhLWcg3wl7Xm - shem

6
如果您对ImageButton感兴趣,可以尝试这个简单的方法:
<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@android:drawable/ic_button"
    android:background="?attr/selectableItemBackgroundBorderless"
/>

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