3

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

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

4 回答 4

5

作为一般规则,混合 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 来处理激活/停用事件。

于 2013-08-25T03:41:16.180 回答
3

问题源于尝试在静态块中进行 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);
    }
}
于 2013-08-25T01:55:37.580 回答
2

LAFs can't be mixed in the general case, they are designed such there is exactly one for all components in any application at any given time. So the outcome of mixing is simply undefined - you might or might not get away with it in a concrete context, but be prepared for unexpected visual and feel artifacts.

An example for visual artefacts (it's done in SwingX testing infrastructure, simple enough to write it out - but I'm too lazy ;-) - open the optionPane, than move it around: you'll see Nimbus striping colors appear more or less unpredictably

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

The technical reason is that the ui-delegates may access properties stored in the UIManager at any time: mostly, they configure the component's properties from those stored in the UIManager at instantiation time and after that access those properties from the component. Occasionally though, they access the UIManager directly .. thus leading to unpredictable artefacts.

于 2013-08-25T09:06:52.717 回答
0

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

private void changeLookAndFeel() {
        final LookAndFeelInfo[] list = UIManager.getInstalledLookAndFeels();
        //Look And Feels available 


            final List<String> lookAndFeelsDisplay = new ArrayList<>();
            final List<String> 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...
            }
    }
于 2015-07-04T00:46:54.753 回答