5

I am using JProgressBar component along with the Nimbus UI Defaults. The problem is that When I want to manually change each progressbar's Bar color, I use BasicProgressBarUI by setting JProgressBar.setUI() function. This makes more trouble because I would like to just change the bar color and it seems that I loose the default look of the jprogressbar (Border, backgroundcolor dissappears).

So I can set UIDefaults of Nimbus ProgressBar when the code initializes. It works.

But I want to change each progressbar's bar color dynamically.

Is there any other way of changing Bar color of JProgressBar?

public class ProgressGenerator extends JFrame {

    protected int minValue = 0;
    protected int maxValue = 100;
    protected int counter = 0;
    protected JProgressBar progressBar;

    public ProgressGenerator() {
        super("JProgressBar Demo");
        setSize(300, 100);

        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
        } catch (ClassNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (InstantiationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (UnsupportedLookAndFeelException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        progressBar = new JProgressBar();
        progressBar.setMinimum(minValue);
        progressBar.setMaximum(maxValue);
        progressBar.setStringPainted(true);

        progressBar.setForeground(Color.GREEN);

        JButton start = new JButton("Start");
        start.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                Thread runner = new Thread() {
                    public void run() {
                        counter = minValue;
                        while (counter <= maxValue) {
                            Runnable runme = new Runnable() {
                                public void run() {
                                    progressBar.setValue(counter);
                                }
                            };
                            SwingUtilities.invokeLater(runme);
                            counter++;
                            try {
                                Thread.sleep(100);
                            } catch (Exception ex) {
                            }
                        }
                    }
                };
                runner.start();
            }
        });
        getContentPane().add(progressBar, BorderLayout.CENTER);
        getContentPane().add(start, BorderLayout.WEST);
        WindowListener wndCloser = new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        };
        addWindowListener(wndCloser);
        setVisible(true);
    }

    public static void main(String[] args) {
        new ProgressGenerator();
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
mbasol
  • 105
  • 1
  • 2
  • 9
  • 1
    for better help sooner post an [SSCCE](http://sscce.org/), short, runnable, compilable, otherwise have to search, google can returns that – mKorbel Jan 17 '13 at 08:39
  • 2
    See [this](http://stackoverflow.com/questions/7174420/change-colors-for-jprogressbar-with-nimbus) similar question/answer, which shows how to use `UIDefaults` and `putClientProperty` to change a single `JProgressBar` colour – David Kroukamp Jan 17 '13 at 09:51
  • The current problem is When I change the Bar color by using setForeground(Color.GREEN); it changes the SelectionBackground Color. And It seems that setBackgroundColor(Color.RED); command does not do anything. – mbasol Jan 17 '13 at 10:16
  • 2
    As answer linked by @DavidKroukamp shows, your [sscce](http://sscce.org/) should include your custom [`Painter`](http://weblogs.java.net/blog/joshy/archive/2006/09/introducing_pai.html). – trashgod Jan 17 '13 at 11:42
  • I set the UI as Nimbus and I wanted to change bar color by progressBar.setForeground(Color.GREEN) and it doesnt work. – mbasol Jan 17 '13 at 12:04
  • 1
    _I wanted to change bar color by progressBar.setForeground_ - and I want to get money by snipping fingers :-) Or in other words: if we apply the incorrect approach, we won't get the expected result. Repeating trashgod - see the QA already referenced by @DavidKroukamp – kleopatra Jan 17 '13 at 13:20
  • @mbasol as Kleo has said that wont work, though I must ask what is wrong with creating your own ProgressBarUI and calling the method on the progressbars UI instance to change color there? – David Kroukamp Jan 17 '13 at 13:25
  • 1
    @DavidKroukamp a custom ui is mostly the wrong approach (and much more work than you'd expect to get it right) - only advisable if you need functionality that's not provided by the core uis. As I understand the question, here it's only a config issue solveable as outlined in the other thread. – kleopatra Jan 17 '13 at 13:35
  • @kleopatra +1 Thank you for that verification, (before I posted wrong code as answer) I was not sure if the custom `UI` approach is *better*, but if you wrap it in your own custom `JProgressBar` class and forwarding the `setBackground` and `setForeground` call of `JProgressBar` to the `UI` which changes the color... I guess still `UIDefault`s are better? – David Kroukamp Jan 17 '13 at 13:38
  • @DavidKroukamp not sure I entirely understand what you meant - so saying none of both :-) Nimbus supports per-class and per-instance skinning, so that's the way the go - though it's typically tougher than expected, Nimbus being ... not quite grown-up. – kleopatra Jan 17 '13 at 13:52

2 Answers2

4

+1 to Kelopatra for being first.

Here is an example I made:

The JProgressBar color is initaily set via:

UIDefaults defaults = new UIDefaults();
defaults.put("ProgressBar[Enabled].foregroundPainter", new MyPainter(Color.GREEN));
defaults.put("ProgressBar[Enabled+Finished].foregroundPainter", new MyPainter(Color.GREEN));

progressBar.putClientProperty("Nimbus.Overrides.InheritDefaults", Boolean.TRUE);
progressBar.putClientProperty("Nimbus.Overrides", defaults);

On 50% progress the color will be changed to RED:

if (progressBar.getValue() == 50) {//change color on 50%
   UIDefaults defaults = new UIDefaults();
   defaults.put("ProgressBar[Enabled].foregroundPainter", new MyPainter(Color.RED));
   defaults.put("ProgressBar[Enabled+Finished].foregroundPainter", new MyPainter(Color.RED));
   progressBar.putClientProperty("Nimbus.Overrides.InheritDefaults", Boolean.TRUE);
   progressBar.putClientProperty("Nimbus.Overrides", defaults);                          
}

Test.java:

enter image description here

enter image description here

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;

public class Test {

    public static void createAndShowGUI() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        final JProgressBar progressBar = new JProgressBar();
        progressBar.setStringPainted(true);
        progressBar.setValue(0);
        progressBar.setBorderPainted(false);

        JButton startButton = new JButton("Start");

        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                UIDefaults defaults = new UIDefaults();
                defaults.put("ProgressBar[Enabled].foregroundPainter", new MyPainter(Color.GREEN));
                defaults.put("ProgressBar[Enabled+Finished].foregroundPainter", new MyPainter(Color.GREEN));

                progressBar.putClientProperty("Nimbus.Overrides.InheritDefaults", Boolean.TRUE);
                progressBar.putClientProperty("Nimbus.Overrides", defaults);

                SwingWorker worker = new SwingWorker() {
                    int current = 0, lengthOfTask = 100;

                    @Override
                    public Void doInBackground() {
                        while (current <= lengthOfTask && !isCancelled()) {

                            try { // dummy task
                                Thread.sleep(50);
                            } catch (InterruptedException ie) {
                            }

                            setProgress(100 * current / lengthOfTask);
                            current++;
                        }
                        return null;
                    }
                };
                worker.addPropertyChangeListener(new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent pce) {

                        String strPropertyName = pce.getPropertyName();
                        if ("progress".equals(strPropertyName)) {
                            int progress = (Integer) pce.getNewValue();
                            progressBar.setValue(progress);

                            if (progressBar.getValue() == 50) {//change color on 50%
                                UIDefaults defaults = new UIDefaults();
                                defaults.put("ProgressBar[Enabled].foregroundPainter", new MyPainter(Color.RED));
                                defaults.put("ProgressBar[Enabled+Finished].foregroundPainter", new MyPainter(Color.RED));

                                progressBar.putClientProperty("Nimbus.Overrides.InheritDefaults", Boolean.TRUE);
                                progressBar.putClientProperty("Nimbus.Overrides", defaults);
                            }

                        }
                    }
                });
                worker.execute();
            }
        });

        JPanel holder = new JPanel();
        holder.add(progressBar);
        holder.add(startButton);

        frame.add(holder);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
                createAndShowGUI();
            }
        });
    }
}

