0

I am using this code to set up a look and feel on a program: UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName()); I am only using the system look and feel on a JPanel (inside a JFrame) that i show during the runtime of the program. The first time i show the JPanel, the L&F is wrong (looks like it is for an older version of windows or something, not the same as i used before, on the other components). I hide the JPanel then open it agin, the L&F is now correct. Sadly i wasn't able to produce a reproducaple example but the problem persists in the original program. It consists of multiple classes, and has an object orientedly written UI, this is the code responsible for the problematic JPanel:

package almanah;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class SeriesProperties extends JPanel {

    JButton button_close = new JButton();
    JPanel container = new JPanel();
    JScrollPane scPane = new JScrollPane(container);

    public SeriesProperties(ItemLib lib) {
        
        try {
            UIManager.setLookAndFeel(
            UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnsupportedLookAndFeelException ex) {
            Logger.getLogger(SeriesProperties.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        this.setLayout(new BorderLayout());

        this.add(button_close, BorderLayout.EAST);
        button_close.addActionListener((e) -> {
            Almanah.frame.swapToTiles();
        });

        scPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        scPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        this.add(scPane, BorderLayout.CENTER);
        
        //container
        container.setBackground(Color.red);
        container.setLayout(new WrapLayout());
        
        for (int i = 0; i < 100; i++) {
            JPanel panel=new JPanel();
            panel.setPreferredSize(new Dimension(100, 100));
            container.add(panel);
        }
        this.updateUI();
    }

}
error13660
  • 120
  • 7
  • 1
    `this.updateUI();` shoaled be avoided. You should only modify the look and feel once, BEFORE you build your UI, this probably suggests that you should do it outside of the `JPanel`. It also looks like you have other UI elements out of the `JPanel` which may be influencing the state - again, best to set the UI L&F before ANY UI elements are created – MadProgrammer Feb 20 '21 at 23:15

2 Answers2

2

Establish the look and feel state BEFORE you create ANY UI elements. The ability to switch the L&F at runtime is actually a side effect and is generally discouraged as it was never a feature of the system.

Instead, probably in your main method, set the look and feel and then build your UI after it.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(
                            UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
                JFrame frame = new JFrame();
                // I don't know what ItemLib, as the source is incomplete
                frame.add(new SeriesProperties(...));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class SeriesProperties extends JPanel {

        JButton button_close = new JButton();
        JPanel container = new JPanel();
        JScrollPane scPane = new JScrollPane(container);

        public SeriesProperties(ItemLib lib) {

            this.setLayout(new BorderLayout());

            this.add(button_close, BorderLayout.EAST);
            button_close.addActionListener((e) -> {
                // This seems like a bad idea
                // You should consider using a observer/delegate
                // pattern instead, reducing the coupling of your code
                Almanah.frame.swapToTiles();
            });

            scPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            scPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            this.add(scPane, BorderLayout.CENTER);

            //container
            container.setBackground(Color.red);
            container.setLayout(new WrapLayout());

            for (int i = 0; i < 100; i++) {
                JPanel panel = new JPanel();
                panel.setPreferredSize(new Dimension(100, 100));
                container.add(panel);
            }
            //this.updateUI();
        }

    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • So as i understand there is no way to set different L&F-s for different parts of a program. Am i right? – error13660 Feb 20 '21 at 23:35
  • 2
    Yes, you're right - or more to the point - I would consider this a bad idea which would have wide ranging and unpredictable side effects, as everything rendered after the fact would adopt that look and feel – MadProgrammer Feb 21 '21 at 00:28
2

Solution:

Error13660, have you fixed your problem? Here is my solution to change look and feel.

And see This answer.

package snippet;

import java.awt.EventQueue;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(() -> {
            
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                    | UnsupportedLookAndFeelException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
            var frame = new JFrame();
            
            var panel = new JPanel();
            
            { // Button 0: onclick change theme 0
                var btn = new JButton("Theme 0");
                btn.addActionListener((e) -> {
                    System.out.println("actionPerformed: change theme 0");
                    Test.changeLaF(frame, "javax.swing.plaf.metal.MetalLookAndFeel");
                });
                panel.add(btn);
            }
         
            { // Button 1: onclick change theme 1
                var btn = new JButton("Theme 1");
                btn.addActionListener((e) -> {
                    System.out.println("actionPerformed: change theme 1");
                    Test.changeLaF(frame, "com.sun.java.swing.plaf.motif.MotifLookAndFeel");
                });
                panel.add(btn);
            } 
            
            frame.add(panel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
 
    
    public static final void changeLaF(final JFrame frame, final String nameLookAndFeel) {
        try {
            UIManager.setLookAndFeel(nameLookAndFeel);
            SwingUtilities.updateComponentTreeUI(frame);
            frame.pack ();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                | UnsupportedLookAndFeelException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

Before edition of 24-02-21:

Have you tried using SwingUtilities.updateComponentTreeUI(frame);

Changing the Look and Feel After Startup: You can change the L&F with setLookAndFeel even after the program's GUI is visible. To make existing components reflect the new L&F, invoke the SwingUtilities updateComponentTreeUI method once per top-level container. Then you might wish to resize each top-level container to reflect the new sizes of its contained components. For example:

see: lookandfeel/plaf#Dynamic

UIManager.setLookAndFeel(lnfName);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack();

Update: /!\ if successful loaded L&F, all components use that L&F. lookandfeel/plaf#Steps

JTorleon
  • 108
  • 1
  • 9
  • 1
    [This answer](http://stackoverflow.com/a/5630271/418556), while focusing mainly on how to combine layouts in a complex GUI, also offers the ability to change the PLAF after the GUI has appeared on-screen. To put that another way, it uses the method seen above, to change the PLAF after a GUI is visible. Good call on adding the `frame.pack();` - many PLAFs will use different sized fonts or padding / insets. A call to `pack()` will adjust the size of the GUI to need. – Andrew Thompson Feb 21 '21 at 07:55