Search code examples
javaswingjpanellook-and-feelnimbus

Update LookAndFeel Values On The Fly


I want to be able to update the LookAndFeel attributes of my Swing GUI on the fly. In this case, I have a simple Swing/Awt game running what starts out as the Nimbus LookAndFeel. At various points after start up I want to change (let us say) just one detail: the background color of my application.

I can change the background color by doing this:

for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
    if ("Nimbus".equals(info.getName())) {
        UIManager.setLookAndFeel(info.getClassName());
        UIManager.getLookAndFeelDefaults().put("Panel.background", Color.RED);
        SwingUtilities.updateComponentTreeUI(SomeGame.this);
        break;
    }
}

This "works" in that the background color of the app changes correctly and the program doesn't crash. But on the command line I get error:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at javax.swing.plaf.synth.SynthLookAndFeel.paintRegion(SynthLookAndFeel.java:371)
    at javax.swing.plaf.synth.SynthLookAndFeel.update(SynthLookAndFeel.java:335)

Obviously, something is null, but I can not figure out what it is or how to fix it. There must be something I don't understand. I have looked at other StackOverflow questions about setting background colors in Nimbus and overriding the LookAndFeel information after startup.

  • When I call getLookAndFeelDefaults() do I need to specify the rest of the defaults as well?
  • Have there been changes in how this works between Java 1.6 and 1.7?

