253

I am using Android v21 support library.

I have created a button with custom background color. The Material design effects like ripple, reveal are gone (except the elevation on click) when I use the back ground color.

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

Background The following is a normal button and the effects are working just fine.

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

Default button

Sathesh
  • 6,323
  • 6
  • 36
  • 48
  • 9
    This occurs because you replace the background with all the nice Material effects with a solid color. As to how you properly color a button while keeping the Material effects, no one on SO has been able to figure that out yet. – Nathan Walters Nov 01 '14 at 04:49
  • 2
    @SweetWisher The ripple and other effects are not applied in the button when I change the background color. – Sathesh Nov 01 '14 at 04:49
  • 2
    @NathanWalters But the default grey color is ugly. :-( – Sathesh Nov 01 '14 at 04:50
  • 6
    Believe me, I know... We'll be in great debt to whoever figures out how to do this properly. – Nathan Walters Nov 01 '14 at 04:53
  • The doc says : * Just add `?android:attr/selectableItemBackground` as the background.* – SweetWisher ツ Nov 01 '14 at 05:00
  • @Sathesh have you tried this code ?` ` – SweetWisher ツ Nov 01 '14 at 05:01
  • @NathanWalters I don't know about Material effects but in previous styles, the `Button` press effect goes off when we put solid color on `Button`, but if we add alpha (little transparency) to that color, you can see the `Button` press effect. Have you tried this for Material effects? – Bugs Happen Jul 23 '15 at 08:27
  • 1
    Awesome answer http://stackoverflow.com/a/32238489/1318946 – Pratik Butani Nov 30 '15 at 11:42

17 Answers17

437

When you use android:background, you are replacing much of the styling and look and feel of a button with a blank color.

Update: As of the version 23.0.0 release of AppCompat, there is a new Widget.AppCompat.Button.Colored style which uses your theme's colorButtonNormal for the disabled color and colorAccent for the enabled color.

This allows you apply it to your button directly via

<Button
  ...
  style="@style/Widget.AppCompat.Button.Colored" />

If you need a custom colorButtonNormal or colorAccent, you can use a ThemeOverlay as explained in this pro-tip and android:theme on the button.

Previous Answer

You can use a drawable in your v21 directory for your background such as:

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

This will ensure your background color is ?attr/colorPrimary and has the default ripple animation using the default ?attr/colorControlHighlight (which you can also set in your theme if you'd like).

Note: you'll have to create a custom selector for less than 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>

Assuming you have some colors you'd like for the default, pressed, and focused state. Personally, I took a screenshot of a ripple midway through being selected and pulled the primary/focused state out of that.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • 13
    Nice answer! Exactly what I was looking for, however needed to clean and rebuild before the project decided to identify the drawable in drawable-v21. – user1354603 Nov 06 '14 at 12:31
  • 1
    http://stackoverflow.com/questions/26519979/coloring-buttons-in-android-with-material-design-and-appcompat – gaara87 Mar 12 '15 at 03:46
  • @ianhanniballake i wonder on how to use material effect buttons with background on pre-lollipop version of android. please help and give me example and links.thanks – Cristiana Chavez Jul 14 '15 at 10:37
  • 4
    @ianhanniballake I wonder how to combine this working technique without losing rounded corners customization in those button – Joaquin Iurchuk Aug 24 '15 at 16:14
  • 3
    @ianhanniballake just a comment, drawable in ripple under can't be a transparent color. In that case ripple won't be shown. It took me a while to realise that. – Ivan Kušt Aug 26 '15 at 07:45
  • Dude a 100 thanks for that protip. Now I finally know ThemeOverlay was the answer. Been Solving this puzzle for the whole day. At last we devs a relatively simple and elegant solution for this mess. – Siddharth Pant Jan 29 '16 at 20:03
  • This is working for me except that I can't get rid of the padding that is inherited by Widget.Material.Button.Colored. Tried setting it to 0dp and -xdp to offset but I don't see any change. Has anyone else had this problem? – Tyler Pfaff Feb 08 '16 at 21:02
  • 2
    With appcompat 23.1.1 and API 22, disabled and enabled state both take the theme's colorButtonNormal color. – Rémy DAVID Feb 11 '16 at 12:08
  • @RémyDAVID - yes, `colorButtonNormal` is the disabled color, `colorAccent` is the enabled color as per [the source](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v7/appcompat/res/drawable-v21/abc_btn_colored_material.xml) - if you're seeing differently, [file a bug](https://code.google.com/p/android/issues/entry?template=Support%20Library%20bug) – ianhanniballake Feb 11 '16 at 17:20
  • 1
    Setting `android:theme` of the button didn't work for me to set the textColor. This answer helped me http://stackoverflow.com/a/32238489/3075340 – Micro Mar 24 '16 at 16:37
  • @MicroR - correct. textColor is a style and should be used with `@style/`, it is not a theme attribute. You could use `textColorPrimary` as that is a theme attribute. – ianhanniballake Mar 24 '16 at 16:49
  • adding a theme to the button breaks onClick for some reason. – Prashanth Debbadwar Apr 19 '16 at 14:29
  • 1
    @PrashanthDebbadwar - that's not how themes work. Sounds like you should ask a new question with your code. – ianhanniballake Apr 19 '16 at 16:25
  • So does that mean there is no way to define custom button **shape** and keep all of the appcompat + material ripple w/out rolling your own? – tir38 May 06 '16 at 01:12
  • @tir38 - well, that wouldn't really be a [material button](https://www.google.com/design/spec/components/buttons.html#buttons-raised-buttons) then would it? :-) You can adopt something similar to the 'Previous Answer' section and use a custom shape drawable instead of a color if you really want the ripple over a custom shape. – ianhanniballake May 06 '16 at 01:19
  • @ianhanniballake fair enough. I guess the specs do define a specific rectangular shape. – tir38 May 16 '16 at 19:16
  • @ianhanniballake I guess i could just mimic how FAB works https://github.com/android/platform_frameworks_support/blob/master/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java – tir38 May 16 '16 at 19:27
  • @user1354603 Cleaning and rebuilding the project didn't work. I still don't see ripple effects. – Srikar Reddy Jul 15 '16 at 07:19
  • I am getting ripple effect but it is very difficult to observe. is it because of color I am using? – Prashanth Debbadwar Jul 19 '16 at 10:46
  • What's if needed not only change color, but set drawable as background (needed keep material design effects)? – Yura Shinkarev Jul 11 '17 at 11:25
  • Now the `Widget.AppCompat.Button.Colored` style which uses your theme's `colorSecondary` for the enabled color instead of the `colorAccent`. – Leonardo Sibela Jun 29 '22 at 15:57
95

There is another simple solution to provide custom background for "Flat" buttons while keeping their "Material" effects.

  1. Place your button in ViewGroup with desired background set there
  2. set selectableItemBackground from current theme as background for your button (API >=11)

i.e. :

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

Can be used for Flat buttons, it works on API >=11, and you will get ripple effect on >=21 devices, keeping regular buttons on pre-21 till AppCompat is updated to support ripple there as well.

You can also use selectableItemBackgroundBorderless for >=21 buttons only.

kiruwka
  • 9,250
  • 4
  • 30
  • 41
  • You literally thought out of the box :-) Although this is one way of doing this, it might increase code coupling and using styles would be the recommended way. – Sathesh Mar 19 '15 at 23:44
  • 3
    This creates a flat button with color background, yes. But the created button looks like a square, no rounded corners and shadow effects. – Sanket Berde Jun 07 '15 at 07:11
  • 7
    @SanketBerde Exactly. Cuz that's what "flat" button is. – kiruwka Jun 07 '15 at 14:36
  • 2
    The original question did not mention flat. btw, if you have a parent in your solution instead of , the resulting button will be raised and have shadows. This even works in pre-lollipop versions. – Sanket Berde Jun 08 '15 at 03:36
  • 1
    @SanketBerde great to know about `CardView` approach, thanks for sharing! – kiruwka Jun 08 '15 at 08:02
90

Here is a simple and backward compatible way to deliver ripple effect to raised buttons with the custom background.

Your layout should look like this

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/my_custom_background"
    android:foreground="?android:attr/selectableItemBackground"/>
AndroidDev
  • 20,466
  • 42
  • 148
  • 239
Bolein95
  • 2,947
  • 2
  • 26
  • 31
  • @Galya make sure you are running on api 21+ – Bolein95 Jul 05 '16 at 08:18
  • 1
    api 21+ but not working. Applied on linear layout with clickable-true, background - different color, foreground- specified. – Alex Oct 27 '16 at 21:25
  • @Alex could you provide a snippet of code so I could understand your problem? – Bolein95 Oct 28 '16 at 09:36
  • 3
    Since Button is a TextView and not a FrameLayout, `foreground` has no effect before 23. https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwj7zID6xtLQAhVrlFQKHXLQDegQFggdMAA&url=http%3A%2F%2Fstackoverflow.com%2Fquestions%2F33379534%2Fcall-requires-api-level-23-getforeground-error-but-is-a-framelayout-method-s&usg=AFQjCNFPD5nnBfbJw4vvlAf5n9VAakv-pw&sig2=tXMNIibafvvsRgh6PLS3ng – androidguy Dec 01 '16 at 08:18
  • If you are using it with TextView don't forget to set clickable property to true. – Muhammad Saqib Apr 29 '17 at 21:18
  • but the effect showing button's corner as rectangle but the button drawable was round shape. – Devendra Singh Dec 10 '18 at 12:14
  • Requires api level 23! – Sumit Shukla Apr 24 '20 at 10:05
25

I ran into this problem today actually playing with the v22 library.

Assuming that you're using styles you can set the colorButtonNormal property and the buttons will use that color by default.

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

Outside of that you could still make a style for the button then use that per button if you needed an assortment of colors (haven't tested, just speculating).

Remember to add android: before the item names in your v21 style.

Ethaan
  • 11,291
  • 5
  • 35
  • 45
Robert Rose
  • 251
  • 3
  • 3
25

The @ianhanniballake's answer is absolutely correct and simple. But it took me few days to understand. For someone who don't understand his answer, here is more detail implementation

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

===Or===

<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>
Frank Nguyen
  • 6,493
  • 3
  • 38
  • 37
  • 5
    Your answer was the last piece of the puzzle for me. One can also write `colorButtonNormal` in the base theme `@style/AppTheme`. This gives a default color to AppCompat to colorize the buttons accordingly. And then can use your answer to theme a view individually. – Siddharth Pant Jan 29 '16 at 20:05
  • 1
    Is it `android:theme="Theme.MaterialButton"` or `android:theme="@style/Theme.MaterialButton"` – osrl Nov 14 '16 at 13:23
  • 1
    @osrl It's `android:theme="@style/Theme.MaterialButton"`. Thank you for your correctness. – Frank Nguyen Dec 08 '17 at 01:55
24

With AppCompat (22.1.1+) you can add a style like this:

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

And use it by just applying the style:

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

Programmatically changing the color, I found that the only way to update the color (on API 15 or 16) was to use the 'background tint list' instead. And it doesn't remove the nice radial animation on API 21 devices:

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

Because button.setBackground(...) and button.getBackground().mutate().setColorFilter(...) do not change the button color on API 15 and 16 like they do on API 21.

Robert
  • 14,999
  • 4
  • 39
  • 46
  • 1
    Thank you very much, it worked perfectly for me (even if using Button in xml layouts as AppCompat inflater replaces it with AppCompatButton) – Louis CAD Sep 21 '15 at 01:04
  • 1
    Use `ColorStateList.valueOf( ... )` to construct a simple `ColorStateList` of only one color. – Taig Sep 05 '16 at 13:32
  • colorAccent and rippleColor do not work in the style – Yar Sep 28 '16 at 16:56
  • Thank you for this post. What worked for me: `ViewCompat.setBackgroundTintList(myButton, ColorStateList.valueOf(myColor);` Javadoc for `setSupportBackgroundTintList` says it should be called this way. – JerabekJakub Nov 24 '17 at 09:45
12

I used the backgroundTint and 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"/>
SpyZip
  • 5,511
  • 4
  • 32
  • 53
  • afaik there is no necessity for the foreground attribute as backgroundTint doesn't override the standard ripple – Julian Os Apr 24 '17 at 20:41
7

I came to this post looking for a way to have a background color of my ListView Item, yet keep the ripple.

I simply added my color as a background and the selectableItemBackground as a foreground:

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

and it works like a charm. I guess the same technique could be used for buttons as well. Good luck :)

Joakim
  • 3,224
  • 3
  • 29
  • 53
6

Use backgroundTint instead of background

shem
  • 4,686
  • 2
  • 32
  • 43
  • for pre-lolipop you should set the tint in code: http://stackoverflow.com/questions/27735890/lollipops-backgroundtint-has-no-effect-on-a-button – shem Sep 21 '15 at 09:12
6

If you are interested in ImageButton you can try this simple thing :

<ImageButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@android:drawable/ic_button"
    android:background="?attr/selectableItemBackgroundBorderless"
/>
5

v22.1 of appcompat-v7 introduced some new possibilities. Now it's possible to assign a specific theme only to one view.

Deprecated use of app:theme for styling Toolbar. You can now use android:theme for toolbars on all API level 7 and higher devices and android:theme support for all widgets on API level 11 and higher devices.

So instead of setting the desired color in a global theme, we create a new one and assign it only to the Button

Example:

<style name="MyColorButton" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorButtonNormal">@color/myColor</item>
</style>

And use this style as theme of your Button

<Button
 style="?android:attr/buttonStyleSmall"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:text="Button1"
 android:theme="@style/MyColorButton"/>
Bhargav Thanki
  • 4,924
  • 2
  • 37
  • 43
4

If you're ok with using a third party library, check out traex/RippleEffect. It allows you to add a Ripple effect to ANY view with just a few lines of code. You just need to wrap, in your xml layout file, the element you want to have a ripple effect with a com.andexert.library.RippleView container.

As an added bonus it requires Min SDK 9 so you can have design consistency across OS versions.

Here's an example taken from the libraries' GitHub repo:

<com.andexert.library.RippleView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:rv_centered="true">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@android:drawable/ic_menu_edit"
        android:background="@android:color/holo_blue_dark"/> 

</com.andexert.library.RippleView>

You can change the ripple colour by adding this attribute the the RippleView element: app:rv_color="@color/my_fancy_ripple_color

guy.gc
  • 3,359
  • 2
  • 24
  • 39
3
<android.support.v7.widget.AppCompatButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:backgroundTint="#fff"
    android:textColor="#000"
    android:text="test"/>

Use

xmlns:app="http://schemas.android.com/apk/res-auto"

and the color will acept on pre-lolipop

André Bäuml
  • 205
  • 2
  • 10
2

There are two approaches explained in the great tutorial be Alex Lockwood: http://www.androiddesignpatterns.com/2016/08/coloring-buttons-with-themeoverlays-background-tints.html:

Approach #1: Modifying the button’s background color w/ a ThemeOverlay

<!-- res/values/themes.xml -->
<style name="RedButtonLightTheme" parent="ThemeOverlay.AppCompat.Light">
    <item name="colorAccent">@color/googred500</item>
</style>

<Button
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:theme="@style/RedButtonLightTheme"/>

Approach #2: Setting the AppCompatButton’s background tint

<!-- res/color/btn_colored_background_tint.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Disabled state. -->
    <item android:state_enabled="false"
          android:color="?attr/colorButtonNormal"
          android:alpha="?android:attr/disabledAlpha"/>

    <!-- Enabled state. -->
    <item android:color="?attr/colorAccent"/>

</selector>

<android.support.v7.widget.AppCompatButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:backgroundTint="@color/btn_colored_background_tint"/>
makovkastar
  • 5,000
  • 2
  • 30
  • 50
2

Programmatically applying the colors:

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {

    ColorStateList colorStateListRipple = new ColorStateList(
            new int[][] {{0}},
            new int[] {Color.WHITE} // ripple color
            );

    RippleDrawable rippleDrawable = (RippleDrawable) myButton.getBackground();
    rippleDrawable.setColor(colorStateListRipple);
    myButton.setBackground(rippleDrawable); // applying the ripple color
}

ColorStateList colorStateList = new ColorStateList(
        new int[][]{
                new int[]{android.R.attr.state_pressed}, // when pressed
                new int[]{android.R.attr.state_enabled}, // normal state color
                new int[]{} // normal state color
        },
        new int[]{
                Color.CYAN, // when pressed
                Color.RED, // normal state color
                Color.RED // normal state color
        }
);

ViewCompat.setBackgroundTintList(myButton, colorStateList); // applying the state colors
Marlon
  • 1,839
  • 2
  • 19
  • 42
1

Programmatically:

 myMaterialButton.setRippleColor(ColorStateList.valueOf(Color.RED));

For example:

 MaterialButton myMaterialButton = new MaterialButton(this);
 myMaterialButton.setLayoutParams(myButtonParams);
 myMaterialButton.setBackgroundColor(Color.GRAY);
 myMaterialButton.setRippleColor(ColorStateList.valueOf(Color.RED));
   
Androidcoder
  • 4,389
  • 5
  • 35
  • 50
-1

I tried this:

android:backgroundTint:"@color/mycolor"

instead of changing background property. This does not remove the material effect.

shiladitya
  • 2,290
  • 1
  • 23
  • 36
  • This will not work for pre-Lollipop devices (70% market share). Actually it has been backported but Android Studio as of now still shows errors when using it without the android prefix. – Siddharth Pant Jan 29 '16 at 20:15