Search code examples
javaswingswingworker

Java Loading Screen isn't displaying any GUI features


I'm trying to create a basic Loading Screen in Java as part of my Computing Project. The issue is, if I was to make a new instance of it when nothing is loading then the GUI items are displayed, however, if there is something loading when i try to display the GUI then it will popup with the GUI but with no text or loading bar on it.

Here's the LoadingScreen class:

package gui;
import java.awt.*;
import javax.swing.*;
public class LoadingScreen extends Canvas
{
    private static final long serialVersionUID = 1L;
    JFrame frame = new JFrame();
    JPanel window = new JPanel();
    int width = 205;
    int height = 110;
    private JLabel progressText;
    private JProgressBar progressBar;
    private String loadingText;
    public boolean complete = false;
    public int progress = 0;
    Task task;
    public LoadingScreen(String loadingText)
    {
        this.loadingText = loadingText;
        try
        {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        frame.setIconImage(new ImageIcon("res/logo/bt/128.png").getImage());
        frame.setSize(new Dimension(width, height));
        frame.add(window);
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(false);
        window.setLayout(null);
        frame.getContentPane().add(window);
        frame.repaint();
    }
    public void setProgress(int percentage)
    {
        progress = percentage;
        frame.repaint();
    }
    public void setProgressText(String progressText)
    {
        this.progressText.setText(progressText);
        frame.repaint();
    }
    public void startLoadingScreen()
    {
        frame.setVisible(true);
        task = new Task();
        task.execute();
    }
    public void closeLoadingScreen()
    {
        setProgress(100);
        complete = true;
        frame.repaint();
        frame.setVisible(false);
    }
    class Task extends SwingWorker<Void, Void>
    {
        protected Void doInBackground() throws Exception
        {
            progressText = new JLabel(loadingText);
            Rectangle progressTextR = new Rectangle(10, 10, 180, 20);
            progressText.setBounds(progressTextR);
            progressText.setHorizontalAlignment(JLabel.CENTER);
            window.add(progressText);
            progressBar = new JProgressBar();
            Rectangle progressBarR = new Rectangle(10, 40, 180, 35);
            progressBar.setBounds(progressBarR);
            window.add(progressBar);
            frame.repaint();
            setProgress(0);
            while(!complete)
            {
                progressBar.setValue(progress);
            }
            return null;
        }
        public void done()
        {
            closeLoadingScreen();
        }
    }
}

And I generally call it in the constructor of another class as such

LoadingScreen loadingScreen = new LoadingScreen("Loading...");
loadingScreen.startLoadingScreen();
// Load some of the things I need to load.
loadingScreen.setProgress(50);
// Load the rest of the things I need to load.
loadingScreen.closeLoadingScreen();

This is the screen that is loaded if it helps...

http://i.gyazo.com/3663c9e3ac720ce191d25fe558b2956f.png

Does anyone know how to fix this? If so I would be more than grateful! :)

Thanks!


Solution

  • Your biggest problem is that your SwingWorker does not respect Swing threading since you're making Swing calls from within its doInBackground method, and your code that calls the LoadingScreen appears to be doing long-running code on the event thread. This is backwards and you need to swap these.

    This code here:

    // Load some of the things I need to load.
    loadingScreen.setProgress(50);
    // Load the rest of the things I need to load.
    

    Needs to be in the doInBackground method. Set the SwingWorker's progress property from within doInBackground, and use a PropertyChangeListener added to the SwingWorker to listen for changes to this.


    For example,

    import java.awt.BorderLayout;
    import java.awt.Font;
    import java.awt.Window;
    import java.awt.Dialog.ModalityType;
    import java.awt.event.ActionEvent;
    import java.beans.PropertyChangeEvent;
    import java.beans.PropertyChangeListener;
    import java.util.Random;
    
    import javax.swing.*;
    
    public class MyLoadingTester extends JPanel {
       public MyLoadingTester() {
          add(new JButton(new LoadingAction()));
       }
    
       private class LoadingAction extends AbstractAction {
          public LoadingAction() {
             super("Load");
          }
    
          @Override
          public void actionPerformed(ActionEvent e) {
             final MyWorker myWorker = new MyWorker();
             Window win = SwingUtilities.getWindowAncestor(MyLoadingTester.this);
             final MyLoadingDialogPanel myLoadingDialogPanel = new MyLoadingDialogPanel("Loading a Bunch of Chit");
             final JDialog dialog = new JDialog(win, "Loading", ModalityType.APPLICATION_MODAL);
             dialog.add(myLoadingDialogPanel);
             dialog.pack();
             dialog.setLocationRelativeTo(win);
    
             myWorker.addPropertyChangeListener(new PropertyChangeListener() {
    
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                   if ("progress".equals(evt.getPropertyName())) {
                      int progress = ((Integer) evt.getNewValue()).intValue();
                      myLoadingDialogPanel.setProgress(progress);
                   } else if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
                      dialog.dispose();
                   }
                }
             });
             myWorker.execute();
             dialog.setVisible(true);
          }
       }
    
       private static void createAndShowGui() {
          JFrame frame = new JFrame("TestLoadingScreen");
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          frame.getContentPane().add(new MyLoadingTester());
          frame.pack();
          frame.setLocationRelativeTo(null);
          frame.setVisible(true);
       }
    
       public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                createAndShowGui();
             }
          });
       }
    }
    
    class MyLoadingDialogPanel extends JPanel {
       private JProgressBar progressBar = new JProgressBar(0, 100);
    
       public MyLoadingDialogPanel(String text) {
          progressBar.setStringPainted(true);
    
          setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
          setLayout(new BorderLayout());
    
          JLabel label = new JLabel(text, SwingConstants.CENTER);
          label.setFont(label.getFont().deriveFont(Font.BOLD, 18f));
          add(label, BorderLayout.PAGE_START);
          add(progressBar, BorderLayout.CENTER);
       }
    
       public void setProgress(int progress) {
          progressBar.setValue(progress);
       }
    }
    
    class MyWorker extends SwingWorker<Void, Void> {
       private static final int MAX = 100;
       private int myProgress = 0;
       private Random random = new Random();
    
       @Override
       protected Void doInBackground() throws Exception {
          // TODO put long-running code here
          // Load some of the things I need to load.
          // ..... call setProgress(...) as you're doing it
          // Load the rest of the things I need to load.
    
          // as an example
          while (myProgress < MAX) {
             myProgress += random.nextInt(5) + 5;
             Thread.sleep(200);
             setProgress(Math.min(MAX, myProgress));
          }
          return null;
       }
    }