Coloring Buttons in Android with Material Design and AppCompat

192,036

Solution 1

Officially fixed in Support Library rev.22 (Fri March 13, 2015). See relevant google code issue:

https://issuetracker.google.com/issues/37008632

Usage example

theme.xml:

<item name="colorButtonNormal">@color/button_color</item>

v21/theme.xml

<item name="android:colorButtonNormal">@color/button_color</item>

Solution 2

Edit (22.06.2016):

Appcompat library started to support material buttons after I posted the original response. In this post you can see the easiest implementation of raised and flat buttons.

Original Answer:

Since that AppCompat doesn't support the button yet you can use xml as backgrounds. For doing that I had a look at the source code of the Android and found the related files for styling material buttons.

1 - Look at the original implementation of material button from source.

Have a look at the btn_default_material.xml on android source code.

You can copy the file into your projects drawable-v21 folder. But don't touch the color attr here. The file you need to change is the second file.

drawable-v21/custom_btn.xml

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?attr/colorControlHighlight">
    <item android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>

2 - Get the shape of the original material button

As you realise there is a shape used inside this drawable which you can find in this file of the source code.

<inset xmlns:android="http://schemas.android.com/apk/res/android"
   android:insetLeft="@dimen/button_inset_horizontal_material"
   android:insetTop="@dimen/button_inset_vertical_material"
   android:insetRight="@dimen/button_inset_horizontal_material"
   android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle">
    <corners android:radius="@dimen/control_corner_material" />
    <solid android:color="?attr/colorButtonNormal" />
    <padding android:left="@dimen/button_padding_horizontal_material"
             android:top="@dimen/button_padding_vertical_material"
             android:right="@dimen/button_padding_horizontal_material"
             android:bottom="@dimen/button_padding_vertical_material" />
</shape>

3 - Getting dimensions of the material button

And in this file you there are some dimensions used from the file that you can find here. You can copy the whole file and put into your values folder. This is important for applying the same size (that is used in material buttons) to all buttons

4 - Create another drawable file for old versions

For older versions you should have another drawable with the same name. I am directly putting the items inline instead of referencing. You may want to reference them. But again, the most important thing is the original dimensions of the material button.

drawable/custom_btn.xml

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
    <!-- pressed state -->
    <item android:state_pressed="true">
        <inset xmlns:android="http://schemas.android.com/apk/res/android"
            android:insetLeft="@dimen/button_inset_horizontal_material"
            android:insetTop="@dimen/button_inset_vertical_material"
            android:insetRight="@dimen/button_inset_horizontal_material"
            android:insetBottom="@dimen/button_inset_vertical_material">
            <shape android:shape="rectangle">
                <corners android:radius="@dimen/control_corner_material" />
                <solid android:color="@color/PRESSED_STATE_COLOR" />
                <padding android:left="@dimen/button_padding_horizontal_material"
                    android:top="@dimen/button_padding_vertical_material"
                    android:right="@dimen/button_padding_horizontal_material"
                    android:bottom="@dimen/button_padding_vertical_material" />
            </shape>
        </inset>
    </item>
    
    <!-- focused state -->
    <item android:state_focused="true">
        <inset xmlns:android="http://schemas.android.com/apk/res/android"
            android:insetLeft="@dimen/button_inset_horizontal_material"
            android:insetTop="@dimen/button_inset_vertical_material"
            android:insetRight="@dimen/button_inset_horizontal_material"
            android:insetBottom="@dimen/button_inset_vertical_material">
            <shape android:shape="rectangle">
                <corners android:radius="@dimen/control_corner_material" />
                <solid android:color="@color/FOCUSED_STATE_COLOR" />
                <padding android:left="@dimen/button_padding_horizontal_material"
                    android:top="@dimen/button_padding_vertical_material"
                    android:right="@dimen/button_padding_horizontal_material"
                    android:bottom="@dimen/button_padding_vertical_material" />
            </shape>
        </inset>
    </item>
    
    <!-- normal state -->
    <item>
        <inset xmlns:android="http://schemas.android.com/apk/res/android"
            android:insetLeft="@dimen/button_inset_horizontal_material"
            android:insetTop="@dimen/button_inset_vertical_material"
            android:insetRight="@dimen/button_inset_horizontal_material"
            android:insetBottom="@dimen/button_inset_vertical_material">
            <shape android:shape="rectangle">
                <corners android:radius="@dimen/control_corner_material" />
                <solid android:color="@color/NORMAL_STATE_COLOR" />
                <padding android:left="@dimen/button_padding_horizontal_material"
                    android:top="@dimen/button_padding_vertical_material"
                    android:right="@dimen/button_padding_horizontal_material"
                    android:bottom="@dimen/button_padding_vertical_material" />
            </shape>
        </inset>
    </item>
