JProgressbar: how to change colour based on progress?
Solution 1
Thanks for the help but by trial and error I found a way to get the colour changing with using my existing code.
I am not sure if its the quirk of JPrgressbar but if call do the following call the foreground bar colour can be changed dynamically with minimal rework:
progressBar.setStringPainted(true);
Solution 2
- This sample was inspired from:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import javax.swing.event.*;
public class GradientPalletProgressBarDemo {
public JComponent makeUI() {
final JProgressBar progressBar = new JProgressBar();
progressBar.setOpaque(false);
progressBar.setUI(new GradientPalletProgressBarUI());
JPanel p = new JPanel();
p.add(progressBar);
p.add(new JButton(new AbstractAction("Start") {
@Override public void actionPerformed(ActionEvent e) {
SwingWorker<Void,Void> worker = new SwingWorker<Void,Void>() {
@Override public Void doInBackground() {
int current = 0, lengthOfTask = 100;
while(current<=lengthOfTask && !isCancelled()) {
try { // dummy task
Thread.sleep(50);
} catch(InterruptedException ie) {
return null;
}
setProgress(100 * current / lengthOfTask);
current++;
}
return null;
}
};
worker.addPropertyChangeListener(new ProgressListener(progressBar));
worker.execute();
}
}));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new GradientPalletProgressBarDemo().makeUI());
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ProgressListener implements PropertyChangeListener {
private final JProgressBar progressBar;
ProgressListener(JProgressBar progressBar) {
this.progressBar = progressBar;
this.progressBar.setValue(0);
}
@Override public void propertyChange(PropertyChangeEvent evt) {
String strPropertyName = evt.getPropertyName();
if("progress".equals(strPropertyName)) {
progressBar.setIndeterminate(false);
int progress = (Integer)evt.getNewValue();
progressBar.setValue(progress);
}
}
}
class GradientPalletProgressBarUI extends BasicProgressBarUI {
private final int[] pallet;
public GradientPalletProgressBarUI() {
super();
this.pallet = makeGradientPallet();
}
private static int[] makeGradientPallet() {
BufferedImage image = new BufferedImage(100, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
Point2D start = new Point2D.Float(0f, 0f);
Point2D end = new Point2D.Float(99f, 0f);
float[] dist = {0.0f, 0.5f, 1.0f};
Color[] colors = { Color.RED, Color.YELLOW, Color.GREEN };
g2.setPaint(new LinearGradientPaint(start, end, dist, colors));
g2.fillRect(0, 0, 100, 1);
g2.dispose();
int width = image.getWidth(null);
int[] pallet = new int[width];
PixelGrabber pg = new PixelGrabber(image, 0, 0, width, 1, pallet, 0, width);
try {
pg.grabPixels();
} catch(Exception e) {
e.printStackTrace();
}
return pallet;
}
private static Color getColorFromPallet(int[] pallet, float x) {
if(x < 0.0 || x > 1.0) {
throw new IllegalArgumentException("Parameter outside of expected range");
}
int i = (int)(pallet.length * x);
int max = pallet.length-1;
int index = i<0?0:i>max?max:i;
int pix = pallet[index] & 0x00ffffff | (0x64 << 24);
return new Color(pix, true);
}
@Override public void paintDeterminate(Graphics g, JComponent c) {
if (!(g instanceof Graphics2D)) {
return;
}
Insets b = progressBar.getInsets(); // area for border
int barRectWidth = progressBar.getWidth() - (b.right + b.left);
int barRectHeight = progressBar.getHeight() - (b.top + b.bottom);
if (barRectWidth <= 0 || barRectHeight <= 0) {
return;
}
int cellLength = getCellLength();
int cellSpacing = getCellSpacing();
// amount of progress to draw
int amountFull = getAmountFull(b, barRectWidth, barRectHeight);
if(progressBar.getOrientation() == JProgressBar.HORIZONTAL) {
// draw the cells
float x = amountFull / (float)barRectWidth;
g.setColor(getColorFromPallet(pallet, x));
g.fillRect(b.left, b.top, amountFull, barRectHeight);
} else { // VERTICAL
//...
}
// Deal with possible text painting
if(progressBar.isStringPainted()) {
paintString(g, b.left, b.top, barRectWidth, barRectHeight, amountFull, b);
}
}
}
Solution 3
You can do either of this way:
UIManager.put("ProgressBar.background", Color.BLACK); //colour of the background
UIManager.put("ProgressBar.foreground", Color.RED); //colour of progress bar
UIManager.put("ProgressBar.selectionBackground",Color.YELLOW); //colour of percentage counter on black background
UIManager.put("ProgressBar.selectionForeground",Color.BLUE); //colour of precentage counter on red background
There is a thread here. Setting the colors of a JProgressBar text
There is a sample demo here at this link as well. how to change color progressbar
Solution 4
As you noticed, is value-dependent "progress" painting not supported. Theoretically, setting the foregound of the progressBar instance dynamically (that is depending on value) is the way to go.
But: the details of progressBar painting are highly LAF dependent
- in simple LAFs (like Metal) setting the foreground is fine
- in fully skinnable LAFs (like synth-based LAF) that support per-instance skinning (f.i. Nimbus) you can try to provide a Painter which uses the foreground
- in not-skinnable LAFs there is no way (except overriding the concrete LAF and try to hook into the painting code)
Some snippet:
final JProgressBar bar = new JProgressBar();
// used for Nimbus (beware: just a proof-of-concept - this looks extremely ugly!)
Painter p = new Painter() {
@Override
public void paint(Graphics2D g, Object object, int width, int height) {
JProgressBar bar = (JProgressBar) object;
g.setColor(bar.getForeground());
g.fillRect(0, 0, width, height);
}
};
// install custom painter on the bar
UIDefaults properties = new UIDefaults();
properties.put("ProgressBar[Enabled].foregroundPainter", p);
bar.putClientProperty("Nimbus.Overrides", properties);
// simulate progress
Action action = new AbstractAction("timer") {
@Override
public void actionPerformed(ActionEvent e) {
bar.setValue(bar.getValue() + 1);
// change foreground value-dependent
if (bar.getValue() > 10) {
bar.setForeground(Color.RED);
}
}
};
Timer timer = new Timer(100, action);
timer.start();
Related videos on Youtube
Foo
Updated on July 11, 2022Comments
-
Foo almost 2 years
Is it possible to be able to change the bar colour depending on the value of the progress? I tried the following but it doesn't work:
percentUsed = (int)(((float) used / (float) max) * BAR_PERCENTAGE); if (percentUsed >= ORANGE_THRESHOLD && percentUsed < RED_THRESHOLD) { if (!m_orangeIndicator) { LOG.warn(String.format("Memory usage exceeds %d percent.", ORANGE_THRESHOLD)); m_orangeIndicator = true; } colour = Color.ORANGE; m_redIndicator = false; } else if (percentUsed >= RED_THRESHOLD) { if (!m_redIndicator) { LOG.warn(String.format("Memory usage exceeds %d percent.", RED_THRESHOLD)); m_orangeIndicator = true; m_redIndicator = true; } colour = Color.RED; } else { m_orangeIndicator = false; m_redIndicator = false; colour = Color.GREEN; } m_memUsageBar.setForeground(colour); m_memUsageBar.setValue(percentUsed); m_memUsageBar.updateUI();
I am guessing it is not a trivial thing to do because JProgressbar is not meant to be used that way... But is it possible or are there alternatives?
-
MadProgrammerFirst thing that pops out is - don't call
updateUI
. This doesn't have anything (directly) to do with requesting repaints. Useinvalidate
andrepaint
instead.
-
-
Foo over 11 yearsIs this the way to be able to dynamically change the colour of the progress bar itself? I thought this is more of initialisation of the bar to be a particular colour?
-
Foo over 11 yearsI tried placing the 4 lines of code before the JProgressbar has been initiated but I didn't notice the colour change...
-
Foo over 11 yearsDoes it matter on the OS the app is running on?
-
mKorbel over 11 yearsthanks for lesson a about PixelGrabber, learning item of day :-)
-
UVM over 11 yearsIt may matter because default look and feel in Apple is different that Windows
-
aterai over 11 years@mKorbel Thanks, I remove unused variable.