Skip to content
Advertisement

What happens between setVisible() and a component being painted?

I have a JScrollPane that contains a JPanel which then contains some custom components which are an extension of JPanel (they are all the same type). This JScrollPane is set invisible before the containing JFrame is shown.

When the user selects a JCheckBox I use setVisible() to show the scroll pane. The first time the pane is shown however there is a significant delay between when setVisible() is called and when the pane is painted. If the user un-selects and re-selects the check box the pane is painted instantly.

My custom components have already been constructed and added to the panel. They are ComponentListeners but have empty implementations except for componentResized(). They also have a custom paintComponent() method, but a breakpoint inside that method shows the delay happens before the this is called so it is not a case of a slow painting.

Neither the JScrollPane or the JPanel have any other listeners. What else happens between setVisible() and paintComponent()? Where else could I look to determine the source of the delay?


EDIT: In an attempt to create and MCVE at the suggestion of Kevin Workman I discovered my issue is slightly different than I thought. It seems that without anything else happening the scroll pane will never be drawn.

In my minimal example (see below) the item will not be shown until I resize my frame. After the initial display though setVisible() applies immediately as in the full program. Here is the example code that demonstrates the problem:

public class VisibilityDelayExample extends JFrame {

    private JPanel contentPane;
    private JCheckBox chckbxAdvancedView;
    private JScrollPane scrollPane;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    VisibilityDelayExample frame = new VisibilityDelayExample();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public VisibilityDelayExample() {
        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);

        chckbxAdvancedView = new JCheckBox("Advanced View");
        chckbxAdvancedView.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent arg0) {
                System.err.println("Property Changed");
                if(chckbxAdvancedView.isSelected() != scrollPane.isVisible()){
                    scrollPane.setVisible(chckbxAdvancedView.isSelected());
                    scrollPane.invalidate();
                    scrollPane.repaint();
                    VisibilityDelayExample.this.invalidate();
                    VisibilityDelayExample.this.repaint();
                }
            }
        });
        contentPane.add(chckbxAdvancedView, BorderLayout.NORTH);

        scrollPane = new JScrollPane();
        scrollPane.setVisible(false);
        contentPane.add(scrollPane, BorderLayout.CENTER);

        JPanel panel = new JPanel();
        scrollPane.setViewportView(panel);
        for(int j = 0; j < 100;j++){
            panel.add(new JLabel("Label " + j));
        }
    }

}

Now the question is, what is happening during a resize that is causing setVisible() to take effect and how can I cause it to happen instantly?

Advertisement

Answer

Now the question is, what is happening during a resize that is causing

When you resize the frame the layout manager is being called so the size of the scrollpane is changed from (0, 0) to an appropriate value.

how can I cause it to happen instantly?

When you add/remove components from a visible GUI you need to use revalidate() and repaint() on the parent container. I know you are not adding the component, but the effect is the same since it doesn’t have a valid size.

// scrollPane.revalidate();
// scrollPane.repaint();
// VisibilityDelayExample.this.invalidate();
// VisibilityDelayExample.this.repaint();
contentPane.revalidate();
contentPane.repaint();
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement