改变特定窗口的外观

我正在为更大的GUI应用程序编写脚本。 主应用程序窗口使用系统的LookAndFeel ,但我希望我的脚本的GUI使用Nimbus LookAndFeel 。 创建GUI后,我想将LookAndFeel设置回原始版本。 我觉得下面的SSCCE应该可以工作,但是在使用我的Component对象时我得到一个NullPointerException

 import java.awt.Dimension; import java.awt.GridBagLayout; import javax.swing.*; import javax.swing.UIManager.LookAndFeelInfo; public class GUI extends JFrame { private static LookAndFeel originalLookAndFeel = UIManager.getLookAndFeel(); static { System.out.println("At start, look and feel is " + UIManager.getLookAndFeel().getName()); try { setNimbusLookAndFeel(); } catch (Exception e) { System.out.println(e.getMessage()); } System.out.println("Look and feel changed to " + UIManager.getLookAndFeel().getName() + " before component creation"); } private GridBagLayout gridBag = new GridBagLayout(); private JTabbedPane tabs = new JTabbedPane(); private JPanel selectionPanel = new JPanel(gridBag); private JPanel infoPanel = new JPanel(gridBag); private JPanel settingsPanel = new JPanel(gridBag); public GUI() { setWindowProperties(); setUpComponents(); addComponents(); try { System.out.println("Setting to original, which is " + originalLookAndFeel.getName()); UIManager.setLookAndFeel(originalLookAndFeel); System.out.println("Current look and feel is " + UIManager.getLookAndFeel().getName()); } catch (UnsupportedLookAndFeelException e) { e.printStackTrace(); } } private void setWindowProperties() { setLayout(gridBag); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(new Dimension(700, 600)); setTitle("fAmos Quester"); setResizable(false); setLocationRelativeTo(null); } private static void setNimbusLookAndFeel() { try { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); } } } catch (Exception e) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e2) { } } } public void setUpComponents() { tabs.addTab("Quest selection", selectionPanel); tabs.addTab("Quest info", infoPanel); tabs.addTab("Settings", settingsPanel); selectionPanel.setPreferredSize(new Dimension(650, 500)); infoPanel.setPreferredSize(new Dimension(650, 500)); settingsPanel.setPreferredSize(new Dimension(650, 500)); } private void addComponents() { add(tabs); } public static void main(String[] args) { new GUI().setVisible(true); } } 

作为一般规则,混合LAF并不是一个好主意。 这个问题就是为什么的一个例子。

