87

I have a Button in my Activity, and I'd like it to have my theme's accent color. Instead of making my own drawables like we had to do pre-Lollipop, naturally I'd like to use the new backgroundTint attribute.

<Button
    android:id="@+id/btnAddCode"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:backgroundTint="@color/accent"
    android:text="@string/addressInfo_edit_addCode" />

Unfortunately it has no effect, the button stays gray.

I tried different values for backgroundTintMode, which didn't change anything.

I also tried doing it programmatically in my Activity, which didn't change anything.

addCodeView.findViewById(R.id.btnAddCode).setBackgroundTintList(
     getResources().getColorStateList(R.color.accent));

Why is my tint ignored?

EDIT: Just to clarify, I am indeed testing on a Lollipop device. Other widgets (e.g. EditText) are correctly and automatically tinted.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
BoD
  • 10,838
  • 6
  • 63
  • 59

16 Answers16

118

The bad news

Like BoD says, it's meaningless to tint a Button's background in Lollipop 5.0 (API level 21).

The good news

Lollipop 5.1 (API level 22) seems to have fixed this by changing btn_mtrl_default_shape.xml (among other files): https://android.googlesource.com/platform/frameworks/base/+/6dfa60f33ca6018959ebff1efde82db7d2aed1e3%5E!/#F0

The great news

The new support library (version 22.1+) adds backward-compatible tinting support to lots of components, including AppCompatButton!

Unfortunately, the android:backgroundTint property still doesn't work (maybe I'm doing something wrong) -- so you have to set the ColorStateList in code, using setSupportBackgroundTintList(). It'd be really nice to see android:backgroundTint supported in the future. Update: Marcio Granzotto commented that app:backgroundTint works on AppCompatButton! Note that it's app:, not android:, because it's in the app/library.

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <AppCompatButton
        android:id="@+id/mybutton"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Testing, testing"
        app:backgroundTint="#ff00ff"/>

</LinearLayout>

Your activity will automatically inflate an AppCompatButton instead of the normal Button if you let it inherit from AppCompatActivity.

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton v = (AppCompatButton) findViewById(R.id.mybutton);
        ColorStateList csl = new ColorStateList(new int[][]{new int[0]}, new int[]{0xffffcc00});
        v.setSupportBackgroundTintList(csl);
    }
}

You should of course get the ColorStateList from a color resource, but I was lazy, so...

Oh, and don't forget to base your app theme on one of the Theme.AppCompat themes, or the compat views will be very, very sad... ;)

This worked on both 2.3.7 (Gingerbread MR1) and 5.0 (Lollipop 'Classic').

Dmytro Rostopira
  • 10,588
  • 4
  • 64
  • 86
Snild Dolkow
  • 6,669
  • 3
  • 20
  • 32
  • 1
    Just added some more info -- most importantly, it's now possible to achieve button tinting with the new version (22.1) of the support library that was released the other day! – Snild Dolkow Apr 24 '15 at 18:29
  • 8
    You can use `colorButtonNormal` with AppCompat 22.1.1 (setting the theme only for the buttons), it works for me on 4.4.4 and 5.1. – hunyadym Apr 27 '15 at 18:26
  • Indeed, and BoD already informed us thusly below. One downside to that method is that setting android:theme on a button makes its context a ContextThemeWrapper (wrapping the activity) instead of just the plain Activity. This means that, for example, the android:onClick attribute doesn't work as expected (it fails to find your method in the wrapper class). – Snild Dolkow Apr 27 '15 at 19:27
  • 2
    Any idea how to set the same thing for TextView ? – android developer Jul 02 '15 at 09:39
  • 1
    I think this should be marked as the correct answer. – MiguelCatalan Jul 27 '15 at 16:05
  • Using setSupportBackgroundTintList I can't achieve a colored Ripple. What state do I have to override in the ColorStateList? – David Corsalini Aug 28 '15 at 15:25
  • Just use @color/secondary and it works from styles – Warpzit Oct 29 '15 at 11:51
  • 9
    Instead of `new ColorStateList(new int[][]{new int[0]}, new int[]{0xffffcc00});` you can write more concisely `ColorStateList.valueOf(0xffffcc00);`. – Ashkan Sarlak Nov 03 '15 at 09:16
  • 5
    You can use android.support.v7.widget.AppCompatButton with app:backgroundTint on the xml – Marcio Granzotto Jan 21 '16 at 13:41
  • The error is still present on devices running Android Lolipop 5.0.x using support library 23.1.1 (current version at time of writing). app:backgroundTint will be ignored for AppCompatButton. No problems on devices with API-Level <= 19 or >= 22. – Chris Feb 19 '16 at 15:37
30

It seems that tinting a ripple drawable is meaningless (and the default background of a button is a ripple drawable).

