Search code examples
javamultithreadingswingsplash-screen

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException (Panel displays before fully loaded?)


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)

Solution

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