Search code examples
javaswingjwindow

Inconsistent outcome when using JWindow


I'm writing this program to display JProgressbar in JWindow. But running this program in same machine without making any changes shows different results each time. Most of the time it shows empty JWindow without anything inside. Other times it shows how I expect it to appear. I don't know what is wrong.

I have tried using JFrame. Then it works perfectly all the time. But I want to use JWindow.

Here is my code :

package des;

import javax.swing.*;
import java.awt.*;

public class Test extends JWindow {
    JPanel panel = new JPanel();
    JLabel messageLabel = new JLabel();
    JProgressBar progressBar = new JProgressBar(0, 100);
    Test() {
        setVisible(true);
        setSize(480, 100);
        setLocationRelativeTo(null);//put it in center of screen

        messageLabel.setText("Hello World");
        messageLabel.setAlignmentX(JLabel.CENTER);
        progressBar.setValue(0);

        panel.setLayout(new BorderLayout());
        panel.add(messageLabel, BorderLayout.CENTER);
        panel.add(progressBar, BorderLayout.SOUTH);
        panel.setBackground(Color.cyan);
        add(panel,BorderLayout.CENTER);



    }

    public static void main(String[] args) {
        new Test();
    }
}

I am running in Windows 10 64bit in intelliJ. Here is my java -version :

openjdk version "12.0.1" 2019-04-16
OpenJDK Runtime Environment (build 12.0.1+12)
OpenJDK 64-Bit Server VM (build 12.0.1+12, mixed mode, sharing)

Solution

  • Calling setVisible(true) will trigger the Event Dispatch Thread (EDT). Once running, every calls updating UI should be run inside the EDT, e.g. by calling SwingUtilities.invokeLater().

    There are some exceptions to that rule, noticeably calling UI updates in constructors when the object is not visible (hence not rendered). As soon as it becomes visible, such calls should be deferred to EDT.

    So you should layout you GUI in your constructor, and call setVisible(true) only at the end, once everything is ready to be rendered.

    As pointed out by @camickr, all UI object buildings should be deferred to EDT as well. Not doing that might lead to undefined behaviour (i.e. it may or may not work, as it is not specified by the JSR)

    Regarding the why it didn't show, I'm again citing @camickr's comment:

    The reason the code doesn't work is because components are added after the GUI is visible. By default components have a default size of (0, 0). So there is nothing to paint. If you add components to a visible GUI then you need to revalidate() and repaint() the Container the component was added to so the layout manager can be invoked.