Java 更改特定窗口的外观和感觉

Java 更改特定窗口的外观和感觉,java,swing,nullpointerexception,look-and-feel,Java,Swing,Nullpointerexception,Look And Feel,我正在为一个更大的GUI应用程序编写脚本。主应用程序窗口使用系统的LookAndFeel,但我希望脚本的GUI使用NimbusLookAndFeel。创建GUI后,我想将LookAndFeel设置回原始设置。我觉得下面的方法应该可以工作,但是当使用我的组件对象时,我得到了一个NullPointerException import java.awt.Dimension; import java.awt.GridBagLayout; import javax.swing.*; import java

我正在为一个更大的GUI应用程序编写脚本。主应用程序窗口使用系统的
LookAndFeel
,但我希望脚本的GUI使用Nimbus
LookAndFeel
。创建GUI后,我想将
LookAndFeel
设置回原始设置。我觉得下面的方法应该可以工作,但是当使用我的
组件
对象时,我得到了一个
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);
    }
}

问题源于试图在静态块中进行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不是一个好主意。这个问题就是一个例子

灵光LAF中有些东西可能不允许你这么做。按原样运行代码。它将LAF设置为
系统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来处理激活/停用的事件。

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委托可以随时访问存储在UIManager中的属性:大多数情况下,他们从实例化时存储在UIManager中的属性配置组件的属性,然后从组件访问这些属性。不过,偶尔他们会直接访问UIManager。。因此会导致不可预测的人工制品。

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

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...
            }
    }
private void changeLookAndFeel(){
final LookAndFeelInfo[]list=UIManager.getInstalledLookAndFeels();
//外观和感觉可用
最终列表lookAndFeelsDisplay=新建ArrayList();
最终列表lookAndFeelsRealNames=new ArrayList();
for(LookAndFeelInfo-each:list){
lookAndFeelsDisplay.add(each.getName());//每个可用外观的简化名称
lookAndFeelsRealNames.add(each.getClassName());//类名
}
如果(lookAndFeelsDisplay.size()!=lookAndFeelsRealNames.size()){
抛出新的InternalError();//不应该发生,冗余
}
String changeSpeed=(String)JOptionPane.showInputDialog(此“选择此处的外观\n(这些在您的系统上都可用):”,“选择外观”,JOptionPane.QUESTION\u消息,null,lookAndFeelsDisplay.toArray(),null);
布尔更新=false;
if(changeSpeed!=null&&changeSpeed.length()>0){
对于(int a=0;a
我不再经历运行时异常,但我的窗口的外观和感觉不是Nimbus。由于组件是在LookAndFeel不是Nimbus时创建的,因此它们不会显示为Nimbus。我最初使用静态块来确保在创建组件之前将Nimbus设置为LookAndFeel。您能展示一个更好地代表实际问题的SSCCE吗?另外,如果已经有一个框架,这个GUI应该是一个对话框。例如,可以很好地看到
GUI
(很模糊)-这是您的GUI还是主GUI?你提到了多个“窗口”,但是你的SSCCE只生成一个窗口。我没有为主应用程序编写代码。我正在写一个插件。所以我引用的唯一窗口是我的插件。“我没有为主应用程序编写代码”也许不是,但“主应用程序”只是额外的几行代码。这应该在SSCCE中。我正在考虑切换到JDialog。那么,我会添加一个窗口侦听器并在闭包时更改LAF吗?如果您使用的是模态JDialog,那么可以尝试一下。再说一遍,这只是我的猜测。