class MyPainter implements Painter<JProgressBar> {

    private final Color color;

    public MyPainter(Color c1) {
        this.color = c1;
    }
    @Override
    public void paint(Graphics2D gd, JProgressBar t, int width, int height) {
        gd.setColor(color);
        gd.fillRect(0, 0, width, height);
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • 1
    +1 for dynamically changing the color depending on value, nice touch :-) – kleopatra Jan 17 '13 at 14:41
  • @David Kroukamp Does it mean I can override setBackground and setForeground methods of JProgressBar and applying UI Defaults right? – mbasol Jan 17 '13 at 15:15
  • @DavidKroukamp I have question relating with Painter class. The bar in the progressbar I want it to not exceed the border area. I tried changing g.fillRect(0,0, width, height -4 ); it does change bottom however the top still exceeds the border. Any Solution? – mbasol Jan 18 '13 at 09:27
3

Nimbus supports per-component skinning, such as already answered in the other QA. Applied to a JProgressBar "bar" that means configuring the instance with a custom foregroundPainter like:

progressBar = new JProgressBar();

UIDefaults defaults = new UIDefaults();
Painter painter = new MyPainter(Color.GREEN);
defaults.put("ProgressBar[Enabled].foregroundPainter", painter);
defaults.put("ProgressBar[Enabled+Finished].foregroundPainter", painter);

progressBar.putClientProperty("Nimbus.Overrides.InheritDefaults", Boolean.TRUE);
progressBar.putClientProperty("Nimbus.Overrides", defaults);

The mimimal (ugly looking) painter would be something like:

public static class MyPainter extends AbstractRegionPainter {

    private Color fillColor;
    public MyPainter(Color color) {
        // as a slight visual improvement, make the color transparent
        // to at least see the background gradient
        // the default progressBarPainter does it as well (plus a bit more)
        fillColor = new Color(
                color.getRed(), color.getGreen(), color.getBlue(), 156);
    }

    @Override
    protected void doPaint(Graphics2D g, JComponent c, int width,
            int height, Object[] extendedCacheKeys) {
        g.setColor(Color.GREEN);
        g.fillRect(0, 0, width, height);
    }

    @Override
    protected PaintContext getPaintContext() {
        return null;
    }

}

To make it visually pleasing, have a look into ProgressBarPainter: it's package private so you can't do much other than understand how it achieves its gradients/path painting and do the same in a custom implementation.

kleopatra
  • 51,061
  • 28
  • 99
  • 211