jScrollPane can't add component

Feanaro picture Feanaro · Jan 4, 2013 · Viewed 14.1k times · Source

I have a jScrollPane and a button on a form. The button adds a component to the jScrollPane. I'm using a FlowLayout with a center alignment to arrange the components within the jScrollPane.

The first component has no problems appearing and is aligned perfectly. When I then hit the button again, nothing seems too happen. When I follow the debugger it shows that everything happens precisely as before.

The code that's being executed when the button is clicked:

jScrollPane.getViewport().add(new Component());

This is how I setup the FlowLayout on the Viewport of the jScrollPane:

jScrollPane.getViewport().setLayout(new FlowLayout(FlowLayout.CENTER));

Answer

MadProgrammer picture MadProgrammer · Jan 4, 2013

You're mixing heavy weight (AWT) components with light weight (Swing) components, this is inadvisable as they don't tend to play well together.

JScrollPane contains a JViewPort onto which you can add a child component, AKA the view.

enter image description here

(image from the JavaDocs)

So the call jScrollPane.getViewport().setLayout(new FlowLayout(FlowLayout.CENTER)); is actually setting the JViewPort's layout manager, which really isn't advisable.

What you should do is create the component you want to add to the scrollpane, set it's layout and add all it's child components to it and then add it to the scroll pane. You can add components to the "view" at later stage if you want, but that's up to you...

// Declare "view" as a class variable...
view = new JPanel(); // FlowLayout is the default layout manager
// Add the components you need now to the "view"
JScrollPane scrollPane = new JScrollPane(view);

Now you can add new components to the view as you need...

view.add(...);

If you don't want to maintain a reference to view, you can access it by calling JViewport#getView which will return the component been managed by the view port.

JPanel view = (JPanel)scrollPane.getViewPort().getView();

Working Example

This works fine for me...

nb - I added view.validate() to my code, which you may not have had, after I added a new component...

public class TestScrollPane01 {

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

    public TestScrollPane01() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new MainPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MainPane extends JPanel {

        private JScrollPane scrollPane;
        private int count;

        public MainPane() {
            setLayout(new BorderLayout());
            scrollPane = new JScrollPane(new JPanel());
            ((JPanel)scrollPane.getViewport().getView()).add(new JLabel("First"));
            add(scrollPane);

            JButton add = new JButton("Add");
            add.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    JPanel view = ((JPanel)scrollPane.getViewport().getView());
                    view.add(new JLabel("Added " + (++count)));
                    view.validate();
                }
            });

            add(add, BorderLayout.SOUTH);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

}