In fact, after looking at the platform's default button drawable, I found the "correct" way to do this:. You have to define this in your theme:

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

(Of course this is only for level 21+.)

Warning: since this is defined in a theme, this will use the given color for all the buttons (at least all of the buttons in activities using that theme.)

As a bonus, you can also change the ripple color by defining this:

    <item name="android:colorControlHighlight">@color/accent_ripple</item>
BoD
  • 10,838
  • 6
  • 63
  • 59
  • `colorControlHighlight` would change a lot of other widgets though. Glad you've worked it out. – natario Jan 02 '15 at 12:46
  • 5
    You can apply this to an individual view by defining an overlay theme (e.g. no parent theme and only one attribute defined) and using the android:theme attribute. – alanv Jan 04 '15 at 20:36
  • 1
    Then how it is fixed for API 22 and up? Also this "bug" occours only in some devices of API 21 (including nexus 5, galaxy s3) – Vedant Agarwala Nov 26 '15 at 06:39
22

To resolve issues related to tinting on Android 5.0.x I use something like this:

public static void setButtonTint(Button button, ColorStateList tint) {
    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP && button instanceof AppCompatButton) {
        ((AppCompatButton) button).setSupportBackgroundTintList(tint);
    } else {
        ViewCompat.setBackgroundTintList(button, tint);
    }
}

It uses the support method only for API 21 and the ViewCompat one for all other cases.

GUG
  • 221
  • 2
  • 3
  • @Marco Use: `if (view instanceof TintableBackgroundView) { ((TintableBackgroundView) view).setSupportBackgroundTintList(tint); } else { ViewCompat.setBackgroundTintList(view, tint); }` – chessdork Sep 01 '16 at 14:49
  • This doesnt work with an ImageButton on pre lollipop devices. – Etienne Lawlor Mar 17 '17 at 21:33
  • I get a lint warning when using AppCompatButton.setSupportBackgroundTintList() AppCompatButton.setSupportBackgroundTintList can only be called from within the same library group (groupId=com.android.support) – starkej2 Apr 19 '17 at 18:12
  • It worked for me but I had to invert the conditions. – Rodrigo Venancio Jan 25 '18 at 23:00
  • "can only be called from within the same library group (groupId=com.android.support" why this warning? – Ferran Negre Oct 03 '18 at 11:04
22

I usually do it dynamically by using PorterDuff:

mbutton = (Button) findViewById(R.id.mybutton);
mbutton.getBackground().setColorFilter(anycolor, PorterDuff.Mode.MULTIPLY);

You can check different blending modes here and nice examples here.

Michael Peterson
  • 10,383
  • 3
  • 54
  • 51
Carlos Borau
  • 1,433
  • 1
  • 24
  • 34
20

Just use app:backgroundTint instead of android:backgroundTint, the tint will take effect below Lollipop. The reason is AppCompatActivity use AppCompatViewInflater to auto change Button or TextView to AppCompatButton or AppCompatTextView, then app:backgroundTint take effect.

enter image description here

In my project I used it, it worked.

drakeet
  • 2,685
  • 1
  • 23
  • 30
18

Tested on API 19 through API 27

<?xml version="1.0" encoding="utf-8"?>
  <android.support.v7.widget.AppCompatButton 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    style="@style/Widget.AppCompat.Button.Colored"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/retry"
    android:textColor="@android:color/white"
    app:backgroundTint="@android:color/holo_red_dark" />

produces output as -

enter image description here

Sahitya Pasnoor
  • 597
  • 5
  • 11
  • Marking this as the accepted answer because after several years, I believe it's now currently the official (and best) way to do this. (Minor detail: I'm pretty sure in most cases you can simply use Button instead of AppCompatButton and it will still work). – BoD Apr 04 '18 at 20:21
9

I think you need to have android:background set to make android:backgroundTint work.

To be more accurate, my guess is that you can't backgroundTint the default button background from Material themes, which is defined as a RippleDrawable.

natario
  • 24,954
  • 17
  • 88
  • 158
  • 1
    I think you are correct. In fact I found the "correct" way to do what I want (see my answer). – BoD Jan 02 '15 at 12:36
5

Just use app:backgroundTint instead of android:backgroundTint

Smirnov Sergey
  • 409
  • 1
  • 8
  • 12
3

Similar issue was reported on google https://code.google.com/p/android/issues/detail?id=201873

But after release of Android Support Library, revision 23.2.1 (March 2016) This bug is solved.

Issue : FloatingActionButton.setBackgroundTintList(@Nullable ColorStateList tint) no longer changes background color

update Support Library to Android Support Library to 23.2.1

Use design support library(23.2.1) and appcompatwidgets as below

