How to change Swing application's look and feel at runtime?
Asked Answered
T

2

6

I know that there's a SwingUtilities.updateComponentTreeUI(Component c) method but it doesn't work perfectly. For example, I have a JFileChooser and the current look and feel is Windows, then I change the look and feel to Nimbus with SwingUtilities.updateComponentTreeUI(mainWindow), and the main window's style is changed correctly, but when I show the file chooser with the JFileChooser.showOpenDialog(Component parent) method, it's still in Windows look and feel. The same happens if I show a popup dialog with the JPopupMenu.show(Component invoker, int x, int y) method.

Any solution to this issue?

Tick answered 23/4, 2013 at 9:10 Comment(1)
To be clear, you also adjusted the current L&F with UIManager.setLookAndFeel()?Spout
P
7

Assuming that value is the class name of the new look-and-feel, here is the snippet to update all windows and sub-components:

public static void updateLAF(String value) {
    if (UIManager.getLookAndFeel().getClass().getName().equals(value)) {
        return;
    }
    try {
        UIManager.setLookAndFeel(value);
        for (Frame frame : Frame.getFrames()) {
            updateLAFRecursively(frame);
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (UnsupportedLookAndFeelException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static void updateLAFRecursively(Window window) {
    for (Window childWindow : window.getOwnedWindows()) {
        updateLAFRecursively(childWindow);
    }
    SwingUtilities.updateComponentTreeUI(window);
}
Penicillate answered 23/4, 2013 at 9:26 Comment(11)
Which will not help if you cache a Swing component which is not part of the Swing hierarchy in your own code.Sixtieth
@Sixtieth Indeed, nice catch for the cached components. Yet, if it is embedded in another component which is the hierarchy, another option is to override updateUI, call super.updateUI and also forward the call to the file-chooser.Penicillate
I know this thanks to experience. Already spend some time on this problem in my own code. Typically for a file chooser it is not contained in the hierarchy, but only opened in a dialog when neededSixtieth
Window[] wins = Window.getWindows(); instead of` Window window : frame.getOwnedWindows()` could be works for all containers that could be isDisplayable(), isDisplayable() returns containers with removed Rootpane or if DecorationsType is changed programaticallyImitable
Updating all windows returned by Window.getWindows() doesn't affect my file chooser that has not yet displayed in a dialog.Tick
@ZhaoYi Please also refer to Robin's comments: components that are cached and not part of the component hierarchy will not be updated. Yet, if your component is cached within another component which is part of the hierarchy, you could simply override updateUI, call super.updateUI and then forward the call to your cached component updateUI() method.Penicillate
One problem is that some components are created from 3rd party libraries, and I'm unable to explicitly update its UI. And do you know how to add a listener to UIManager as pointed out by Robin?Tick
@ZhaoYi Probably with UIManager.addPropertyChangeListener and watch for the change of property "lookAndFeel".Penicillate
It doesn't help if I can't get all references to components.Tick
@ZhaoYi How can't you get references to some components but yet you manage to display those components? If you are absolutely unable to reach those components, then there is not much you can do except check that, once displayed, the components use the proper L&F and if not call updateUI()/updateComponentTreeUI.Penicillate
Sure that's possible, e.g. a popup up menu created by a 3rd library, and is shown by JPopupMenu.show. In this case, how could I get the reference to that popup menu?Tick
S
6

Calling SwingUtilities.updateComponentTreeUI(mainWindow) will only update the Swing components in the Swing hierarchy under mainWindow.

If you store the JFileChooser somewhere in your code (e.g. in a field of a class) without showing the JFileChooser the chooser will not be updated by the SwingUtilities.updateComponentTreeUI(mainWindow) call. You can work around this by adding a listener to the UIManager yourself and call SwingUtilities.updateComponentTreeUI(myStoredFileChooser) from that listener when the look-and-feel is changed.

Make sure you do not create a memory leak with this, e.g. let that listener only have a WeakReference to the JFileChooser (as the lifetime of the UIManager equals the lifetime of the JVM)

Sixtieth answered 23/4, 2013 at 9:22 Comment(4)
How to add a listener to UIManager?Tick
Great job on highlighting the WeakReference.Spout
@ZhaoYi did you even bother to open the javadoc of the UIManager class. In that case I am sure you would have found the addPropertyChangeListener methodSixtieth
As I've said below, some components may be created by 3rd party libs, thus I'm unable to get their references.Tick

© 2022 - 2024 — McMap. All rights reserved.