如何在运行时更改Swing应用程序的外观和感觉?

5
我知道有一个SwingUtilities.updateComponentTreeUI(Component c)方法,但它不完美。例如,如果我有一个JFileChooser并且当前的外观是Windows,然后我用SwingUtilities.updateComponentTreeUI(mainWindow)将外观更改为Nimbus,主窗口的样式会正确更改,但是当我使用JFileChooser.showOpenDialog(Component parent) 方法显示文件选择器时,它仍然保持Windows的外观。如果我使用JPopupMenu.show(Component invoker, int x, int y)方法显示弹出对话框,也会发生同样的情况。

这个问题有什么解决办法吗?


请明确一下,您是否也使用了UIManager.setLookAndFeel()来调整当前的L&F? - Duncan Jones
2个回答

7

假设value是新外观的类名,以下代码片段可用于更新所有窗口和子组件:

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);
}

如果您在自己的代码中缓存了不属于Swing层次结构的Swing组件,则这将没有帮助。 - Robin
@Robin 确实,对于缓存组件来说是个不错的发现。但是,如果它嵌入到另一个组件中,即层次结构中,另一个选项是覆盖 updateUI,调用 super.updateUI 并将调用转发到文件选择器。 - Guillaume Polet
1
我知道这是因为经验。在我的代码中已经花了一些时间解决这个问题。通常情况下,文件选择器不包含在层次结构中,而只有在需要时才在对话框中打开。 - Robin
通过 Window.getWindows() 返回的所有窗口进行更新,不会影响尚未在对话框中显示的文件选择器。 - Zhao Yi
@ZhaoYi 请参考Robin的评论:被缓存且不属于组件层次结构的组件将不会被更新。但是,如果您的组件被缓存在层次结构中的另一个组件中,您可以简单地覆盖updateUI,调用super.updateUI,然后将调用转发到您缓存的组件的updateUI()方法。 - Guillaume Polet
显示剩余6条评论

6
调用SwingUtilities.updateComponentTreeUI(mainWindow)只会更新mainWindow下的Swing组件树。
如果您在代码中存储了JFileChooser(比如存储在类的字段中)而没有显示它,那么SwingUtilities.updateComponentTreeUI(mainWindow)调用将不会更新该选择器。您可以通过向UIManager添加监听器并在外观发生更改时从该监听器调用SwingUtilities.updateComponentTreeUI(myStoredFileChooser)来解决这个问题。
请确保您不会因此创建内存泄漏,例如,只让监听器对JFileChooser具有WeakReference(因为UIManager的生命周期等同于JVM的生命周期)。

如何向UIManager添加监听器? - Zhao Yi
在突出显示“WeakReference”方面做得非常好。 - Duncan Jones
@ZhaoYi 你有没有打开过 UIManager 类的 javadoc。如果是这样,我相信你会发现 addPropertyChangeListener 方法。 - Robin
正如我下面所说,有些组件可能是由第三方库创建的,因此我无法获取它们的引用。 - Zhao Yi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接