Solution

    • all code for Java6, have to change Nimbus imports for Java7

    • Nimbus has a few suprises, one of them is that is possible (without dirty hacks, I'm leaving comments for those hacks) to change Background for JPanel only one time

    enter image description here

    enter image description here

    enter image description here

    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.Color;
    import java.awt.AlphaComposite;
    import javax.swing.*;
    import javax.swing.UIManager.LookAndFeelInfo;
    import javax.swing.border.EmptyBorder;
    
    public class ButtonTest {
    
        private JFrame frame;
        private JPanel panel;
        private JButton opaqueButton1;
        private JButton opaqueButton2;
        private SoftJButton softButton1;
        private SoftJButton softButton2;
        private Timer alphaChanger;
    
        public void createAndShowGUI() {
            opaqueButton1 = new JButton("Opaque Button");
            opaqueButton2 = new JButton("Opaque Button");
            softButton1 = new SoftJButton("Transparent Button");
            softButton2 = new SoftJButton("Transparent Button");
            opaqueButton1.setBackground(Color.GREEN);
            softButton1.setBackground(Color.GREEN);
            panel = new JPanel();
            panel.setLayout(new java.awt.GridLayout(2, 2, 10, 10));
            panel.add(opaqueButton1);
            panel.add(softButton1);
            panel.add(opaqueButton2);
            panel.add(softButton2);
            panel.setBorder(new EmptyBorder(10, 10, 10, 10));
            frame = new JFrame();
            frame.add(panel);
            frame.setLocation(150, 100);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
            alphaChanger = new Timer(50, new ActionListener() {
    
                private float incrementer = -.03f;
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    float newAlpha = softButton1.getAlpha() + incrementer;
                    if (newAlpha < 0) {
                        newAlpha = 0;
                        incrementer = -incrementer;
                    } else if (newAlpha > 1f) {
                        newAlpha = 1f;
                        incrementer = -incrementer;
                    }
                    softButton1.setAlpha(newAlpha);
                    softButton2.setAlpha(newAlpha);
                }
            });
            alphaChanger.start();
            Timer uiChanger = new Timer(3500, new ActionListener() {
    
                private final LookAndFeelInfo[] laf = UIManager.getInstalledLookAndFeels();
                private int index = 1;
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    try {
                        UIManager.setLookAndFeel(laf[index].getClassName());
                        if (laf[index].getClassName().equals("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel")) {
                            UIManager.getLookAndFeelDefaults().put("Panel.background", Color.orange);
                            SwingUtilities.updateComponentTreeUI(frame);
                        } else {
                            panel.setBackground(Color.yellow);
                            SwingUtilities.updateComponentTreeUI(frame);
                        }
                        opaqueButton1.setText(laf[index].getClassName());
                        softButton1.setText(laf[index].getClassName());
                        frame.pack();
                    } catch (Exception exc) {
                        exc.printStackTrace();
                    }
                    index = (index + 1) % laf.length;
                }
            });
            uiChanger.start();
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new ButtonTest().createAndShowGUI();
                }
            });
        }
    
        private static class SoftJButton extends JButton {
    
            private static final JButton lafDeterminer = new JButton();
            private static final long serialVersionUID = 1L;
            private boolean rectangularLAF;
            private float alpha = 1f;
    
            SoftJButton() {
                this(null, null);
            }
    
            SoftJButton(String text) {
                this(text, null);
            }
    
            SoftJButton(String text, Icon icon) {
                super(text, icon);
                setOpaque(false);
                setFocusPainted(false);
            }
    
            public float getAlpha() {
                return alpha;
            }
    
            public void setAlpha(float alpha) {
                this.alpha = alpha;
                repaint();
            }
    
            @Override
            public void paintComponent(java.awt.Graphics g) {
                java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
                g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
                if (rectangularLAF && isBackgroundSet()) {
                    Color c = getBackground();
                    g2.setColor(c);
                    g.fillRect(0, 0, getWidth(), getHeight());
                }
                super.paintComponent(g2);
            }
    
            @Override
            public void updateUI() {
                super.updateUI();
                lafDeterminer.updateUI();
                rectangularLAF = lafDeterminer.isOpaque();
            }
        }
    }
    

    enter image description here

    enter image description here

    enter image description here

    import java.awt.Color;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.LookAndFeel;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    import javax.swing.UIDefaults;
    import javax.swing.UIManager;
    import javax.swing.UIManager.LookAndFeelInfo;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class NimbusTestButtonsBackground extends JFrame {
    
        private static final long serialVersionUID = 1L;
        private javax.swing.JButton button;
    
        public NimbusTestButtonsBackground() {
            button = new javax.swing.JButton();
            button.setText("Text");
            add(button);
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
            this.pack();
            Timer t = new Timer(1000, new ActionListener() {
    
                private Random r = new Random();
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    Color c = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
                    try {
                        LookAndFeel lnf = UIManager.getLookAndFeel().getClass().newInstance();
                        UIDefaults uiDefaults = lnf.getDefaults();
                        uiDefaults.put("nimbusBase", c);
                        UIManager.getLookAndFeel().uninitialize();
                        UIManager.setLookAndFeel(lnf);
                    } catch (InstantiationException ex) {
                    } catch (IllegalAccessException ex) {
                    } catch (UnsupportedLookAndFeelException ex) {
                    }
                    UIDefaults defaults = UIManager.getDefaults();
                    defaults.put("Button.background", c);
                    SwingUtilities.updateComponentTreeUI(button);
                }
            });
            t.start();
        }
    
        public static void main(String args[]) {
            try {
                for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                    if ("Nimbus".equals(info.getName())) {
                        UIManager.setLookAndFeel(info.getClassName());
                        break;
                    }
                }
            } catch (Exception e) {
                return;
            }
    
            java.awt.EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new NimbusTestButtonsBackground().setVisible(true);
                }
            });
        }
    }
    
    • then I see (this way as most comfortable, easiest, etc..) overriding paintComponent for JPanel as best the best way

    enter image description hereenter image description hereenter image description here

    import java.awt.AlphaComposite;
    import java.awt.Color;
    import java.awt.Container;
    import java.awt.EventQueue;
    import java.awt.GradientPaint;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridLayout;
    import java.awt.Rectangle;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.image.BufferedImage;
    import java.util.Random;
    import javax.swing.JButton;
    import javax.swing.JComponent;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.RepaintManager;
    import javax.swing.Timer;
    import javax.swing.UIManager;
    import javax.swing.UIManager.LookAndFeelInfo;
    
    public class ChangePanelBackgroundNimbus {
    
        private JFrame frame = new JFrame();
    
        public ChangePanelBackgroundNimbus() {
            GradientPane pane = new GradientPane();
            pane.setLayout(new GridLayout(6, 4, 10, 10));
            for (int i = 1; i <= 24; i++) {
                pane.add(createButton(i));
            }
            pane.setOpaque(false);
            frame.add(pane);
            RepaintManager.setCurrentManager(new RepaintManager() {
    
                @Override
                public void addDirtyRegion(JComponent c, int x, int y, int w, int h) {
                    Container con = c.getParent();
                    while (con instanceof JComponent) {
                        if (!con.isVisible()) {
                            return;
                        }
                        if (con instanceof GradientPane) {
                            c = (JComponent) con;
                            x = 0;
                            y = 0;
                            w = con.getWidth();
                            h = con.getHeight();
                        }
                        con = con.getParent();
                    }
                    super.addDirtyRegion(c, x, y, w, h);
                }
            });
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationByPlatform(true);
            //frame.setSize(400, 300);
            frame.pack();
            frame.setVisible(true);
        }
    
        private JButton createButton(final int text) {
            JButton button = new JButton(Integer.toString(text));
            return button;
        }
    
        class GradientPane extends JPanel {
    
            private static final long serialVersionUID = 1L;
            private final int h = 150;
            private BufferedImage img = null;
            private BufferedImage shadow = new BufferedImage(1, h, BufferedImage.TYPE_INT_ARGB);
    
            public GradientPane() {
                paintBackGround(new Color(150, 250, 150));
                Timer t = new Timer(500, new ActionListener() {
    
                    private Random r = new Random();
    
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        paintBackGround(new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256)));
                    }
                });
                t.start();
            }
    
            public void paintBackGround(Color g) {
                Graphics2D g2 = shadow.createGraphics();
                g2.setPaint(g);
                g2.fillRect(0, 0, 1, h);
                g2.setComposite(AlphaComposite.DstIn);
                g2.setPaint(new GradientPaint(0, 0, new Color(0, 0, 0, 0f), 0, h, new Color(0.4f, 0.8f, 0.8f, 0.5f)));
                g2.fillRect(0, 0, 1, h);
                g2.dispose();
            }
    
            @Override
            public void paintComponent(Graphics g) {
                if (img == null || img.getWidth() != getWidth() || img.getHeight() != getHeight()) {
                    img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
                }
                Graphics2D g2 = img.createGraphics();
                super.paintComponent(g2);
                Rectangle bounds = this.getVisibleRect();
                g2.scale(bounds.getWidth(), -1);
                g2.drawImage(shadow, bounds.x, -bounds.y - h, null);
                g2.scale(1, -1);
                g2.drawImage(shadow, bounds.x, bounds.y + bounds.height - h, null);
                g2.dispose();
                g.drawImage(img, 0, 0, null);
                repaint();
            }
        }
    
        public static void main(String[] args) {
            try {
                for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                    if ("Nimbus".equals(info.getName())) {
                        UIManager.setLookAndFeel(info.getClassName());
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    ChangePanelBackgroundNimbus ml = new ChangePanelBackgroundNimbus();
                }
            });
        }
    }