1

We've been experimenting around with this but without luck, please note I'm probably using the components the wrong way... We've got a JFrame cointaining a JPanel, which contains a JScrollPane with another JPanel inside of it, called interiorPanel. Having a JButton with an action that adds components dynamically to the innermost panel and then calls its revalidate() method, interiorPanel grows like we need inside of the JScrollPane, showing its scroll bars like this:

the desired case

However, we want to add components with a loop instead of using a button. Using this loop:

for (int i = 1; i < 10; i++) {
    demo.getPanelInterior().add(new JButton("Hello"));
    demo.getPanelInterior().revalidate();
    demo.getPanelInterior().repaint();
}

doesn't seem to work like in the case when we click the button since the innermost panel grows, but the scroll bars don't show up, so we end with something like this:

undesired effect

What should we do to get the result from the first image? Here's the working example code:

    import java.awt.*;
    import java.awt.event.*;

    import javax.swing.*;


    public class Demo extends JPanel {

        private JPanel exteriorPanel;
        private JPanel interiorPanel;
        private JButton button;
        private JList<String> list;
        private String[] imageNames = { "Bird", "Cat", "Dog", "Rabbit", "Pig", "dukeWaveRed",
        "kathyCosmo", "lainesTongue", "left", "middle", "right", "stickerface"};
        private DefaultListModel<String> model;



        public Demo() {

            //Create the list of images and put it in a scroll pane.

            model = new DefaultListModel<>();
            for (String s : imageNames) {
                model.addElement(s);
            }

            list = new JList<String>(model);
            list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            list.setSelectedIndex(0);

            JScrollPane listScrollPane = new JScrollPane();
            listScrollPane.setViewportView(list);

            button = new JButton("Add element");
            button.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    buttonActionPerformed(e);
                }
            });

        interiorPanel = new JPanel();
        interiorPanel.add(listScrollPane);
        interiorPanel.add(button);
        interiorPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 2, 2));

        JScrollPane anotherScroll = new JScrollPane();
        anotherScroll.setViewportView(interiorPanel);

        exteriorPanel = new JPanel();
        exteriorPanel.add(anotherScroll);
        exteriorPanel.setPreferredSize(new Dimension(400, 200));

        interiorPanel.setBackground(new Color(255, 0, 0));


    }

    private void buttonActionPerformed(ActionEvent e) {
        model.addElement("Hola");
        interiorPanel.add(new JButton("Hello"));
        interiorPanel.revalidate();
    }

    //Used by SplitPaneDemo2
    public JList getImageList() {
        return list;
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {

        //Create and set up the window.
        JFrame frame = new JFrame("Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Demo demo = new Demo();
        frame.getContentPane().add(demo.getExteriorPanel());

        //Display the window.
        frame.pack();
        frame.setVisible(true);

        for (int i = 1; i < 10; i++) {
            demo.getPanelInterior().add(new JButton("Hello"));
            demo.getPanelInterior().revalidate();
            demo.getPanelInterior().repaint();
        }


    }

    public JPanel getExteriorPanel() {
        return exteriorPanel;
    }

    public JPanel getPanelInterior() {
        return interiorPanel;
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }


}

Thanks in advance

  • 2
    You need to use a layout which will restrict the growth of the scrollpane or use the `Scrollable` interface to restrict the "normal" viewable area of the inner component – MadProgrammer Aug 19 '14 at 02:21
  • I considered the idea of using `Scrollable` for it, but I don't know if it's possible to make some kind of anonymous class that implements that interface, without having to place the innermost panel in another class file. If you know any way to do that, I would be very grateful. And if there's no other way but to move the panel to another class, well, I think we shall do that –  Aug 19 '14 at 02:29

1 Answers1

2

The (main) problem is to do with the exteriorPanel's use of FlowLayout, which is allowing it to expand to meet the requirements of the JScrollPane, which will try and use the preferred size of it's contents as a base for it's own layout calculations...this is compounding to produce the result you are seeing. In fact, even when using the "click button", this will happen if you try resizing the window...

Instead, try changing the layout manager of the exteriorPanel, for example...

exteriorPanel = new JPanel(new BorderLayout());

This will force the scrollpane to meet the requirements of the parent container and then will allow the view to overflow its bounds and display the scrollbars...

Using a border layout

You should also have a read through Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Excellent thank you. By the way, what other problems did you spot with this? I'm new to swing and would like to know what's wrong so that we can learn from our mistakes and do things the correct way –  Aug 19 '14 at 02:35
  • 1
    The call to `revalidate` and `repaint` in the `for-loop` is a little overrated, you could call them outside the loop and still get the same affect... – MadProgrammer Aug 19 '14 at 02:39