Material effect on button with background color
Asked Answered
D

17

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

Depressed answered 1/11, 2014 at 4:46 Comment(8)
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.Verminous
@SweetWisher The ripple and other effects are not applied in the button when I change the background color.Depressed
@NathanWalters But the default grey color is ugly. :-(Depressed
Believe me, I know... We'll be in great debt to whoever figures out how to do this properly.Verminous
The doc says : * Just add ?android:attr/selectableItemBackground as the background.*Oryx
@Depressed have you tried this code ?<resources> <style name="AppTheme" parent="android:Theme.Material"> <!-- Main theme colors --> <!-- your app's branding color (for the app bar) --> <item name="android:colorPrimary">@color/primary</item> <!-- darker variant of colorPrimary (for status bar, contextual app bars) --> <item name="android:colorPrimaryDark">@color/primary_dark</item> <!-- theme UI controls like checkboxes and text fields --> <item name="android:colorAccent">@color/accent</item> </style> </resources>Oryx
@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?Codify
Awesome answer https://mcmap.net/q/56990/-coloring-buttons-in-android-with-material-design-and-appcompatEthmoid
O
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.

Orb answered 1/11, 2014 at 5:21 Comment(21)
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.Terrorist
#26520479Omphale
@Orb 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.thanksParkin
@Orb I wonder how to combine this working technique without losing rounded corners customization in those buttonDivisible
@Orb just a comment, drawable in ripple under <item android:drawable ... /> can't be a transparent color. In that case ripple won't be shown. It took me a while to realise that.Brambling
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.Prude
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?Detestable
With appcompat 23.1.1 and API 22, disabled and enabled state both take the theme's colorButtonNormal color.Rashidarashidi
@RémyDAVID - yes, colorButtonNormal is the disabled color, colorAccent is the enabled color as per the source - if you're seeing differently, file a bugOrb
Setting android:theme of the button didn't work for me to set the textColor. This answer helped me https://mcmap.net/q/56990/-coloring-buttons-in-android-with-material-design-and-appcompatFerro
@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.Orb
adding a theme to the button breaks onClick for some reason.Prorogue
@PrashanthDebbadwar - that's not how themes work. Sounds like you should ask a new question with your code.Orb
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?Pistareen
@Pistareen - well, that wouldn't really be a material button 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.Orb
@Orb fair enough. I guess the specs do define a specific rectangular shape.Pistareen
@Orb I guess i could just mimic how FAB works github.com/android/platform_frameworks_support/blob/master/…Pistareen
@Terrorist Cleaning and rebuilding the project didn't work. I still don't see ripple effects.Keaton
I am getting ripple effect but it is very difficult to observe. is it because of color I am using?Prorogue
What's if needed not only change color, but set drawable as background (needed keep material design effects)?Krum
Now the Widget.AppCompat.Button.Colored style which uses your theme's colorSecondary for the enabled color instead of the colorAccent.Croton
C
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.

Cornemuse answered 13/1, 2015 at 22:28 Comment(5)
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.Depressed
This creates a flat button with color background, yes. But the created button looks like a square, no rounded corners and shadow effects.Essen
@SanketBerde Exactly. Cuz that's what "flat" button is.Cornemuse
The original question did not mention flat. btw, if you have a <CardView> parent in your solution instead of <FrameLayout> , the resulting button will be raised and have shadows. This even works in pre-lollipop versions.Essen
@SanketBerde great to know about CardView approach, thanks for sharing!Cornemuse
B
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"/>
Byte answered 24/11, 2015 at 21:12 Comment(7)
@Galya make sure you are running on api 21+Byte
api 21+ but not working. Applied on linear layout with clickable-true, background - different color, foreground- specified.Unpremeditated
@Unpremeditated could you provide a snippet of code so I could understand your problem?Byte
Since Button is a TextView and not a FrameLayout, foreground has no effect before 23. google.com/…Yapok
If you are using it with TextView don't forget to set clickable property to true.Maidenhair
but the effect showing button's corner as rectangle but the button drawable was round shape.Kaspar
Requires api level 23!Puritanical
M
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.

Markitamarkka answered 14/4, 2015 at 21:47 Comment(2)
For an assortment of coloured buttons, see other Robert's answer: https://mcmap.net/q/56448/-material-effect-on-button-with-background-colorAlcahest
With the background colour now set, is there also a general attribute I can use in the above code block to specify the button text colour?Alcahest
B
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>
Bromo answered 19/1, 2016 at 8:53 Comment(3)
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.Prude
Is it android:theme="Theme.MaterialButton" or android:theme="@style/Theme.MaterialButton"Nocti
@Nocti It's android:theme="@style/Theme.MaterialButton". Thank you for your correctness.Bromo
C
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.

Chinchin answered 5/8, 2015 at 6:57 Comment(4)
Thank you very much, it worked perfectly for me (even if using Button in xml layouts as AppCompat inflater replaces it with AppCompatButton)Balsaminaceous
Use ColorStateList.valueOf( ... ) to construct a simple ColorStateList of only one color.Dorina
colorAccent and rippleColor do not work in the styleAuthorship
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.Anisole
G
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"/>
Godfrey answered 1/11, 2016 at 15:17 Comment(1)
afaik there is no necessity for the foreground attribute as backgroundTint doesn't override the standard rippleStronghold
M
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 :)

Maltose answered 21/12, 2016 at 9:13 Comment(0)
C
6

Use backgroundTint instead of background

Calais answered 21/9, 2015 at 6:51 Comment(1)
for pre-lolipop you should set the tint in code: #27736390Calais
C
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"
/>
Canea answered 13/4, 2017 at 15:0 Comment(0)
F
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"/>
Feldt answered 16/12, 2015 at 12:54 Comment(0)
A
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

Archaeological answered 3/9, 2015 at 20:22 Comment(0)
W
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

Wilden answered 21/9, 2016 at 11:54 Comment(0)
P
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"/>
Psychometrics answered 12/10, 2017 at 13:17 Comment(0)
B
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
Bier answered 12/12, 2017 at 10:27 Comment(0)
E
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));
   
Exhaust answered 19/7, 2021 at 18:21 Comment(0)
F
-1

I tried this:

android:backgroundTint:"@color/mycolor"

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

Furie answered 15/12, 2015 at 17:55 Comment(1)
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.Prude

© 2022 - 2024 — McMap. All rights reserved.