0
votes

I've been getting this error when I press on a JButton too quickly on a JFrame, so it works if there's a slight delay between compiling the program and pressing on the button. I tried to make a splash screen/loading screen to see if I give the program more time to compile if it will work without the delay, yet thus far it still crashes if pressed too quickly. I was wondering if there is a way to pre-load it such that there doesn't need to be a delay before the button is pressed and so that the error won't exist, thanks!

(relevant pieces of code are listed, but since the entire package has a few thousand lines I'm not going to put all of it here)

This is ran in the main method.

Panels panel = new Panels(); 
panel.getWindow();

This is in a separate "Window" class

public Window(JPanel panel)
{
    super("Knight Quest");
    //loading screen
    JWindow window = new JWindow();
    window.getContentPane().add(new JLabel("Loading..."));
    window.getContentPane().add(
            new JLabel("", new ImageIcon(getClass().getResource("loading.gif")), SwingConstants.CENTER));
    window.setBounds(600, 300, 400, 300);
    window.setVisible(true);
    try 
    {
        Thread.sleep(2500);
    } catch (InterruptedException e) 
    {
        e.printStackTrace();
    }
    window.setVisible(false);
    //end of loading screen
    setLayout(new BorderLayout());
    add(panel, BorderLayout.CENTER);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(600,550);
    setLocation(525, 225);
    setVisible(true);;

}

This is at the top of the Panel class

private Window window = new Window(main());     

This is the main() JPanel that starts off the program (just adds stuff to a JPanel)

public JPanel main()
{
    JPanel main = new JPanel();
    check = "main";
    main.setBackground(lightGrey);
    Handler handles = new Handler();

    one = new JButton("Start ");
    two = new JButton("Patch Notes");
    three = new JButton("Exit   ");

    one.addActionListener(handles);
    two.addActionListener(handles);
    three.addActionListener(handles);

    image =  new ImageIcon(getClass().getResource("templar.jpg"));

    JLabel title = new JLabel("Knight Quest");
    title.setFont(new Font("Serif", Font.PLAIN, 45));
    JLabel mainPic = new JLabel(image);
    JLabel versionNum = new JLabel("v1.1.0");
    JLabel credit = new JLabel("Created and developed by Joseph Torres");

    Box panel = Box.createVerticalBox();
    panel.add(title);
    panel.add(Box.createRigidArea(new Dimension(50, 50)));
    panel.add(one);
    panel.add(Box.createRigidArea(new Dimension(50,50)));
    panel.add(two);
    panel.add(Box.createRigidArea(new Dimension(50,50)));
    panel.add(three);
    panel.add(Box.createRigidArea(new Dimension(50,150)));
    panel.add(versionNum);
    panel.add(credit);

    main.add(panel);
    main.add(mainPic);
    return main;
}

And the nullpointerexception is generated at

private class Handler implements ActionListener, KeyListener
{
    public void actionPerformed(ActionEvent event)
    {
        //First Button
        if(event.getSource() == one)
        {
            if(check.equals("main"))
            {
                window.setPanel(prologue());
            }
                    }
            }
     }

and lastly setPanel is a a method made in the windows class that is

public void setPanel(final JPanel panel) {
    SwingUtilities.invokeLater(new Runnable() {

        @Override
        public void run() {
            getContentPane().removeAll();
            add(panel, BorderLayout.CENTER);
            if(Panels.sizeCheck != 1)
            {
                setSize(600, 550);
                setLocation(525, 225);
            }
            else
            {
                setSize(1000, 600);
                setLocation(300, 100);
            }
            revalidate();
            repaint();
        }
    });
}

This is already a huge wall of text, but those are all of the important pieces that I felt were relevant. The code itself works, its just the one issue of it loading too fast or something that I may have overlooked. Let me know if you need any more pieces of code or the full program and I'll be willing to post it. Thanks!

Edit: Stack trace

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at Panels$Handler.actionPerformed(Panels.java:1228)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
2
For better help sooner, post an MCVE (Minimal Complete and Verifiable Example). - Andrew Thompson
Provide a stack trace, to locate a source of the error. - lpiepiora
Hey! I wanted to add something that was much shorter and could be pasted into a compiler, but the thing is that its a long program, and the errors trace back through a variety of methods and lines. If you have any suggestions on how to cut that down, I'll definitely edit that, thanks - finaris
Try adding the volatile qualifier to your check field. - DaoWen
Always do it in reverse order "main".equals(check) to avoid NullPointerException - Braj

2 Answers

3
votes

You should not be using Thread.sleep() to delay the loading of your panel. This blocks the UI thread and doesn't do what you want. Instead, use a Swing Timer:

public Window(final JPanel panel)
{
    super("Knight Quest");
    //loading screen
    final JWindow window = new JWindow();
    window.getContentPane().add(new JLabel("Loading..."));
    window.getContentPane().add(
            new JLabel("", new ImageIcon(getClass().getResource("loading.gif")), SwingConstants.CENTER));
    window.setBounds(600, 300, 400, 300);
    window.setVisible(true);
    javax.swing.Timer timer = new javax.swing.Timer(2500,
        new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                window.setVisible(false);
                setLayout(new BorderLayout());
                add(panel, BorderLayout.CENTER);
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                setSize(600,550);
                setLocation(525, 225);
                setVisible(true);
            }
        }
    );
    timer.setRepeats(false);
    timer.start();
}

EDIT: If the only reason for your splash screen is to give the assignment to window a chance to complete, then there's an easy way to eliminate that: separate construction of the Window object from the display of the panel. You could do something like this:

public Window() {
    super("Knight Quest");
}

public void display(JPanel panel) {
    setLayout(new BorderLayout());
    add(panel, BorderLayout.CENTER);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(600,550);
    setLocation(525, 225);
    setVisible(true);
}

Then at the top of the Panels class, replace this line:

private Window window = new Window(main());

with a simple declaration:

private Window window;

and move the assignment into the constructor for Panels:

public Panels() {
    // . . . other constructor stuff (if any)

    window = new Window();
    window.display(main());
}
0
votes

The fact that you need a delay to avoid the NullPointerException means that there's an ordering error in your code. It looks like you're just making the Window visible before all your references are set up. Instead of trying to "fix" this with a delay (treating the symptom rather than the underlying problem), you can rework the code a bit so all the references are properly initialized before making the window visible.

Here's an updated Window constructor:

public Window(JPanel panel)
{
    super("Knight Quest");
    // DELETED loading screen
    setLayout(new BorderLayout());
    add(panel, BorderLayout.CENTER);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(600,550);
    setLocation(525, 225);
    // DELETED setVisible(true);
}

and an updated Window field initializer in the other class:

private Window window = new Window(main());
// Added initializer to make the Window, then set it as visible
// You could also move this initializer code into the constructor
{ window.setVisible(true); }

Now I believe the Window should be fully constructed, and all the references should be set (including window) before the Window is made visible. That way the user doesn't have a chance to click too early.

If there's actually an issue with the window being invisible for too long, then you might want to do something like suggested in @TedHopp's answer, but rather than adding a delay you can just use invokeLater so that the window updates as soon as it's ready instead of always waiting 2.5 seconds. The fact that it's being scheduled after the current event in the UI event queue should be sufficient without any specific delay.