通过交换UIDefaults改变外观和感觉

时间:2015-11-09 20:31:45

标签: java swing look-and-feel uimanager uidefaults

我正在编写GUI Builder并希望用户能够更改他构建的GUI的LookAndFeel。只应为编辑器区域内的组件更改LookAndFeel。应用程序的其余部分应保留在SystemLookAndFeel中。

最大的问题是,LookAndFeel是作为Singleton实现的,并且在Application期间多次更改LookAndFeel会导致很多错误。

我开始尝试按钮: 我尝试将ButtonUI设置为MetalButtonUI,但它们没有正确呈现。所以我调试了JButton的默认paintComponent方法,看到ButtonUI仍然需要UIDefaults,因为它们是WindowsUIDefaults所以不完整。

我目前的解决方案是设置MetalLookAndFeel,保存UIDefaults,然后将LookAndFeel更改为SystemLookAndFeel并保存那些UIDefaults以及每次我在编辑器中绘制一个Button我交换UIDefaults。

以下是代码:

public class MainClass{
    public static Hashtable systemUI;
    public static Hashtable metalUI;

    public static void main(String[] args) {
         EventQueue.invokeLater(new Runnable() {
             public void run() {
                 try {
                    UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
                    metalUI = new Hashtable();
                    metalUI.putAll(UIManager.getDefaults());

                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    systemUI = new Hashtable();
                    systemUI.putAll(UIManager.getDefaults());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                /* ...
                 * Generate JFrame and other stuff
                 * ...
                 */
            }
        });
    }
}

public class MyButton extends JButton {
    public MyButton(String text) {
        super(text);
        ui = MetalButtonUI.createUI(this);
    }

    @Override public synchronized void paintComponent(Graphics g) {
        UIManager.getDefaults().putAll(Application.metalUI);

        super.paintComponent(g);

        UIManager.getDefaults().putAll(Application.systemUI);
    }
}

正如您所见here,结果非常好。左边是MetalLaF,它应该看起来在右边,它是如何在我的应用程序中呈现的。渐变绘制正确,但边框和字体不是。

所以我需要知道为什么不是LaF的所有元素都应用于Button以及如何解决这个问题。

-

修改 我找到了一个丑陋的解决方案。必须在创建Button之前更改LookAndFeel,因为将在构造函数中创建Graphics对象。调用超级构造函数后,您可以更改LookAndFeel。

接下来,您需要在绘制/重新绘制Component之前更改LookAndFeel。我得到它的唯一一点就是在调用super之前的paintComponent中。你可以在调用super之后将其更改回来。

代码:

import javax.swing.*;
import javax.swing.plaf.metal.MetalButtonUI;
import java.awt.*;

public class MainClass {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception e) {
                    e.printStackTrace();
                }

                JFrame f = new JFrame("Test");
                f.setDefaultCloseOperation(f.getExtendedState() | JFrame.EXIT_ON_CLOSE);

                try {
                    UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
                } catch (Exception ex) {
                }
                f.setLayout(new FlowLayout());

                f.add(new MyButton("MetalButton"));
                f.add(new JButton("SystemButton"));

                f.pack();
                f.setVisible(true);
            }
        });
    }
}

class MyButton extends JButton {
    public MyButton(String text) {
        super(text);
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
        }
        ui = MetalButtonUI.createUI(this);
    }

    @Override public synchronized void paintComponent(Graphics g) {
        try {
            UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());

            super.paintComponent(g);

            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
        }
    }
}

编辑2: 除非绝对必要,否则不要使用此功能!

这非常不稳定。经过大量的测试后,当我换掉UIDefaults而不是整个LookAndFeel时,我发现它不那么多了,但是我不建议做任何这些。

编辑3: 我发现最好的解决方案是使用JavaFX作为GUI。我将一个摆动节点插入编辑器区域,现在可以根据需要随时修改摆动组件的外观,而没有任何明显的副作用。

咆哮: 如果要修改应用程序的样式,可以随时选择JavaFX。 CSS使它尽可能简单,没有任何副作用!

非常感谢

的Jhonny

1 个答案:

答案 0 :(得分:3)

Disclarimer

Swing的外观和感觉并非设计为在首次初始化后进行切换,它实际上是一种不可避免的副作用。一些外观和感觉,一些组件可能不喜欢你这样做,并且在正常情况下可能不会像其他人那样表现。

可能的解决方案

为了热爱理智,不要在paintComponent方法中更改UI默认值(不要在任何jQuery(document).ready(function (jQuery) { var screenWidth = (screen.width-400)/2, screenHeight = (screen.height-400)/2; jQuery('.open-window').click(function () { var NWin = window.open(jQuery(this).prop('href'), '', "'scrollbars=1, height=400, width=400, top="+screenHeight+", left="+screenWidth+"'"); if (window.focus) { NWin.focus(); } return false; }); }); 方法中改变UI的状态EVER,绘画仅绘制当前状态) ,这只是要求无尽的麻烦。

相反,必要时使用paintUIManager.setLookAndFeel(,,,)并传入最顶级的容器

例如......

I have a bad feeling about this

SwingUtiltiies#updateComponentTreeUI