I'm using an horizontal progress bar in my Android application, and I want to change its progress color (which is Yellow by default). How can I do it using code
(not XML)?
30 Answers
I'm sorry that it's not the answer, but what's driving the requirement setting it from code ?
And .setProgressDrawable
should work if it's defined correctly
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dip" />
<gradient
android:startColor="#ff9d9e9d"
android:centerColor="#ff5a5d5a"
android:centerY="0.75"
android:endColor="#ff747674"
android:angle="270"
/>
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="5dip" />
<gradient
android:startColor="#80ffd300"
android:centerColor="#80ffb600"
android:centerY="0.75"
android:endColor="#a0ffcb00"
android:angle="270"
/>
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners
android:radius="5dip" />
<gradient
android:startColor="@color/progress_start"
android:endColor="@color/progress_end"
android:angle="270"
/>
</shape>
</clip>
</item>
</layer-list>
For a horizontal ProgressBar, you can use a ColorFilter
, too, like this:
progressBar.getProgressDrawable().setColorFilter(
Color.RED, android.graphics.PorterDuff.Mode.SRC_IN);
Note: This modifies the appearance of all progress bars in your app. To only modify one specific progress bar, do this:
Drawable progressDrawable = progressBar.getProgressDrawable().mutate();
progressDrawable.setColorFilter(Color.RED, android.graphics.PorterDuff.Mode.SRC_IN);
progressBar.setProgressDrawable(progressDrawable);
If progressBar is indeterminate then use getIndeterminateDrawable()
instead of getProgressDrawable()
.
Since Lollipop (API 21) you can set a progress tint:
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
This is not programmatically but I think it could help a lot of people anyway.
I tried a lot and the most efficient way was to add this lines to my ProgressBar in the .xml File:
android:indeterminate="true"
android:indeterminateTintMode="src_atop"
android:indeterminateTint="@color/secondary"
So in the end this code did it for me:
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_marginTop="50dp"
android:layout_marginBottom="50dp"
android:visibility="visible"
android:indeterminate="true"
android:indeterminateTintMode="src_atop"
android:indeterminateTint="@color/secondary">
This solution works for API 21+
For my indeterminate progressbar (spinner) I just set a color filter on the drawable. Works great and just one line.
Example where setting color to red:
ProgressBar spinner = new android.widget.ProgressBar(
context,
null,
android.R.attr.progressBarStyle);
spinner.getIndeterminateDrawable().setColorFilter(0xFFFF0000, android.graphics.PorterDuff.Mode.MULTIPLY);
This is an old question, but using theme is not mentioned here. If your default theme is using AppCompat
, your ProgressBar
's color will be colorAccent
you have defined.
Changing colorAccent
will also change your ProgressBar
's color, but there changes also reflects at multiple places. So, if you want a different color just for a specific PregressBar
you can do that by applying theme to that ProgressBar
:
Extend your default theme and override
colorAccent
<style name="AppTheme.WhiteAccent"> <item name="colorAccent">@color/white</item> <!-- Whatever color you want--> </style>
And in
ProgressBar
add theandroid:theme
attribute:android:theme="@style/AppTheme.WhiteAccent"
So it will look something like this:
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10dp"
android:theme="@style/AppTheme.WhiteAccent" />
So you are just changing a colorAccent
for your particular ProgressBar
.
Note: Using style
will not work. You need to use android:theme
only.
You can find more use of theme here: https://plus.google.com/u/0/+AndroidDevelopers/posts/JXHKyhsWHAH
All API
if use all API just create the theme in style
style.xml
<resources>
//...
<style name="progressBarBlue" parent="@style/Theme.AppCompat">
<item name="colorAccent">@color/blue</item>
</style>
</resources>
and use in progress
<ProgressBar
...
android:theme="@style/progressBarBlue" />
API level 21 and higher
if used in API level 21 and higher just use this code:
<ProgressBar
//...
android:indeterminate="true"
android:indeterminateTintMode="src_atop"
android:indeterminateTint="@color/secondary"/>
This works for me. It also works for lower version too. Add this to your syles.xml
<style name="ProgressBarTheme" parent="ThemeOverlay.AppCompat.Light">
<item name="colorAccent">@color/colorPrimary</item>
</style>
And use it like this in xml
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/ProgressBarTheme"
/>
as per some of the suggestions, you CAN specify a shape and clipdrawable with a colour, then set it. I have this working programatically. This is how I do it..
First make sure you import the drawable library..
import android.graphics.drawable.*;
Then use the code similar to below;
ProgressBar pg = (ProgressBar)row.findViewById(R.id.progress);
final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
pgDrawable = new ShapeDrawable(new RoundRectShape(roundedCorners, null,null));
String MyColor = "#FF00FF";
pgDrawable.getPaint().setColor(Color.parseColor(MyColor));
ClipDrawable progress = new ClipDrawable(pgDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
pg.setProgressDrawable(progress);
pg.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.progress_horizontal));
pg.setProgress(45);
Nowadays in 2016 I found some pre-Lollipop devices don't honour the colorAccent
setting, so my final solution for all APIs is now the following:
// fixes pre-Lollipop progressBar indeterminateDrawable tinting
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Drawable wrapDrawable = DrawableCompat.wrap(mProgressBar.getIndeterminateDrawable());
DrawableCompat.setTint(wrapDrawable, ContextCompat.getColor(getContext(), android.R.color.holo_green_light));
mProgressBar.setIndeterminateDrawable(DrawableCompat.unwrap(wrapDrawable));
} else {
mProgressBar.getIndeterminateDrawable().setColorFilter(ContextCompat.getColor(getContext(), android.R.color.holo_green_light), PorterDuff.Mode.SRC_IN);
}
For bonus points, it doesn't use any deprecated code. Try it!
THis is what i did. Worked.
ProgressBar:
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="4"
android:indeterminateDrawable="@drawable/progressdrawable"
/>
progressdrawable.xml:
Here use gradient to change colour as you like. And android:toDegrees="X" increse the value of X and progressbar rotate fast. Decrease and it rotate slow.Customize according to your needs.
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="4000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360" >
<shape
android:innerRadius="20dp"
android:shape="ring"
android:thickness="4dp"
android:useLevel="false" >
<size
android:height="48dp"
android:width="48dp" />
<gradient
android:centerColor="#80ec7e2a"
android:centerY="0.5"
android:endColor="#ffec7e2a"
android:startColor="#00ec7e2a"
android:type="sweep"
android:useLevel="false" />
</shape>
</rotate>
Hit the same problem while working on modifying the look/feel of the default progress bar. Here is some more info that will hopefully help people :)
- The name of the xml file must only contain characters:
a-z0-9_.
(ie. no capitals!) - To reference your "drawable" it is
R.drawable.filename
- To override the default look, you use
myProgressBar.setProgressDrawable(...)
, however you need can't just refer to your custom layout asR.drawable.filename
, you need to retrieve it as aDrawable
:Resources res = getResources(); myProgressBar.setProgressDrawable(res.getDrawable(R.drawable.filename);
- You need to set style before setting progress/secondary progress/max (setting it afterwards for me resulted in an 'empty' progress bar)
You can try to change your Styles, Themes, or using android:indeterminateTint="@color/yourColor" anywhere you want, but there's just one way o doing that will work on any Android SKD version:
If you progress bar is not indeterminate, please use:
progressBar.getProgressDrawable().setColorFilter(ContextCompat.getColor(context, R.color.yourColor), PorterDuff.Mode.SRC_IN );
If you progress bar is indeterminate, please use:
progressBar.getIndeterminateDrawable().setColorFilter(ContextCompat.getColor(getContext(), R.color.yourColor), PorterDuff.Mode.SRC_IN );
It's sad that Android is such a mess!
There's probably one thing that hasn't been referred to in this answer:
If your theme is inheriting from Theme.AppCompat
, ProgressBar
will assume the color you defined as "colorAccent"
in your theme.
So, using..
<item name="colorAccent">@color/custom_color</item>
..will tint the color of the ProgressBar automagically to the @color/custom_color
.
Simplest Solution if you want to change the colour in the layout xml file, use the below code and use indeterminateTint property for your desired color.
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:indeterminate="true"
android:indeterminateTintMode="src_atop"
android:indeterminateTint="#ddbd4e"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
The most simple way of changing the foreground and background colour of a progress bar is
<ProgressBar
style="@android:style/Widget.ProgressBar.Horizontal"
android:id="@+id/pb_main"
android:layout_width="match_parent"
android:layout_height="8dp"
android:progress="30"
android:progressTint="#82e9de"
android:progressBackgroundTint="#82e9de"
/>
just add
android:progressTint="#82e9de" //for foreground colour
android:progressBackgroundTint="#82e9de" //for background colour
To change horizontal ProgressBar color (in kotlin):
fun tintHorizontalProgress(progress: ProgressBar, @ColorInt color: Int = ContextCompat.getColor(progress.context, R.color.colorPrimary)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
progress.progressTintList = ColorStateList.valueOf(color)
} else{
val layerDrawable = progress.progressDrawable as? LayerDrawable
val progressDrawable = layerDrawable?.findDrawableByLayerId(android.R.id.progress)
progressDrawable?.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
}
}
To change indeterminate ProgressBar color:
fun tintIndeterminateProgress(progress: ProgressBar, @ColorInt color: Int = ContextCompat.getColor(progress.context, R.color.colorPrimary)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
progress.indeterminateTintList = ColorStateList.valueOf(color)
} else {
(progress.indeterminateDrawable as? LayerDrawable)?.apply {
if (numberOfLayers >= 2) {
setId(0, android.R.id.progress)
setId(1, android.R.id.secondaryProgress)
val progressDrawable = findDrawableByLayerId(android.R.id.progress).mutate()
progressDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
}
}
}
}
And it finally normally tint pre-lollipop progressBars
One more little thing, the theme solution does work if you inherit a base theme, so for app compact your theme should be:
<style name="AppTheme.Custom" parent="@style/Theme.AppCompat">
<item name="colorAccent">@color/custom</item>
</style>
And then set this in the progress bar theme
<ProgressBar
android:id="@+id/progressCircle_progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:theme="@style/AppTheme.Custom"
android:indeterminate="true"/>
simply use:
PorterDuff.Mode mode = PorterDuff.Mode.SRC_IN;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
mode = PorterDuff.Mode.MULTIPLY;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
progressBar.setProgressBackgroundTintList(ColorStateList.valueOf(Color.RED));
} else {
Drawable progressDrawable;
progressDrawable = (progressBar.isIndeterminate() ? progressBar.getIndeterminateDrawable() : progressBar.getProgressDrawable()).mutate();
progressDrawable.setColorFilter(context.getResources().getColor(Color.RED), mode);
progressBar.setProgressDrawable(progressDrawable);
}
Horizontal progress bar custom material style :
To change color of background and progress of horizontal progress bar.
<style name="MyProgressBar" parent="@style/Widget.AppCompat.ProgressBar.Horizontal">
<item name="android:progressBackgroundTint">#69f0ae</item>
<item name="android:progressTint">#b71c1c</item>
<item name="android:minWidth">200dp</item>
</style>
Apply it to progress bar by setting style attribute, for custom material styles and custom progress bar check http://www.zoftino.com/android-progressbar-and-custom-progressbar-examples
Use the android.support.v4.graphics.drawable.DrawableCompat:
Drawable progressDrawable = progressBar.getIndeterminateDrawable();
if (progressDrawable != null) {
Drawable mutateDrawable = progressDrawable.mutate();
DrawableCompat.setTint(mutateDrawable, primaryColor);
progressBar.setProgressDrawable(mutateDrawable);
}
Posted to add info about PaulieG's answer, since ateiob asked me to explain something...
I can say that there is (or at least was, at the time of writing when I looked at that current version of Android source code) a bug/issue/optimisation in the ProgressBar
code that ignores an attempt to set the progress to a value it is already at.
- i.e. if progress = 45, and you try to set it to 45, the code will do nothing, and will not redraw the progress.
After calling ProgressBar.setProgressDrawable()
, your progress bar will be blank (because you changed the drawable part).
This means you need to set the progress, and redraw it. But if you just set the progress to a preserved value, it will do nothing.
You must set it to 0 first, then to the "old" value again, and the bar will redraw.
So to summarise:
- preserve the "old" progress value
- update the drawable / colour (makes bar blank)
- reset the progress to 0 (otherwise next line does nothing)
- reset the progress to the "old" value (fixes bar)
- invalidate
Below is a method I have that does this:
protected void onResume()
{
super.onResume();
progBar = (ProgressBar) findViewById(R.id.progress_base);
int oldProgress = progBar.getProgress();
// define new drawable/colour
final float[] roundedCorners = new float[]
{ 5, 5, 5, 5, 5, 5, 5, 5 };
ShapeDrawable shape = new ShapeDrawable(new RoundRectShape(
roundedCorners, null, null));
String MyColor = "#FF00FF";
shape.getPaint().setColor(Color.parseColor(MyColor));
ClipDrawable clip = new ClipDrawable(shape, Gravity.LEFT,
ClipDrawable.HORIZONTAL);
progBar.setProgressDrawable(clip);
progBar.setBackgroundDrawable(getResources().getDrawable(
android.R.drawable.progress_horizontal));
// work around: setProgress() ignores a change to the same value
progBar.setProgress(0);
progBar.setProgress(oldProgress);
progBar.invalidate();
}
As far as HappyEngineer's solution, I think it was a similar workaround, to manually set the "progress" offset. In either case, the above code should work for you.
For a horizontal style ProgressBar I use:
import android.widget.ProgressBar;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ClipDrawable;
import android.view.Gravity;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
public void setColours(ProgressBar progressBar,
int bgCol1, int bgCol2,
int fg1Col1, int fg1Col2, int value1,
int fg2Col1, int fg2Col2, int value2)
{
//If solid colours are required for an element, then set
//that elements Col1 param s the same as its Col2 param
//(eg fg1Col1 == fg1Col2).
//fgGradDirection and/or bgGradDirection could be parameters
//if you require other gradient directions eg LEFT_RIGHT.
GradientDrawable.Orientation fgGradDirection
= GradientDrawable.Orientation.TOP_BOTTOM;
GradientDrawable.Orientation bgGradDirection
= GradientDrawable.Orientation.TOP_BOTTOM;
//Background
GradientDrawable bgGradDrawable = new GradientDrawable(
bgGradDirection, new int[]{bgCol1, bgCol2});
bgGradDrawable.setShape(GradientDrawable.RECTANGLE);
bgGradDrawable.setCornerRadius(5);
ClipDrawable bgclip = new ClipDrawable(
bgGradDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
bgclip.setLevel(10000);
//SecondaryProgress
GradientDrawable fg2GradDrawable = new GradientDrawable(
fgGradDirection, new int[]{fg2Col1, fg2Col2});
fg2GradDrawable.setShape(GradientDrawable.RECTANGLE);
fg2GradDrawable.setCornerRadius(5);
ClipDrawable fg2clip = new ClipDrawable(
fg2GradDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
//Progress
GradientDrawable fg1GradDrawable = new GradientDrawable(
fgGradDirection, new int[]{fg1Col1, fg1Col2});
fg1GradDrawable.setShape(GradientDrawable.RECTANGLE);
fg1GradDrawable.setCornerRadius(5);
ClipDrawable fg1clip = new ClipDrawable(
fg1GradDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
//Setup LayerDrawable and assign to progressBar
Drawable[] progressDrawables = {bgclip, fg2clip, fg1clip};
LayerDrawable progressLayerDrawable = new LayerDrawable(progressDrawables);
progressLayerDrawable.setId(0, android.R.id.background);
progressLayerDrawable.setId(1, android.R.id.secondaryProgress);
progressLayerDrawable.setId(2, android.R.id.progress);
//Copy the existing ProgressDrawable bounds to the new one.
Rect bounds = progressBar.getProgressDrawable().getBounds();
progressBar.setProgressDrawable(progressLayerDrawable);
progressBar.getProgressDrawable().setBounds(bounds);
// setProgress() ignores a change to the same value, so:
if (value1 == 0)
progressBar.setProgress(1);
else
progressBar.setProgress(0);
progressBar.setProgress(value1);
// setSecondaryProgress() ignores a change to the same value, so:
if (value2 == 0)
progressBar.setSecondaryProgress(1);
else
progressBar.setSecondaryProgress(0);
progressBar.setSecondaryProgress(value2);
//now force a redraw
progressBar.invalidate();
}
An example call would be:
setColours(myProgressBar,
0xff303030, //bgCol1 grey
0xff909090, //bgCol2 lighter grey
0xff0000FF, //fg1Col1 blue
0xffFFFFFF, //fg1Col2 white
50, //value1
0xffFF0000, //fg2Col1 red
0xffFFFFFF, //fg2Col2 white
75); //value2
If you don't need the 'secondary progress' simply set value2 to value1.
Apply this custom style to the progress bar.
<style name="customProgress" parent="@android:style/Widget.ProgressBar.Small">
<item name="android:indeterminateDrawable">@drawable/progress</item>
<item name="android:duration">40</item>
<item name="android:animationCache">true</item>
<item name="android:drawingCacheQuality">low</item>
<item name="android:persistentDrawingCache">animation</item>
</style>
@drawable/progress.xml -
<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/spinner_white"
android:pivotX="50%"
android:pivotY="50%" />
Use this type of image for progress bar.
For better result you can use multiple progress images. And please don't hesitate using images, because Android Platform itself use images for progressbar. The code is extracted from sdk :)
android:indeterminateTint="@android:color/white"
works only on API >=21 – Madeyedexter