We have a large application. At startup we set some UIManager properties, like Button.background and Panel.background. Almost all components then uses these values, but some components have custom backgrounds. Now we would like the users to have an option of customizing their colors, and be able to view them at once, without restarting the application. Setting new values for eg Button.background and Panel.background works fine on new components, eg if the user opens a new JDialog, but will not update existing components. I have tried SwingUtilities.updateComponentTreeUI, updateUI etc on the components, but it seems that the background is allready set on the existing components and will not update. I cannot loop through all components and set background accordingly, since some backgrounds have custom backgrounds. What should I do? We use jgoodies look&feel, but it will not work in standard UIManager.getCrossPlatformLookAndFeelClassName() either. I will provide a small coding example of what I want.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Div extends JFrame implements ActionListener{
private JButton green;
private JButton red;
public static void main(String[] args) throws Exception{
new Div();
}
public Div() throws Exception{
UIManager.put("Button.background", Color.green);
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
green = new JButton("Should be changed");
red = new JButton("Should not be changed");
JPanel cp = (JPanel)getContentPane(); cp.setLayout(new GridLayout(2,1));
cp.add(green);
cp.add(red);
red.setBackground(Color.red);
green.addActionListener(this);
setSize(200,100); setLocation(100,50);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
UIManager.put("Button.background", ((Color)UIManager.get("Button.background")).darker());
green.updateUI(); //Should update the green button with a new color, but doesn't
}
}
EDIT/SOLUTION: camickrs answer seems to work on all components that I have tried except my provided JButton-example (eg Panel.background, ComboBox.background, TextField.background etc) And I even found that the L&F that we use, jgoodies, even respects this for JButton :-D So now I am happy. So code changes to the example would be:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.ColorUIResource;
public class Div extends JFrame implements ActionListener{
private JButton green;
private JButton red;
private JPanel panel;
private JComboBox<?> box;
private JTextField txt;
public static void main(String[] args) throws Exception{
new Div();
}
public Div() throws Exception{
ColorUIResource r = new ColorUIResource(new Color(0,255,0));
UIManager.put("Button.background", r);
UIManager.put("Panel.background", r);
UIManager.put("ComboBox.background", r);
UIManager.put("TextField.background", r);
//UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); //Works, but not on JButtons
UIManager.setLookAndFeel("com.jgoodies.looks.plastic.PlasticXPLookAndFeel"); //Also works with JButton :-D
green = new JButton("Should be changed");
red = new JButton("Should not be changed");
panel = new JPanel();
box = new JComboBox();
txt = new JTextField();
JPanel cp = (JPanel)getContentPane(); cp.setLayout(new GridLayout(5,1));
cp.add(green);
cp.add(red);
cp.add(panel);
cp.add(box);
cp.add(txt);
red.setBackground(Color.red);
green.addActionListener(this);
setSize(200,200); setLocation(100,100);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
Color col = ((Color)UIManager.get("Button.background")).darker();
ColorUIResource r = new ColorUIResource(col);
UIManager.put("Button.background", r);
UIManager.put("Panel.background", r);
UIManager.put("ComboBox.background", r);
UIManager.put("TextField.background", r);
SwingUtilities.updateComponentTreeUI(this);
}
}
UIManager.put("Button.background", ((Color)UIManager.get("Button.background")).darker());
I believe that the code should be:
Color background = UIManager.getColor("Button.background").darker();
UIManager.put("Button.background", new ColorUIResource(background));
SwingUtilities.updateComponentTree(...);
The UIManager will only update properties that are part of the LAF, so you need to use the ColorUIResource wrapper.
Edit:
The main idea is you need to originally set the UIManager color to use the ColorUIResource. This works for a JTextField:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class Div extends JFrame implements ActionListener{
private JTextField green;
private JTextField red;
public static void main(String[] args) throws Exception{
new Div();
}
public Div() throws Exception{
UIManager.put("TextField.background", new ColorUIResource(Color.green));
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
green = new JTextField("Should be changed");
red = new JTextField("Should not be changed");
JPanel cp = (JPanel)getContentPane(); cp.setLayout(new GridLayout(2,1));
cp.add(green);
cp.add(red);
red.setBackground(Color.red);
green.addActionListener(this);
setSize(200,100); setLocation(100,50);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
Color background = Color.YELLOW;
UIManager.put("TextField.background", new ColorUIResource(background));
SwingUtilities.updateComponentTreeUI(this);
}
}
I'm not exactly sure why a JButton doesn't work, but as @mKorbel has mentioned, JButton is different from other components.