我正在编写一个 GUI Builder 并希望用户能够更改他构建的 GUI 的 LookAndFeel。LookAndFeel 应该只针对编辑器区域内的组件进行更改。应用程序的其余部分应保留在 SystemLookAndFeel 中。
最大的问题是,LookAndFeel 是作为单例实现的,并且在应用程序期间多次更改 LookAndFeel 会导致很多错误。
我开始尝试使用 Button:我尝试将 ButtonUI 设置为 MetalButtonUI,但它们没有正确渲染。所以我调试了JButton的默认paintComponent方法,发现ButtonUI仍然需要UIDefaults,因为它们是WindowsUIDefaults,所以它们并不完整。
我当前的解决方案是设置 MetalLookAndFeel,保存 UIDefaults,然后将 LookAndFeel 更改为 SystemLookAndFeel 并保存这些 UIDefaults,每次在编辑器中绘制按钮时,我都会交换 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);
}
}
正如您在此处看到的,结果非常好。左边是 MetalLaF 的外观,右边是它在我的应用程序中的呈现方式。渐变绘制正确,但边框和字体不正确。
所以我需要知道为什么不是所有的 LaF 元素都应用于 Button 以及如何解决这个问题。
-
编辑: 我找到了一个丑陋的解决方案。LookAndFeel 必须在 Button 创建之前更改,因为 Graphics 对象将在 Constructor 中创建。调用超级构造函数后,您可以将 LookAndFeel 改回来。
接下来,您需要在绘制/重新绘制组件之前更改 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。我在编辑器区域中插入了一个摇摆节点,现在可以根据需要随时修改摇摆组件的外观和感觉,而不会产生任何明显的副作用。
Rant: 如果您想修改应用程序的样式,总是可以选择 JavaFX。CSS使它尽可能简单,没有任何副作用!
非常感谢
强尼