</selector>

Result

Your button will have ripple effect on Lollipop devices. The old versions will have exactly same button except the ripple effect. But since that you provide drawables for different states, they'll also respond to touch events (as the old way).

Solution 3

This has been enhanced in v23.0.0 of AppCompat library with the addition of more themes including

Widget.AppCompat.Button.Colored

First of all include appCompat dependency if you haven't already

compile('com.android.support:appcompat-v7:23.0.0') {
    exclude group: 'com.google.android', module: 'support-v4'
}

now since you need to use v23 of the app compat, you'll need to target SDK-v23 as well!

    compileSdkVersion = 23
    targetSdkVersion = 23

In your values/theme

<item name="android:buttonStyle">@style/BrandButtonStyle</item>

In your values/style

<style name="BrandButtonStyle" parent="Widget.AppCompat.Button.Colored">
    <item name="colorButtonNormal">@color/yourButtonColor</item>
    <item name="android:textColor">@color/White</item>
</style>

In your values-v21/style

<style name="BrandButtonStyle" parent="Widget.AppCompat.Button.Colored">
    <item name="android:colorButtonNormal">@color/yourButtonColor</item>
    <item name="android:textColor">@color/White</item>
</style>

Since your button theme is based on Widget.AppCompat.Button.Colored The text color on the button is by default white!

but it seems there is an issue when you disable the button, the button will change its color to light grey, but the text color will remain white!

a workaround for this is to specifically set the text color on the button to white! as I have done in the style shown above.

now you can simply define your button and let AppCompat do the rest :)

<Button
        android:layout_width="200dp"
        android:layout_height="48dp" />

Disabled State Disabled state

Enabled State Enabled State

Edit:

To add <Button android:theme="@style/BrandButtonStyle"/>

Solution 4

In Android Support Library 22.1.0, Google made the Button tint aware. So, another way to customise the background color of button is to use the backgroundTint attribute.

For example,

<Button
       android:id="@+id/add_remove_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:backgroundTint="@color/bg_remove_btn_default"
       android:textColor="@android:color/white"
       tools:text="Remove" />

Solution 5

To support colored buttons use the latest AppCompat library (>23.2.1) with:

inflate - XML

AppCompat Widget:

android.support.v7.widget.AppCompatButton

AppCompat Style:

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

NB! To set a custom color in xml: use the attr: app instead of android

(use alt+enter or declare xmlns:app="http://schemas.android.com/apk/res-auto" to use app)

app:backgroundTint="@color/your_custom_color"

Example:

<android.support.v7.widget.AppCompatButton
     style="@style/Widget.AppCompat.Button.Colored"
     app:backgroundTint="@color/your_custom_color"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"     
     android:text="Colored Button"/>

or set it programmatically - JAVA

 ViewCompat.setBackgroundTintList(your_colored_button,
 ContextCompat.getColorStateList(getContext(),R.color.your_custom_color));
Share:
192,036

Related videos on Youtube

mail929
Author by

mail929

Updated on January 15, 2021

