5

I want to use a different background color for all my JPanels in an application. How can I do that when using Nimbus Look and Feel?

I follow Changing the Color Theme to change the color of components in Nimbus Look and Feel.

It only works sometimes, randomly. If I set a PropertyChagneListener before I change the color, it is only notified once.

Here is some test code:

public class RedPanels extends JFrame {

  public RedPanels() {
    JPanel panel = new JPanel();
    add(panel);
    setPreferredSize(new Dimension(100, 100));
    pack();
    setVisible(true);
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

      @Override
      public void run() {

        try {
          for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                UIManager.setLookAndFeel(info.getClassName());
                UIManager.getDefaults().addPropertyChangeListener(
                                               new PropertyChangeListener() {

                  @Override
                  public void propertyChange(PropertyChangeEvent event) {
                    if (event.getPropertyName().equals("Panel.background")) {
                      System.out.println("color changed");
                    }

                });
                UIManager.put("Panel.background", new Color(255,0,0));
                break;
            }
          }
        } catch (Exception e) {
            // Nimbus is not available.
        }
        new RedPanels();
        }
    });
  }
}
Jonas
  • 121,568
  • 97
  • 310
  • 388
  • I'd put in some code to allow you to see if someone/something else clobbered Panel.background. – Ed Staub Nov 23 '11 at 17:48
  • @EdStaub: I added an `PropertyChangeListener` for `Panel.background` now, see my code. But it's never *notified*, not even when I set the color. – Jonas Nov 23 '11 at 18:17
  • again (my fault, unconcentrated, time for some food ;-) The only property the UIManager knows of is "lookAndFeel", that is it notifies its listeners about a change of the LAF, but not about changes of any values stored into it (afair) BTW, not sure if Nimbus honors the background color stored in the ui, there had been issues – kleopatra Nov 23 '11 at 18:37
  • @kleopatra: You are right, I have updated my code with the propertyChangeListener, now it listens on UIDefaults. – Jonas Nov 23 '11 at 18:38
  • @kleopatra: Yes, this seem to be such an issue. I followed this tutorial [Changing the Color Theme](http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/color.html) – Jonas Nov 23 '11 at 18:43
  • Apologies, yes, I see it now. @kleopatra I do see the need to repaint when the lnf changes, but you did mention that it is false. Did I miss something? – Vern Nov 23 '11 at 18:47
  • @Vern: I set the colors **before** the interface is initiated. So it never changes. – Jonas Nov 23 '11 at 18:49
  • @Jonas I like what you have above (including Igor's answer below). But how can I change, say, the color of the background panel on the fly at arbitrary points long after the UI has been created an instantiated? – john_science Jan 29 '13 at 04:22

3 Answers3

7
UIManager.getLookAndFeelDefaults().put("Panel.background", Color.RED);
Igor Nardin
  • 1,611
  • 14
  • 12
2

there are three ways

1) override nimbusBase for set DerivedColor

2) create own Painter, only one example is there -> aephyr codesource,

3) simple and dirty hack to set the Color directly

enter image description here

import java.awt.*;
import javax.swing.*;
import javax.swing.border.LineBorder;

public class NimbusJPanelBackGround {

    public NimbusJPanelBackGround() {
        JPanel p = new JPanel();
        UIDefaults nimbusOverrides = new UIDefaults();
        nimbusOverrides.put("Panel.background", Color.blue);
        p.putClientProperty("Nimbus.Overrides", nimbusOverrides);
        SwingUtilities.updateComponentTreeUI(p);

        JPanel p1 = new JPanel();
        nimbusOverrides = new UIDefaults();
        nimbusOverrides.put("Panel.background", Color.green);
        p1.putClientProperty("Nimbus.Overrides", nimbusOverrides);
        SwingUtilities.updateComponentTreeUI(p1);
        p1.setBorder(new LineBorder(Color.black, 1));

        JPanel p2 = new JPanel();
        nimbusOverrides = new UIDefaults();
        nimbusOverrides.put("Panel.background", Color.ORANGE);
        p2.putClientProperty("Nimbus.Overrides", nimbusOverrides);
        SwingUtilities.updateComponentTreeUI(p2);

        JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.add(p, BorderLayout.NORTH);
        f.add(p1, BorderLayout.CENTER);
        f.add(p2, BorderLayout.SOUTH);
        f.setSize(200, 100);
        f.setLocation(150, 150);
        f.setVisible(true);
    }

    public static void main(String[] args) {

        try {
            for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(laf.getName())) {
                    UIManager.setLookAndFeel(laf.getClassName());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                NimbusJPanelBackGround nimbusJPanelBackGround = new NimbusJPanelBackGround();
            }
        });
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • No, it's **not** an alternative to set the colors for every component, it's hundreds of them. I need a way to change the colors for all of them in one place. – Jonas Nov 23 '11 at 18:58
  • you can put component with same properties (meaning BackGround, Foreground, Border, Font) to the whatever Array and change that this way, check aephyr sourcecode, I think that there are all three ways :-) – mKorbel Nov 23 '11 at 19:09
  • I think the only way to go is to stop using Nimbus. – Jonas Nov 23 '11 at 19:16
  • @Jonas as you decided, note you have to calculate that other Custom L&F are so sensitive to EDT, no suprises :-) – mKorbel Nov 23 '11 at 20:28
2

Looks like a bug in jdk6, Panel.background one of the properties not taken. Following works in jdk7 (note the sequence: first set the color, then the LAF)

 UIManager.put("Panel.background", new Color(255,0,0));
 UIManager.setLookAndFeel(info.getClassName());

My guess is that it's still somehow buggy, as Nimbus is supposed to update its properties on receiving any change in the managers setting, so reversing the sequence to first set Nimbus, then put the color) should work as well, but doesn't even in jdk7

 UIManager.setLookAndFeel(info.getClassName());
 UIManager.put("Panel.background", new Color(255,0,0));
 //UIManager.put("control", Color.MAGENTA);

Seems to be specific to Panel.background (and most probably a bunch of others), "control" is okay in both jdks, both before and after setting the LAF.

kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • I use JDK7 for all my tests, so the they only works "sometimes" in JDK7, even the first of your examples. – Jonas Nov 24 '11 at 11:03
  • @Jonas hmm ... you mean: running your code with the put("Panel.background", ...) moved to the start of the try block only works sporadically? Or that other properties don't work? or ...? Anyway, that sounds bad - hoped that jdk7 had resolved most of the Nimbus quirks ... – kleopatra Nov 24 '11 at 12:42