Material Design for Pre-Lollipop Devices :

AppCompat (aka ActionBarCompat) started out as a backport of the Android 4.0 ActionBar API for devices running on Gingerbread, providing a common API layer on top of the backported implementation and the framework implementation. AppCompat v21 delivers an API and feature-set that is up-to-date with Android 5.0


Android Support Library 22.1 :

The ability to tint widgets automatically when using AppCompat is incredibly helpful in keeping strong branding and consistency throughout your app. This is done automatically when inflating layouts - replacing Button with AppCompatButton, TextView with AppCompatTextView, etc. to ensure that each could support tinting. In this release, those tint aware widgets are now publicly available, allowing you to keep tinting support even if you need to subclass one of the supported widgets.

Amit Vaghela
  • 22,772
  • 22
  • 86
  • 142
2

If we look into the source code of Support Library, we see that it tints normally it's known buttons, but if we change the shape of our button (I have round button) tint doesn't work ok in api<=21. We can also see that TintManager became public class (appcompat-v7:23.1.1), so we can take ColorStateList from default button shape (which is tinted ok in 5.0) for current theme (so we don't have to create the array of colors):

    Context c = ...; // activity
    AppCompatButton ab = ...; // your button
    // works ok in 22+:
    if (Build.VERSION.SDK_INT <= 21) {
        // default appcompat button, that is tinted ok with current theme colors "abc_btn_default_mtrl_shape":
        // ColorStateList tint = TintManager.get(c).getTintList(R.drawable.abc_btn_default_mtrl_shape);
        // Appcompat 23.2 change:
        ColorStateList tint = AppCompatDrawableManager.get().getTintList(c, R.drawable.abc_btn_default_mtrl_shape);
        ab.setSupportBackgroundTintList(tint);
        }
Pavel Biryukov
  • 1,076
  • 12
  • 19
0

Because attribute backgroundTint is only used in API level 21 and higher

fedache
  • 131
  • 2
  • 5
  • I realize that, and I am indeed testing on a Lollipop device. I will update the question to make this clear. – BoD Jan 02 '15 at 00:38
0

Be aware recyclerview most updated lib can cause this bug as well.

This command

  sendBtnView.setBackgroundTintList(colorState)

worked perfectly in the past but stop working for me. after research it turns out the cause is the lib that was addeded to gradle dependencies:

  compile 'com.android.support:recyclerview-v7:+'

So I tried to change it to 23.02.1 as it was recommended here in Amit Vaghela post. I changed to

  compile  'com.android.support:recyclerview-v7:23.02.1'

But gradle error said recyclerview lib does not have this version (23.02.1) (gradle could not find it in Jcenter raw.github or repo).

Then, becaouse I knew setBackgroundTintList command used to worke well in the past with version 22.02.0' in all the other libs I have in gradle dependencies. so I change it to:

compile   'com.android.support:recyclerview-v7:22.02.0'

And now it works again.

Udi Reshef
  • 1,083
  • 1
  • 11
  • 14
0

I am not sure if this is recommended but you can try this:

Drawable newDrawable = mBtnAction.getBackground();  // obtain Bg drawable from the button as a new drawable
DrawableCompat.setTint(newDrawable, mActivity.getHomeTobBarBGColor());  //set it's tint
mBtnAction.setBackground(newDrawable);  //apply back to button

In a general sense it works. Tried ViewCompat but it doesn't seem to work properly.

sud007
  • 5,824
  • 4
  • 56
  • 63
0

you can use backgroundTint <android.support.design.button.MaterialButton with "com.android.support:design:28.0.0-rc01" version

Zafer Celaloglu
  • 1,438
  • 1
  • 17
  • 28
0

If you are using androidx then adding both prefixed and not prefixed version resolved issue on android 5.1:

<style name="Button_Primary">
    <item name="android:layout_height">wrap_content</item>
    <item name="android:layout_width">wrap_content</item>
    <item name="android:backgroundTint">@color/button_primary_selector</item>
    <item name="backgroundTint">@color/button_primary_selector</item><!--needed for android 5-->
</style>

button_primary selector is in color folder with following content:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:local="http://schemas.android.com/apk/res-auto">
    <item android:state_enabled="true" android:color="@android:color/holo_blue_dark" />
    <item android:state_enabled="false" android:color="@android:color/darker_gray" />
</selector>

and apply it on regular button on AppCompatActivity

<Button style="@style/Button_Primary"/>
Sly
  • 15,046
  • 12
  • 60
  • 89
0

If you are developing an app that your target sdk is higher than api 21 and your minSdkVersion is 21(Lollipop) instead of

android:backgroundTint="@color/accent"

you can simply say..

android:background="@color/accent"
StackOverflower
  • 369
  • 2
  • 12