简体   繁体   English

更改特定窗口的外观

[英]Changing look and feel of specific window

I'm writing a script for a larger GUI application. 我正在为更大的GUI应用程序编写脚本。 The main application window uses the system's LookAndFeel , but I want my script's GUI to use the Nimbus LookAndFeel . 主应用程序窗口使用系统的LookAndFeel ,但是我希望脚本的GUI使用Nimbus LookAndFeel After GUI creation, I want to set the LookAndFeel back to the original. 创建GUI后,我想将LookAndFeel设置回原始状态。 I feel the below SSCCE should work, but I'm getting a NullPointerException when using my Component objects. 我觉得下面的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);
    }
}

As a general rule it is not a good idea to mix LAF's. 一般来说,混合LAF并不是一个好主意。 This problem is an example of why. 这个问题就是一个例子。

There is something in the Nimbus LAF that may not allow you to do this. Nimbus LAF中有某些内容可能不允许您执行此操作。 Run the code as is. 按原样运行代码。 It will set the LAF to the System LAF and then reset the LAF. 它将LAF设置为System LAF ,然后重置LAF。 In my case the system is Windows and it appear to work fine. 在我的情况下,该系统是Windows,并且运行正常。 Then change the code to use the Nimbus LAF. 然后更改代码以使用Nimbus LAF。 It appears to work initially, but try resizing the frame and you get the errors. 它最初似乎可以正常工作,但是尝试调整框架的大小会出现错误。 So it would appear that the Nimbus frame does not work completely independent of the current 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);
    }
}

Maybe a solution would be to create the component using the Nimbus LAF as is done above. 也许一种解决方案是像上面那样使用Nimbus LAF创建组件。 However, don't reset the LAF until the frame is deactivated. 但是,在停用框架之前,请勿重置LAF。 Then you could try resetting the LAF every time the frame is activated. 然后,您可以尝试在每次激活框架时重置LAF。 You would use a WindowListener to handle the activated/deactivated events. 您将使用WindowListener来处理已激活/已禁用的事件。

The problem originates from attempting to do the PLAF change in a static block. 问题源于尝试在静态块中进行PLAF更改。 Move it to the constructor and it works. 将其移至构造函数,即可正常工作。

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

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. LAF通常不能混合使用,它们的设计使得在任何给定时间任何应用程序中的所有组件都只有一个。 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 视觉人工制品的示例(在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);

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. 技术原因是ui代理可以随时访问UIManager中存储的属性:大多数情况下,它们在实例化时从UIManager中存储的属性中配置组件的属性,然后再从组件中访问这些属性。 Occasionally though, they access the UIManager directly .. thus leading to unpredictable artefacts. 但是,有时它们会直接访问UIManager,从而导致无法预测的伪像。

This solution assumes that you are going to change the Look and Feel setting for "this" particular window (a little private helper method). 此解决方案假定您将更改“此”特定窗口的外观设置(一个小私有帮助器方法)。 I used a dialog window to get input from the user (you could re engineer this yourself to hide it from the user and make the change happen when you need it). 我使用了一个对话框窗口来获取用户的输入(您可以自己对其进行重新设计,以使其对用户隐藏,并在需要时进行更改)。 Of course this is a little long for clarity, and can be easily shortened. 当然,为了清楚起见,这有点长,可以很容易地缩短。

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...
            }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM