19

Is there any way we can tint the Drawable used in the TextView? DrawableTint works only on API level 23 and above.

Currently I'm using a Vertical Linear Layout to achieve my requirement.

<LinearLayout style="@style/ChoiceIllustratorIconTextContainerStyle">

  <ImageView
    style="@style/ChoiceIllustratorImageStyle"
    android:contentDescription="@string/cd_university"
    android:src="@drawable/ic_account_balance_white_24dp" />

  <TextView
    style="@style/ChoiceIllustratorTextStyle"
    android:text="@string/ci_text_university" />

</LinearLayout>

And it looks like,enter image description here

Android studio is suggesting me to use Compound Drawble with TextView to achieve this. And I'm able to achieve it, but I cannot find a way to Tint the drawable.

<TextView
   style="@style/ChoiceIllustratorTextStyle"
   android:drawablePadding="4dp"
   android:drawableTop="@drawable/ic_account_balance_white_24dp"
   android:text="@string/ci_text_university" />
Nishanth Sreedhara
  • 1,266
  • 1
  • 19
  • 34

4 Answers4

35

AndroidX appcompat library supports tinting in TextView since version 1.1.0-alpha03 [ref].

Add dependencies to appcompat library

dependencies {
  implementation "androidx.appcompat:appcompat:1.1.0"
}

Then drawable in TextView can be tinted from XML like this

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:drawableStartCompat="@drawable/ic_plus"
  app:drawableTint="@color/red" />

Don't forget to include

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

and to extend your Activity from AppCompatActivity.

shinwan
  • 617
  • 6
  • 11
24

The programmatic way to do this is

       Drawable[] drawables = textView.getCompoundDrawables();
       if (drawables[0] != null) {  // left drawable
           drawables[0].setColorFilter(color, Mode.MULTIPLY);
       }

This works for all API levels.

This is your best option for pre-Marshmallow devices.

kris larson
  • 30,387
  • 5
  • 62
  • 74
  • 8
    And if you need to use a different color on another screen or for different view states, I believe you would want to `mutate()` the drawable before call setColorFilter. – androidguy May 08 '17 at 22:41
  • 2
    PorterDuff.Mode.MULTIPLY does not work for me. Instead, PorterDuff.Mode.SRC_ATOP works for me. Does PorterDuff.Mode.MULTIPLY for you? – Francis Bacon Nov 01 '17 at 12:07
  • SRC_ATOP was what the OP settled on, so it's not surprising that it works for you too. Can't remember exactly, but I think I might have been using a drawable with all-white pixels, so that MULTIPLY and SRC_ATOP would look pretty much the same. The OP's method of using the compatibility classes is a better option as well. – kris larson Nov 01 '17 at 13:02
  • 4
    for reference : drawable[0] left, drawable[1] top, drawable[2] right, drawable[3] bottom . Thanks this works. – ralphgabb Jul 11 '18 at 01:50
  • 3
    Thanks this worked. Also note to use getCompoundDrawablesRelative for Start/Top/End/Bottom – Jerry Sha Feb 18 '19 at 00:59
15

This answer is based on @kris larson suggestion.

I use the following methods and it is working fine on all devices.

setTintedCompoundDrawable a custom method that takes the TextView to which you would want to set the compound drawable, a drawable res id & and the res id of your color choice.

private void setTintedCompoundDrawable(TextView textView, int drawableRes, int tintRes) {
    textView.setCompoundDrawablesWithIntrinsicBounds(
            null,  // Left
            Utils.tintDrawable(ContextCompat.getDrawable(getContext(), drawableRes),
                    ContextCompat.getColor(getContext(), tintRes)), // Top
            null, // Right
            null); //Bottom
    // if you need any space between the icon and text.
    textView.setCompoundDrawablePadding(12);
}

Tint tintDrawable method goes like this:

public static Drawable tintDrawable(Drawable drawable, int tint) {
    drawable = DrawableCompat.wrap(drawable);
    DrawableCompat.setTint(drawable, tint);
    DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_ATOP);

    return drawable;
}
Nishanth Sreedhara
  • 1,266
  • 1
  • 19
  • 34
  • 1
    call mutate() on the drawable loaded from resource id to get a separate drawable if you use the drawable resource somewhere else in the app with a different tint. – farid_z Aug 09 '19 at 19:58
8

You could use TextViewCompat class for this case:

TextViewCompat.setCompoundDrawableTintList(TextView, ColorStateList)
Ali Rezaiyan
  • 3,011
  • 3
  • 16
  • 27
  • this won't work. This will always take effect when running on API v24 or newer – You Qi Nov 26 '21 at 02:48
  • @YouQi Compat class is used, it means supports all the versions – Ali Rezaiyan Nov 26 '21 at 22:10
  • You may want to read the doc of the function: `This will always take effect when running on API v24 or newer. When running on platforms previous to API v24, it will only take effect if textView implements the TintableCompoundDrawablesView interface.` ```. given the context of the question is below 24, your answer is not good enough as you didn't address the full caveat – You Qi Nov 28 '21 at 19:28
  • Works great with `MaterialTextView` on API 21 – Artem Mostyaev Jan 11 '22 at 13:46