setVisible()和正在绘制的组件之间会发生什么?

时间:2014-03-13 18:31:22

标签: java swing visibility

我有一个JScrollPane,其中包含JPanel,其中包含一些自定义组件,这些组件是JPanel的扩展(它们都是相同的类型)。在显示包含JFrame之前,此JScrollPane设置为不可见。

当用户选择JCheckBox时,我使用setVisible()来显示滚动窗格。第一次显示窗格时,在调用setVisible()和绘制窗格之间存在显着延迟。如果用户取消选中并重新选中复选框,则会立即绘制窗格。

我的自定义组件已经构建并添加到面板中。它们是ComponentListener但除了componentResized()之外有空实现。它们也有一个自定义paintComponent()方法,但该方法中的断点显示延迟发生在调用它之前,因此它不是慢速绘制的情况。

JScrollPane或JPanel都没有任何其他侦听器。 setVisible()paintComponent()之间还会发生什么?我还能在哪里确定延迟的来源?


修改 我试图根据MCVE的建议创建和Kevin Workman,我发现我的问题与我的想法略有不同。似乎没有其他任何事情发生滚动窗格将永远不会被绘制。

在我的最小例子中(见下文),在我调整框架大小之前,该项目不会显示。在初始显示后,setVisible()会立即应用,如同完整程序一样。以下是演示此问题的示例代码:

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

}

现在的问题是,调整大小期间发生了什么导致setVisible()生效?如何让它立即发生?

2 个答案:

答案 0 :(得分:4)

  

现在问题是,在调整大小期间发生了什么

调整框架大小时,将调用布局管理器,以使滚动窗格的大小从(0,0)更改为适当的值。

  

我怎样才能让它立即发生?

在可见GUI中添加/删除组件时,需要使用revalidate()和repaint()父容器。我知道你没有添加组件,但效果是一样的,因为它没有有效的大小。

// scrollPane.revalidate();
// scrollPane.repaint();
// VisibilityDelayExample.this.invalidate();
// VisibilityDelayExample.this.repaint();
contentPane.revalidate();
contentPane.repaint();

答案 1 :(得分:2)

Conversely,您的示例的此变体会覆盖滚动窗格的getPreferredSize()方法以提供任意大小。该复选框使用ItemListener来切换可见性。请注意使用pack(),其中&#34;使Window的大小适合其子组件的首选大小和布局。&#34;

附录:此更新使滚动窗格最初不可见,并调整框架的大小以适应。

image

public class VisibilityDelayExample extends JFrame {

    private JCheckBox chckbxAdvancedView;
    private JScrollPane scrollPane;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                VisibilityDelayExample frame = new VisibilityDelayExample();
                frame.setVisible(true);
            }
        });
    }

    public VisibilityDelayExample() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout(0, 0));
        chckbxAdvancedView = new JCheckBox("Advanced View");
        chckbxAdvancedView.addItemListener(new ItemListener() {

            @Override
            public void itemStateChanged(ItemEvent e) {
                System.out.println(e);
                scrollPane.setVisible(chckbxAdvancedView.isSelected());
            }
        });
        add(chckbxAdvancedView, BorderLayout.NORTH);
        JPanel panel = new JPanel(new GridLayout(0, 1));
        for (int j = 0; j < 100; j++) {
            panel.add(new JLabel("Label " + j));
        }
        scrollPane = new JScrollPane(panel) {

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(450, 300);
            }
        };
        add(scrollPane, BorderLayout.CENTER);
        pack();
        scrollPane.setVisible(false);
        setSize(scrollPane.getPreferredSize());
    }
}