Comments

  • mail929
    mail929 over 3 years

    Before the AppCompat update came out today I was able to change the color of buttons in Android L but not on older versions. After including the new AppCompat update I am unable to change the color for either version, when I do try the button just disappears. Does anyone know how to change the button color?

    The following pictures shows what I want to achieve:

    picture showing desired result

    The white button is default, the red one is what I want.

    This is what I was doing previously to change the color of the buttons in the styles.xml:

    <item name="android:colorButtonNormal">insert color here</item>
    

    and to do it dynamically:

    button.getBackground().setColorFilter(getResources().getColor(insert color here), PorterDuff.Mode.MULTIPLY);
    

    Also I did change the theme parent from @android:style/Theme.Material.Light.DarkActionBar to Theme.AppCompat.Light.DarkActionBar

    • Informatic0re
      Informatic0re over 9 years
      I tried the same but nothing changed the color of the Button. I also removed android: from the attribute becouse it is from the support lib and not part of android namespace
    • Informatic0re
      Informatic0re over 9 years
      If you are using android:colorButtonNormal with Android 5.0 it works - but it seems not to be backwards compatible
    • mail929
      mail929 over 9 years
      Yes that is exactly what I was experiencing
    • Informatic0re
      Informatic0re over 9 years
      I also figured out that the accent color does not change the CheckBox color, but it does in older versions
    • theapache64
      theapache64 about 8 years
      plus one for that dynamic method. :)
  • fernandohur
    fernandohur over 9 years
    So after implementing this, we just add android:background="@drawable/custom_btn" to our button?
  • eluleci
    eluleci over 9 years
    Yes exactly. If you use the same background for all buttons and if you don't want to add this again and again, define a button style in your styles.xml <style name="ButtonStyle" parent="android:Widget.Button"> <item name="android:background">@drawable/custom_btn</item> </style> and add button style in your theme <item name="android:buttonStyle">@style/ButtonStyle</item>
  • xakpc
    xakpc over 9 years
    how can i add elevation to this button on pre-21? as i understand i should add new shape with shadow below button, but where exactly i should put it?
  • eluleci
    eluleci over 9 years
    the shadow should be added to drawables for each state. ex: <!-- pressed state --> <item android:state_pressed="true"> <!-- include the drawable that contains shadow --> </item>
  • Sam
    Sam over 9 years
    Very annoying that this isn't supported out of the box
  • eluleci
    eluleci over 9 years
    I totally agree with you. Button is the mostly used widget but it is not included in the support library for material. I wonder what they were thinking.
  • JMRboosties
    JMRboosties about 9 years
    It's unclear to me how to set the color for a single instance of the button. For example, I want most of my buttons to just be the standard grey, but a specific button to be red. Setting colorButtonNormal in the main theme will obviously make all buttons red, but when I try to include colorButtonNormal="@color/red" in the style which the button uses, this is ignored. What is the correct way to do this? I don't think having a different btn_default_mtrl_shape file for each color you may use is practical...
  • JMRboosties
    JMRboosties about 9 years
    Also, following your guide it seems that the background isn't showing up at all...
  • theblang
    theblang about 9 years
    If you don't want to change any of the "tint" colors or selected colors this is definitely an easy solution to still get a feedback with colored buttons. Thanks!
  • ZakTaccardi
    ZakTaccardi about 9 years
    @JMRboosties I am having the same problem. Did you find a solution?
  • JMRboosties
    JMRboosties about 9 years
    I ended up getting it to work, the guide is fine. At step 2, you might want to hardcode the color resource directly into the shape's solid color instead of relying on a ?attr. I was trying to use a value from ?attr but was doing it wrong.
  • ScouseChris
    ScouseChris about 9 years
    Has anyone else noticed the drop shadow effect disappears when using a shape drawable in the ripple but not if you set a colour directly?
  • Igoussam
    Igoussam about 9 years
    Is there any example how to use it? How can I set colors on just one Button?
  • WindRider
    WindRider about 9 years
    I updated the answer with the attribute definitions that i use and know they work.
  • dominik4142
    dominik4142 about 9 years
    @WindRider I have a problem though, I want to use it to create buttons with different colors. I create styles Button.Red, Button.Green, all with parent equals Button base style. I set item colorButtonNormal to requested color, set this as button theme. Unfortunately it only colours buttons for 21. Below it does not override colorButtonNormal defined in theme.
  • WindRider
    WindRider about 9 years
    What parent style do you use? Check that all your styles contain colorButtonNormal for <v21 and android:colorButtonNormal for v21. Looks like you only have the second defined. Just a give me a pastebin with your styles.
  • Greg Ennis
    Greg Ennis about 9 years
    @WindRider Unfortunately I'm having the same issue. When applying theme directly to a button, like <Button android:theme="@style/RedButton"... />, the theme only gets applied for v21. (If I put the colors in the app-wide theme definitions, it works for all API levels, but I cant do that because I need buttons with different colors). Seems like a bug in appcompat to me.
  • Greg Ennis
    Greg Ennis about 9 years
    @dominik4142 did you find a solution for this? (see my comment above)
  • WindRider
    WindRider about 9 years
    Greg, shouldn't it be <Button style="@style/RedButton"... /> instead?
  • dominik4142
    dominik4142 about 9 years
    @WindRider you didn't mention me and I didn't get any notification. This is what I found out: In order for this styling to work, I need to set theme:= of the button. This works nicely on >21. Unfortunately, this doesn't seem to bother lower versions. They are bothered only by settings colorButtonNormal in app theme. Because I want to achieve different button colors, your solution doesn't help me much.
  • dominik4142
    dominik4142 about 9 years
    @GregEnnis I totally agree, same for me. I worked it around by replacing button background on lower versions (this were very small contextual buttons used in one place so I could afford that). I simply used theme= and style= both on one button, theme to apply colouring on >21, style override in both <21 (background + margins) and >21 (empty to disable <21 background replacement).
  • Greg Ennis
    Greg Ennis about 9 years
    @WindRider - no it should not. android:theme is required here. As I mentioned, it works on v21, so that is not the problem. It sounds like you have it working - can you post a working sample?
  • GuillermoMP
    GuillermoMP about 9 years
    Finally! Blog post about the 22.1 release: android-developers.blogspot.no/2015/04/…
  • Stephane Maarek
    Stephane Maarek about 9 years
    Documentation says: "This will automatically be used when you use Button in your layouts. You should only need to manually use this class when writing custom views." developer.android.com/reference/android/support/v7/widget/… does this mean we don't have to manually change all of our layouts and java code?
  • Anton Holovin
    Anton Holovin about 9 years
    @Stephane no, we should change layouts. chris.banes.me/2015/04/22/support-libraries-v22-1-0
  • swooby
    swooby about 9 years
    AWESOME! Now, is there a AppCompatToggleButton that lets you change the background of ToggleButtons? Maybe I can plagiarize the code...
  • Daniel Gomez Rico
    Daniel Gomez Rico about 9 years
    Dont work for Base.Widget.AppCompat.Button in Compat 22.1.1
  • hunyadym
    hunyadym about 9 years
    With AppCompat 22.1.1 it works for me on 2.3.7, 4.4.4 and 5.1 too for one button only: setting button's android:theme="@style/ColoredButton", and in styles.xml <style name="ColoredButton" parent="Widget.AppCompat.Button"> <item name="colorButtonNormal">@color/button_yellow</item> </style>
  • JJD
    JJD about 9 years
    Android Support Library 22.1.1 has been released. A changelog is pending.
  • Juan Saravia
    Juan Saravia almost 9 years
    Thanks!!! I just changed the parent value for "Widget.AppCompat.EditText" as I need to update the EditText style.
  • Timur_C
    Timur_C almost 9 years
    Unfortunately this works only on API 21+. The android:backgroundTint attribute doesn't work on pre-Lollipop even with AppCompat library. Only colorButtonNormal from theme works on pre-Lollipop.
  • Dave Jensen
    Dave Jensen almost 9 years
    This plus the link to the blog post is the answer. @mail929 it would be great if you could update the answer as such.
  • Natix
    Natix almost 9 years
    How do you change the text color (for example to white)?
  • gladed
    gladed almost 9 years
    @hunyadym, adding a theme to the button breaks onClick for some reason. I would classify this as a workaround until the Design library provides a better way.
  • hunyadym
    hunyadym almost 9 years
    @gladed: It seems in your case the problem is this bug: code.google.com/p/android/issues/detail?id=62795#c1
  • Rachel
    Rachel almost 9 years
    Thank you, worked for me as well. Make sure to use android:theme and not style. You can also add colorControlHighlight and colorControlActivated.
  • Rachel
    Rachel almost 9 years
    @gladed I have the same issue. Did you manage to find a good workaround?
  • Rachel
    Rachel almost 9 years
    @gladed My current workaround is to set the OnClickListener programmatically.
  • gladed
    gladed almost 9 years
    @Rachel another way is to use AppCompatButton and programmatically set the background tint modes but this is very messy. The way you are doing it is probably the best we can do for now.
  • Shajeel Afzal
    Shajeel Afzal almost 9 years
    It is not working, the AppCompat library version is 22.2.0
  • Andrey Uglev
    Andrey Uglev almost 9 years
    does't work, API 22, AppCompatActivity, theme Material.Light
  • Artem
    Artem almost 9 years
    @AndreyUglev try to use Theme.AppCompat.Light instead.
  • Aron Lorincz
    Aron Lorincz almost 9 years
    @hunyadym I can confirm it works just fine! The answer should be edited!
  • Amio.io
    Amio.io almost 9 years
    Hi, it changes the desired color BUT removes the shadows.
  • Amio.io
    Amio.io almost 9 years
    Hi, it changes the desired color BUT removes the shadows.
  • Amio.io
    Amio.io over 8 years
    Hi again, I can see. It's proper behaviour now.
  • Amio.io
    Amio.io over 8 years
    Hi again, I can see. It's proper behaviour now.
  • mohlendo
    mohlendo over 8 years
    That's the best way to do it, now! You can also use the style on individual buttons with <Button android:theme="@style/BrandButtonStyle"/>. It has to be the theme attribute and not the style attribute.
  • Bresiu
    Bresiu over 8 years
    @Muhammad Alfaifi Do you have sample project or gist for this? I've created clean project and it does not work: github.com/Bresiu/ThemeTest. In Lolipop button colors with accent color and on API v16, button remains gray: i.imgur.com/EVwkpk2.png
  • Kamil Nękanowicz
    Kamil Nękanowicz over 8 years
    It helps only partially, setEnabled(false) no effect
  • Kamil Nękanowicz
    Kamil Nękanowicz over 8 years
    It helps only partially, setEnabled(false) no effect
  • Ray Hunter
    Ray Hunter over 8 years
    This only worked fro me when specifying the attribute android:theme. I could not get the attribute style to work for me with the button tag.
  • Pratik Butani
    Pratik Butani over 8 years
    compile 'com.android.support:appcompat-v7:23.1.0' You must have to add.
  • Patrick
    Patrick over 8 years
    Using v23.1 lib, this did not work correctly, it always showed the accent color as bg, not the one defined, when trying to set it to the whole theme
  • Rémy DAVID
    Rémy DAVID over 8 years
    I confirm this does not work for the whole theme for API < 21, it works when assigning the theme to the button though
  • AhmedW
    AhmedW over 8 years
    Same issue here on , on v23.1 the disabled button is the same colour as the enabled button. very frustrating. anyone figure this out?
  • AhmedW
    AhmedW over 8 years
    This approach worked for me , v23.1.1 code.google.com/p/android/issues/detail?id=176472
  • Apostrofix
    Apostrofix over 8 years
    but the question is how to have material buttons with different colors, so that not all have the primary color of the theme.
  • Apostrofix
    Apostrofix over 8 years
    This solution works fine, but the problem is when you need more buttons with different colors, then you have to create ripples and shapes for each button, which is quite annoying..
  • jturolla
    jturolla about 8 years
    This is the simplest solution I think, this worked fine for android 4.2.2, 5.0 and others.
  • Sagar Nayak
    Sagar Nayak about 8 years
    what are other attributes i can add into styles .
  • Semanticer
    Semanticer about 8 years
    From AppCompatButton javadoc: "This will automatically be used when you use {@link android.widget.Button} in your * layouts. You should only need to manually use this class when writing custom views." So you dont have to manualy specify <android.support.v7.widget.AppCompatButton /> just use <Button android:theme="@style/BrandButtonStyle"/>
  • Muhammad Alfaifi
    Muhammad Alfaifi about 8 years
    @Semanticer thanks for noticing and you are right, old answer forgot to edit that :)
  • Micro
    Micro about 8 years
    How can you do this in Java?
  • Micro
    Micro about 8 years
    Your Java code worked for me. And Muhammad Alfaifi answer helped set the text color of the button.
  • Micro
    Micro about 8 years
    This along with TouchBoarder's answer helped me set the button color and text color in Java using: ViewCompat.setBackgroundTintList(your_colored_button, ContextCompat.getColorStateList(getContext(),R.color.your_cu‌​stom_color));
  • ingyesid
    ingyesid about 8 years
    @MicroR check the answer above.
  • wrozwad
    wrozwad almost 8 years
    You almost found the 100% correct way how to use new @style/Widget.AppCompat.Button.Colored ;) - see this answer stackoverflow.com/a/35811157/19733911
  • Prashanth Debbadwar
    Prashanth Debbadwar almost 8 years
    I was using Activity instead of AppCompatActivity because of that button color didn't change.
  • vault
    vault over 7 years
    is there an easy way to programmatically set the tint (not background)?
  • sud007
    sud007 over 7 years
    Wonderful! Works on 4.4.4 as well! And selector state is also maintained quite well!
  • gMale
    gMale over 7 years
    Per the docs: This is automatically used when setting buttons in your layouts. You only need to specify AppCompatButton when creating custom views.
  • velval
    velval over 7 years
    This helped....I guess it is important to note to apply the button style as a theme instead of a style in the Button widget. I was applying it under style="@style/BlueImageButton" and it didn't work
  • Leon
    Leon about 7 years
    Interestingly the XML changes worked perfectly in a fragment but not in an Activity layout. The Activity buttons used colorAccent only. The programmatic version worked though.
  • d.aemon
    d.aemon almost 7 years
    Aren't app compat widgets used automatically when inflating XML layouts? I believe ImageView gets replaced with AppCompatImageView and it should be the same with all widgets, that have corresponding AppCompat version.
  • farukcankaya
    farukcankaya over 6 years
    Nope, If you don't target last target version, you need to define android.support.v7.widget.AppCompatImageView explicitly.
  • d.aemon
    d.aemon over 6 years
    What do you mean by last target version, @power?
  • Someone Somewhere
    Someone Somewhere over 6 years
    Error:(3732, 21) No resource found that matches the given name: attr 'textColor'. ... the subtle difference in the values/style and values-v21/style shows you what we're up against in the Android world.
  • Someone Somewhere
    Someone Somewhere over 6 years
    using this method I see the lifting (shadow) effect, but not the ripple ...
  • epic
    epic over 3 years
    If anyone looking for proof android are crazy, here it is! 3 hours and counting to change button color! Jesus Christ!