0

I get a Nullpointerexception at addPropertyChangeListener when I am using Nimbus Look and Feel. When I use the Linux Standard LAF (Metal) everything works just fine.

Here is a small Testproject which simulates the problem! I have a Class which extends JPanel and provides to Content to a Frame. The Finsih button on the frame will only be enabled when some conditions are met (in this case the button2 is pressed).

This is the Mainclass:

public class Main extends JFrame {

    /**
     * 
     */
    private static final long serialVersionUID = -3120562776241721109L;
    private JPanel contentPane;
    private JButton button;
    private PropertyChangeListener changeListener;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {


        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Main frame = new Main();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public Main() {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
            System.out.println(UIManager.getLookAndFeel());
        } catch (ClassNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (InstantiationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (UnsupportedLookAndFeelException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        setContentPane(contentPane);

        button = new JButton("BUTTON");
        button.setEnabled(false);
        changeListener = new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equalsIgnoreCase("state")) {
                    button.setEnabled((boolean) evt.getNewValue());
                }
            }
        };
        contentPane.add(button, BorderLayout.SOUTH);
        Panel panel = new Panel();
        contentPane.add(panel, BorderLayout.CENTER);
        panel.addPropertyChangeListener(changeListener);
    }

Here is the Panel Class:

public class Panel extends JPanel {
    /**
     * 
     */
    private static final long serialVersionUID = -5036784576513445229L;
    private PropertyChangeSupport changes;
    private boolean state;
    /**
     * Create the panel.
     */
    public Panel() {

        this.changes = new PropertyChangeSupport(this);
        JButton button = new JButton("BUTTON2");
        add(button);
        state = false;
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                state = !state;
                changes.firePropertyChange("state", null, state);
            }
        });

    }

    @Override
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        changes.addPropertyChangeListener(listener);
    }
}

Like is said before, it works without any problem with Metal LAF

Andreas Freitag
  • 357
  • 1
  • 7
  • 20

1 Answers1

2

Looks like you override addPropertyChangeListeners and you store the listeners in an List called "changes". Since the UI is installed before the rest of your constructor is executed, your changes variable has not been initialized.

Add a null-test before adding the listener to your List and instantiate the List if needed (and do the same in your constructor as well). I can't remember if initializing the variable inline could work or not (and I don't have a JDK here to test and tell you that).

Or consider not overriding that method. Why did you do that in the first place? It looks unnecessary (but I can't tell precisely without the code).

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • Inline initialization happens after the super constructor is called if I remember correctly – Robin May 06 '12 at 15:02
  • Thanks for your answer! I dont understand how this can happen with Nimbus LAF and NOT with Metal LAF. – Andreas Freitag May 06 '12 at 15:54
  • Some L&F seem to register a PropertyChangeListener and some other may not need to do so. That's all – Guillaume Polet May 06 '12 at 16:01
  • Thanks...this was the Problem! I have created a little testproject where I try to set Nimbus before initializing the GUI, this fails! When I set LAF after GUI init it works! – Andreas Freitag May 09 '12 at 08:26
  • @GuillaumePolet Now I have the problem that i cannot set the Nimbus LAF because the NullPointerException only occurs when I open a childframe. The mainframe opens without any problems. Are there any chances to get this working? – Andreas Freitag May 11 '12 at 11:45
  • @AndreasFreitag I am pretty sure that we can get this to work. Several questions regarding your remark: 1) Is it the same NPE, or is it somewhere else? 2) Without having a reproductible case or seeing more of your code, it will be hard to debug. 3) Why do you need to override the addPropertyChangeListener()? – Guillaume Polet May 11 '12 at 12:12
  • Ive replaced the old code with the testproject i have created! – Andreas Freitag May 11 '12 at 12:24
  • @AndreasFreitag A quick solution would be to create a private lazy-loading method to create your PropertyChangeSupport. Something like `private PropertyChangeSupport getChanges() { if (changes==null)`{ changes = new PropertyChangeSupport(this);} return changes;}` and wherever you used to reference the variable `changes` replace it with `getChanges()` – Guillaume Polet May 11 '12 at 12:38
  • 1
    @AndreasFreitag the proper solution would be to get rid of your override and your changes variable. When you need to fire the property change event use: `Panel.this.firePropertyChange("state", null, state);` (from within the button ActionListener) – Guillaume Polet May 11 '12 at 12:40
  • Wow! Thanks alot! Worked on the Testproject...now i will try it on the project. – Andreas Freitag May 11 '12 at 12:42
  • @GuillaumePolet I will try that too! First the first step, your quick solution worked! Now I will try the proper solution. – Andreas Freitag May 11 '12 at 12:50