Nimbus LAF中有些东西可能不允许你这样做。 按原样运行代码。 它将LAF设置为System LAF ,然后重置LAF。 在我的情况下,系统是Windows,它似乎工作正常。 然后更改代码以使用Nimbus LAF。 它似乎最初工作,但尝试调整框架大小,你会得到错误。 因此,看起来Nimbus帧不能完全独立于当前的LAF。

 import java.awt.*; import java.awt.event.*; import java.awt.GridBagLayout; import javax.swing.*; import javax.swing.UIManager.LookAndFeelInfo; public class GUI2 extends JFrame { private static LookAndFeel originalLookAndFeel = UIManager.getLookAndFeel(); /* private GridBagLayout gridBag = new GridBagLayout(); private JTabbedPane tabs = new JTabbedPane(); private JPanel selectionPanel = new JPanel(gridBag); private JPanel infoPanel = new JPanel(gridBag); private JPanel settingsPanel = new JPanel(gridBag); */ private GridBagLayout gridBag; private JTabbedPane tabs; private JPanel selectionPanel; private JPanel infoPanel; private JPanel settingsPanel; public GUI2() { System.out.println("At start, look and feel is " + UIManager.getLookAndFeel().getName()); try { // setNimbusLookAndFeel(); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } System.out.println("Look and feel changed to " + UIManager.getLookAndFeel().getName() + " before component creation"); gridBag = new GridBagLayout(); setLayout(gridBag); tabs = new JTabbedPane(); selectionPanel = new JPanel(gridBag); infoPanel = new JPanel(gridBag); settingsPanel = new JPanel(gridBag); setUpComponents(); addComponents(); setWindowProperties(); Action reset = new AbstractAction() { public void actionPerformed(ActionEvent ae) { try { System.out.println("Setting to original, which is " + originalLookAndFeel.getName()); UIManager.setLookAndFeel(originalLookAndFeel); System.out.println("Current look and feel is " + UIManager.getLookAndFeel().getName()); } catch (UnsupportedLookAndFeelException e) { //e.printStackTrace(); System.out.println(e.getMessage()); } } }; Timer timer = new Timer(500, reset); timer.setRepeats(false); timer.start(); } private void setWindowProperties() { // setLayout(gridBag); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setTitle("fAmos Quester"); // setResizable(false); pack(); setLocationRelativeTo(null); } private void setNimbusLookAndFeel() { try { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); } } } catch (Exception e) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e2) { } } } public void setUpComponents() { tabs.addTab("Quest selection", selectionPanel); tabs.addTab("Quest info", infoPanel); tabs.addTab("Settings", settingsPanel); selectionPanel.setPreferredSize(new Dimension(650, 500)); infoPanel.setPreferredSize(new Dimension(650, 500)); settingsPanel.setPreferredSize(new Dimension(650, 500)); } private void addComponents() { add(tabs); } public static void main(String[] args) { new GUI2().setVisible(true); } } 

也许解决方案是使用Nimbus LAF创建组件,如上所述。 但是,在取消激活帧之前,请勿重置LAF。 然后,您可以尝试在每次激活帧时重置LAF。 您将使用WindowListener来处理激活/停用的事件。

问题源于尝试在静态块中进行PLAF更改。 将它移动到构造函数,它的工作原理。

 import java.awt.Dimension; import java.awt.GridBagLayout; import javax.swing.*; import javax.swing.UIManager.LookAndFeelInfo; public class GUI extends JFrame { private static LookAndFeel originalLookAndFeel = UIManager.getLookAndFeel(); private GridBagLayout gridBag = new GridBagLayout(); private JTabbedPane tabs = new JTabbedPane(); private JPanel selectionPanel = new JPanel(gridBag); private JPanel infoPanel = new JPanel(gridBag); private JPanel settingsPanel = new JPanel(gridBag); public GUI() { System.out.println("At start, look and feel is " + UIManager.getLookAndFeel().getName()); try { setNimbusLookAndFeel(); } catch (Exception e) { e.printStackTrace(); } System.out.println("Look and feel changed to " + UIManager.getLookAndFeel().getName() + " before component creation"); setWindowProperties(); setUpComponents(); addComponents(); try { System.out.println("Setting to original, which is " + originalLookAndFeel.getName()); UIManager.setLookAndFeel(originalLookAndFeel); System.out.println("Current look and feel is " + UIManager.getLookAndFeel().getName()); } catch (UnsupportedLookAndFeelException e) { //e.printStackTrace(); System.out.println(e.getMessage()); } } private void setWindowProperties() { setLayout(gridBag); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(new Dimension(700, 600)); setTitle("fAmos Quester"); setResizable(false); setLocationRelativeTo(null); } private void setNimbusLookAndFeel() { try { for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); } } } catch (Exception e) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e2) { } } } public void setUpComponents() { tabs.addTab("Quest selection", selectionPanel); tabs.addTab("Quest info", infoPanel); tabs.addTab("Settings", settingsPanel); selectionPanel.setPreferredSize(new Dimension(650, 500)); infoPanel.setPreferredSize(new Dimension(650, 500)); settingsPanel.setPreferredSize(new Dimension(650, 500)); } private void addComponents() { add(tabs); } public static void main(String[] args) { new GUI().setVisible(true); } } 

LAF在一般情况下不能混合,它们的设计使得在任何给定时间任何应用中的所有组件都只有一个。 因此,混合的结果是未定义的 – 您可能会或可能不会在具体的上下文中使用它,但要为意外的视觉和感觉做法做好准备。

视觉假象的一个例子(它在SwingX测试基础设施中完成,简单到写出来 – 但我太懒了;-) – 打开optionPane,而不是移动它:你会看到Nimbus条纹颜色或多或少出现不可预测的

 setLAF("Metal"); final JTable table = new JTable(new AncientSwingTeam()); JXFrame frame = wrapWithScrollingInFrame(table, "Metal-base"); Action action = new AbstractAction("show dialog") { @Override public void actionPerformed(ActionEvent e) { setLAF("Nimbus"); JOptionPane.showMessageDialog(table, "dummy - we are Nimbus!"); setLAF("Metal"); } }; addAction(frame, action); show(frame); 

技术原因是ui-delegates可以随时访问存储在UIManager中的属性:大多数情况下,它们在实例化时从存储在UIManager中的属性配置组件的属性,然后从组件访问这些属性。 有时他们会直接访问UIManager ..因此导致不可预测的文物。

此解决方案假定您要更改“此”特定窗口的外观设置(一个小的私有帮助程序方法)。 我使用了一个对话窗口来获取用户的输入(您可以自己重新设计它以将其隐藏起来并在需要时进行更改)。 当然,为了清晰起见,这有点长,并且可以很容易地缩短。

 private void changeLookAndFeel() { final LookAndFeelInfo[] list = UIManager.getInstalledLookAndFeels(); //Look And Feels available final List lookAndFeelsDisplay = new ArrayList<>(); final List lookAndFeelsRealNames = new ArrayList<>(); for (LookAndFeelInfo each : list) { lookAndFeelsDisplay.add(each.getName()); //simplified name of each available look and feel lookAndFeelsRealNames.add(each.getClassName()); //class name } if (lookAndFeelsDisplay.size() != lookAndFeelsRealNames.size()) { throw new InternalError(); //should never happen, redundant } String changeSpeed = (String) JOptionPane.showInputDialog(this, "Choose Look and Feel Here\n(these are all available on your system):", "Choose Look And Feel", JOptionPane.QUESTION_MESSAGE, null, lookAndFeelsDisplay.toArray(), null); boolean update = false; if (changeSpeed != null && changeSpeed.length() > 0) { for (int a = 0; a < lookAndFeelsDisplay.size(); a++) { if (changeSpeed.equals(lookAndFeelsDisplay.get(a))) { try { UIManager.setLookAndFeel(lookAndFeelsRealNames.get(a)); //reads the identical class name at the corresponding index position. this.whichLookAndFeel = changeSpeed; update = true; break; } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { err.println(ex); ex.printStackTrace(); Logger.getLogger(MyClass.class.getName()).log(Level.SEVERE, null, ex); } } } } if (update) { // make updates here... } }