230

I am editing to make the question simpler, hoping that helps towards an accurate answer.

Say I have the following oval shape:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:angle="270"
           android:color="#FFFF0000"/>
    <stroke android:width="3dp"
            android:color="#FFAA0055"/>
</shape>

How do I set the color programmatically, from within an activity class?

Sky Kelsey
  • 19,192
  • 5
  • 36
  • 77
Cote Mounyo
  • 13,817
  • 23
  • 66
  • 87
  • What do you set this drawable to? – Vikram Jul 24 '13 at 01:03
  • The drawable is an `oval` and is the background of an ImageView. – Cote Mounyo Jul 24 '13 at 03:29
  • If this question as asked is too difficult, is there a way to draw multiple images onto a canvas and set the layered end product as the background of a view? – Cote Mounyo Jul 24 '13 at 21:01
  • You can achieve this by extending the `View` class, and using it as the `base` view in a layout that allows overlapping of widgets(`RelativeLayout`, `FrameLayout`). Inside this extended `View` class, you can `draw multiple images onto a canvas`. But, before doing that, take a look at this --> [Link](http://www.betaful.com/2012/01/programmatic-shapes-in-android/) (if you haven't already). – Vikram Jul 24 '13 at 22:08

19 Answers19

310

Note: Answer has been updated to cover the scenario where background is an instance of ColorDrawable. Thanks Tyler Pfaff, for pointing this out.

The drawable is an oval and is the background of an ImageView

Get the Drawable from imageView using getBackground():

Drawable background = imageView.getBackground();

Check against usual suspects:

if (background instanceof ShapeDrawable) {
    // cast to 'ShapeDrawable'
    ShapeDrawable shapeDrawable = (ShapeDrawable) background;
    shapeDrawable.getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    // cast to 'GradientDrawable'
    GradientDrawable gradientDrawable = (GradientDrawable) background;
    gradientDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    // alpha value may need to be set again after this call
    ColorDrawable colorDrawable = (ColorDrawable) background;
    colorDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

Compact version:

Drawable background = imageView.getBackground();
if (background instanceof ShapeDrawable) {
    ((ShapeDrawable)background).getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    ((GradientDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    ((ColorDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

Note that null-checking is not required.

However, you should use mutate() on the drawables before modifying them if they are used elsewhere. (By default, drawables loaded from XML share the same state.)

Community
  • 1
  • 1
Vikram
  • 51,313
  • 11
  • 93
  • 122
  • 4
    Thanks for answering. (+1). My code is experiencing other bugs so it's difficult to test. But still this might set the `solid` portion of the shape. How about the `stroke` portion? – Cote Mounyo Jul 24 '13 at 05:03
  • 1
    @TiGer You should add `@username` in your comment to make sure a notification is sent to the user. By the way, you will need to subclass `ShapeDrawable` in order to set the stroke portion. More info here: [Link](http://stackoverflow.com/a/3663956). Look at the comment as it mentions a problem with the accepted answer. – Vikram Oct 19 '13 at 01:33
  • 3
    android.graphics.drawable.GradientDrawable cannot be cast to android.graphics.drawable.ShapeDrawable The cast fails on me – John Mar 27 '14 at 19:35
  • 3
    @John If your `ImageView's` background is set to a `GradientDrawable`, `getBackground()` will not return a `ShapeDrawable`. Instead, use the `GradientDrawable` that is returned: `GradientDrawable gradientDrawable = (GradientDrawable)imageView.getBackground();`.... `gradientDrawable.setColors(new int[] { color1, color2 });`. – Vikram May 08 '14 at 19:51
  • @Vikram could you help with a similar question here: http://stackoverflow.com/questions/35398658/change-the-shape-color-of-a-imagebutton –  Feb 15 '16 at 12:19
  • @vikram what if it's a ColorDrawable? – Tyler Pfaff Feb 20 '16 at 02:46
  • @TylerPfaff I have updated the answer to cover the case of `ColorDrawable`. Thank you for pointing this out. – Vikram Mar 05 '16 at 19:31
  • Thanks @Vikram. I'm getting some really really weird side effects from this method. Random other views in my app are taking on the colors of different views that have had their background set with this method. getPaint() looks suspicious to me in the ShapeDrawable case. Is anyone else having this problem? Perhaps setting the paint color affects more than just the object you are currently working with? – Tyler Pfaff Mar 07 '16 at 20:03
  • 1
    @Vikram I figured out my issue described above. You must call mutate() to avoid coloring any view that shares a drawable with the one you are coloring. :) – Tyler Pfaff Mar 10 '16 at 00:31
  • Can someone explain me why the explicit casts? – Joaquin Iurchuk Jun 01 '16 at 18:26
  • 1
    @joaquin `getPaint()` is only accessible on a `ShapeDrawable`, not `Drawable` - hence the explicit cast. `setColor(int)` is not available for `Drawable` or any common parent of `GradientDrawable` & `ColorDrawable` - hence the explicit casts. – Vikram Aug 19 '16 at 19:41
  • "However, you should use mutate() on the drawables before modifying them if they are used elsewhere. (By default, drawables loaded from XML share the same state.)" Any ideas how can I use mutate() if that is the case? Thanks! – Neon Warge Nov 08 '16 at 16:37
  • Very thorough and helpful answer! I would never have guessed my `` resource was actually a GradientDrawable instead of a ShapeDrawable. Your laying out the options and how to tell which it was saved me a lot of time. – LarsH Dec 01 '16 at 22:41
  • I found that even though my background drawable was a in the XML, I got both GradientDrawable and ColorDrawable back in the code. So had to handle all of them as in the example. – Dave Hubbard Sep 30 '19 at 15:35
78

A simpler solution nowadays would be to use your shape as a background and then programmatically change its color via:

view.background.setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP)

See PorterDuff.Mode for the available options.

UPDATE (API 29):

The above method is deprecated since API 29 and replaced by the following:

view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP)

See BlendMode for the available options.

Georgios
  • 4,764
  • 35
  • 48
  • 7
    Correct one is: `view.getBackground().setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP);` Since there may be a border on the background or rounded courners. – Berkay Turancı Apr 14 '19 at 10:07
  • 1
    Nice one @BerkayTurancı my shape did have rounded corners. I could omit the `getBackground()` call. My imageview.src contained a shape and I used: `imageIndicator.setColorFilter(toggleColor, PorterDuff.Mode.SRC_ATOP);` where `toggleColor` is just an int that previously stored the result from getColor() – Someone Somewhere Apr 24 '19 at 19:57
  • 2
    Having `PorterDuff.Mode` for `BlendModeColorFilter` won't compile as it requires `BlendMode`. So for API 29 it should be `view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP)`. – Onik Jun 01 '20 at 10:56
  • Good catch @Onik. I've updated the answer accordingly. Thank you! – Georgios Jun 01 '20 at 22:00
  • @Onik but BlendMode.SRC_ATOP does not preserve stroke ! – Shikhar Jun 29 '23 at 02:53
51

Do like this:

    ImageView imgIcon = findViewById(R.id.imgIcon);
    GradientDrawable backgroundGradient = (GradientDrawable)imgIcon.getBackground();
    backgroundGradient.setColor(getResources().getColor(R.color.yellow));
Leonly91
  • 613
  • 5
  • 6
26

This question was answered a while back, but it can modernized by rewriting as a kotlin extension function.

fun Drawable.overrideColor(@ColorInt colorInt: Int) {
    when (this) {
        is GradientDrawable -> setColor(colorInt)
        is ShapeDrawable -> paint.color = colorInt
        is ColorDrawable -> color = colorInt
    }
}
Carlos Paulino
  • 736
  • 6
  • 9
16

Try this:

 public void setGradientColors(int bottomColor, int topColor) {
 GradientDrawable gradient = new GradientDrawable(Orientation.BOTTOM_TOP, new int[]  
 {bottomColor, topColor});
 gradient.setShape(GradientDrawable.RECTANGLE);
 gradient.setCornerRadius(10.f);
 this.setBackgroundDrawable(gradient);
 }

for more detail check this link this

hope help.

androidqq6
  • 1,526
  • 2
  • 22
  • 47
15

hope this will help someone with the same issue

GradientDrawable gd = (GradientDrawable) YourImageView.getBackground();
//To shange the solid color
gd.setColor(yourColor)

//To change the stroke color
int width_px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, youStrokeWidth, getResources().getDisplayMetrics());
gd.setStroke(width_px, yourColor);
medhdj
  • 1,168
  • 10
  • 17
  • 1
    Initially I couldn't get this to work, I figured out **yourColor** must be provided like this: `gd.setStroke(width_px, Color.parseColor("#FF5722"));` – pwnsauce Oct 13 '15 at 17:42
12

Expanding on Vikram's answer, if you are coloring dynamic views, like recycler view items, etc.... Then you probably want to call mutate() before you set the color. If you don't do this, any views that have a common drawable (i.e a background) will also have their drawable changed/colored.

public static void setBackgroundColorAndRetainShape(final int color, final Drawable background) {

    if (background instanceof ShapeDrawable) {
        ((ShapeDrawable) background.mutate()).getPaint().setColor(color);
    } else if (background instanceof GradientDrawable) {
        ((GradientDrawable) background.mutate()).setColor(color);
    } else if (background instanceof ColorDrawable) {
        ((ColorDrawable) background.mutate()).setColor(color);
    }else{
        Log.w(TAG,"Not a valid background type");
    }

}
Community
  • 1
  • 1
Tyler Pfaff
  • 4,900
  • 9
  • 47
  • 62
  • 3
    needs and extra check and parameter: `if (background instanceof LayerDrawable) { background = ((LayerDrawable) background.mutate()).getDrawable(indexIfLayerDrawable); } if (background instanceof ShapeDrawable)[...]` to deal with backgrounds layouts that use ` – Johny Jan 15 '18 at 02:20
7

this is the solution that works for me...wrote it in another question as well: How to change shape color dynamically?

//get the image button by id
ImageButton myImg = (ImageButton) findViewById(R.id.some_id);

//get drawable from image button
GradientDrawable drawable = (GradientDrawable) myImg.getDrawable();

//set color as integer
//can use Color.parseColor(color) if color is a string
drawable.setColor(color)
Community
  • 1
  • 1
7

Nothing work for me but when i set tint color it works on Shape Drawable

 Drawable background = imageView.getBackground();
 background.setTint(getRandomColor())

require android 5.0 API 21

Sumit
  • 1,022
  • 13
  • 19
3

My Kotlin extension function version based on answers above with Compat:

fun Drawable.overrideColor_Ext(context: Context, colorInt: Int) {
    val muted = this.mutate()
    when (muted) {
        is GradientDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        is ShapeDrawable -> muted.paint.setColor(ContextCompat.getColor(context, colorInt))
        is ColorDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        else -> Log.d("Tag", "Not a valid background type")
    }
}
Sattar Hummatli
  • 1,360
  • 1
  • 15
  • 26
2

The simple way to fill the shape with the Radius is:

(view.getBackground()).setColorFilter(Color.parseColor("#FFDE03"), PorterDuff.Mode.SRC_IN);
SANAT
  • 8,489
  • 55
  • 66
1

May be I am too late.But if you are using Kotlin. There is way like this

var gd = layoutMain.background as GradientDrawable

 //gd.setCornerRadius(10)
  gd.setColor(ContextCompat.getColor(ctx , R.color.lightblue))
  gd.setStroke(1, ContextCompat.getColor(ctx , R.color.colorPrimary)) // (Strokewidth,colorId)

Enjoy....

Zealous System
  • 2,080
  • 11
  • 22
1

This might help

1.Set the shape color initially to transparent

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
   <solid android:angle="270"
       android:color="@android:color/transparent"/>
   <stroke android:width="3dp"
        android:color="#FFAA0055"/>
</shape>
  1. Set the shape as a background to the view

  2. Set your preferred color as follows:

    Drawable bg = view.getBackground();
    bg.setColorFilter(Color.parseColor("#Color"), PorterDuff.Mode.ADD);
    
afhamu
  • 930
  • 11
  • 17
1

I needed to do this in my adapter but the solutions above were either not working or required >= android version 10. The code below worked for me!

val drawable = DrawableCompat.wrap(holder.courseName.background)
DrawableCompat.setTint(drawable, Color.parseColor("#4a1f60"))

EDIT!!: OR if your color is an Int and not a Hex

val myDrawable = DrawableCompat.wrap(holder.courseName.background)
DrawableCompat.setTint(myDrawable, ContextCompat.getColor(context, newColor))

ADDED INFO!!: Another way to do this is to get the drawable, change its tint first, then set it to the background of your view.

val tintDrawable = ContextCompat.getDrawable(context, R.drawable.my_shape)!!.mutate()
DrawableCompat.setTint(tintDrawable, ContextCompat.getColor(context, newColor))
view.background = tintDrawable
0

For anyone using C# Xamarin, here is a method based on Vikram's snippet:

private void SetDrawableColor(Drawable drawable, Android.Graphics.Color color)
{
    switch (drawable)
    {
        case ShapeDrawable sd:
            sd.Paint.Color = color;
            break;
        case GradientDrawable gd:
            gd.SetColor(color);
            break;
        case ColorDrawable cd:
            cd.Color = color;
            break;
    }
}
Ryan
  • 1,670
  • 18
  • 25
0

The Best way to change Solid color of custom drawable is For Kotlin.

 (findViewById<TextView>(R.id.testing1).getBackground()).setColorFilter(Color.parseColor("#FFDE03"), PorterDuff.Mode.SRC_IN); 
Asif Ashraf
  • 13
  • 1
  • 7
0

We can create this kotlin function.

fun View.updateViewBGSolidColor(colorString: String) {
    when (val background: Drawable = this.background) {
        is ShapeDrawable -> {
            background.paint.color = Color.parseColor(colorString)
        }
        is GradientDrawable -> {
            background.setColor(Color.parseColor(colorString))
        }
        is ColorDrawable -> {
            background.color = Color.parseColor(colorString)
        }
    }
}

And use it like the below:

yourTextView.updateViewBGSolidColor("#FFFFFF")
Rumit Patel
  • 8,830
  • 18
  • 51
  • 70
0
GradientDrawable gd = new GradientDrawable(
        GradientDrawable.Orientation.TOP_BOTTOM,
        new int[] {0xFF616261,0xFF131313});
gd.setCornerRadius(0f);

layout.setBackgroundDrawable(gd);
0

For 2023 with compileSdkVersion 33 the most voted answer at this moment worked in some way. My shape it's a simple rounded border for a complex view.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <stroke
        android:width="2dp"
        android:color="@color/white"/>
    <corners
        android:bottomRightRadius="10dp"
        android:topLeftRadius="10dp"
        android:topRightRadius="10dp"
        android:bottomLeftRadius="10dp"/>
</shape>

The shape is set as background in a view with name VScroll. I need the color of the shape to be the same as the themed view so I adapted the accepted answer and set the color in the Fragment:

val lineBackground = binding.vScroll.background as GradientDrawable
lineBackground.setStroke(5,Color.parseColor(line.color))

Where line.color is an hexadecimal string value like #FFFFFF. The int value for the stroke width is set in pixels, so it has to be converted to dp for a correct size.

Rafa J
  • 61
  • 9