I want to add JPanel containers to a JScrollPane and add this scroll pane to a JFrame. But when I add multiple panels to the scroll pane this happens. The gap between the scroll pane and the top bar increases. I use BoxLayout as layout manager for all the components that I use.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class ScrollPaneGUI {
public ScrollPaneGUI() {
initGUI();
}
private void initGUI() {
JScrollPane scroll = new JScrollPane();
String title = "Title";
Border eBorder = BorderFactory.createEtchedBorder();
Border eTitledBorder = BorderFactory.createTitledBorder(eBorder, title);
JPanel panel = new JPanel();
BoxLayout boxlayout = new BoxLayout(panel, BoxLayout.PAGE_AXIS);
panel.setLayout(boxlayout);
JButton button1 = new JButton("Button1");
panel.add(Box.createRigidArea(new Dimension(0, 5)));
panel.add(button1);
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
scroll.setViewportView(panel);
scroll.setBorder(eTitledBorder);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
panel.add(Box.createRigidArea(new Dimension(0, 5)));
panel.add(new JButton("Button2"));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
scroll.updateUI();
JFrame frame = new JFrame();
frame.getContentPane().add(scroll, BorderLayout.CENTER);
frame.setSize(600, 600);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
new ScrollPaneGUI();
}
};
SwingUtilities.invokeLater(r);
}
}
This is the desired layout:
CodePudding user response:
Here is my take on laying out this GUI. Some notes:
- Rather than use a
BoxLayoutin theJScrollPaneit puts aGridLayoutin thePAGE_STARTof aBorderLayout. This is fine for when it's OK to stretch the elements in the scroll pane to the full width of the GUI. Stick to aBoxLayout(which I rarely use) or aGridBagLayoutif it's necessary to keep the elements at their preferred size. - This strategy of layout is basically 'divide and conquer' in that it starts with the smallest sub-divisions of the GUI (e.g. the
FlowLayoutfor the buttons) and then adds those containers to larger containers with different layouts and constraints (e.g. adding that button panel to theLINE_ENDof aBorderLayout- to push I to the right of the GUI) as needed for the overall effect. - I'd also consider using a
JList(using a panel for the renderer) in the scroll pane. It depends on the use as to whether that makes sense. - Note that this code is an MRE. An MRE should have everything that's needed (including imports, a class structure and the main method) for another person to compile and run the code.
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.ActionEvent;
// ref: https://stackoverflow.com/a/70934802/418556
public class ScrollPaneTestGUI {
int elementCount = 1;
JPanel elementsPanel = new JPanel(new GridLayout(0,1,2,2));
public ScrollPaneTestGUI() {
initGUI();
}
private void initGUI() {
// this will become the content pane of the frame
JPanel gui = new JPanel(new BorderLayout(4,4));
gui.setBorder(new EmptyBorder(4,4,4,4));
JPanel pageStartPanel = new JPanel(new BorderLayout(2,2));
gui.add(pageStartPanel, BorderLayout.PAGE_START);
pageStartPanel.add(new JLabel("LINE START label"), BorderLayout.LINE_START);
// default flow layout is good for this one
JPanel buttonPanel = new JPanel();
pageStartPanel.add(buttonPanel, BorderLayout.LINE_END);
buttonPanel.add(new JButton("Does Nothing"));
Action addToScrollAction = new AbstractAction("Add to scrollPane") {
@Override
public void actionPerformed(ActionEvent e) {
elementsPanel.add(getPanelForScroll());
elementsPanel.revalidate();
}
};
JButton addToScrollButton = new JButton(addToScrollAction);
buttonPanel.add(addToScrollButton);
JPanel scrollPanel = new JPanel(new BorderLayout());
scrollPanel.add(elementsPanel, BorderLayout.PAGE_START);
gui.add(new JScrollPane(scrollPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
);
for (int ii=0; ii<2; ii ) {
elementsPanel.add(getPanelForScroll());
}
JFrame frame = new JFrame("ScrollPane GUI");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setContentPane(gui);
frame.pack(); // sets the GUI to the exact size needed
frame.setMinimumSize(frame.getSize());
frame.setVisible(true);
}
private JPanel getPanelForScroll() {
JPanel p = new JPanel();
p.add(new JLabel("Panel " elementCount ));
p.setBorder(new EmptyBorder(10,200,10,200));
p.setBackground(Color.GREEN);
return p;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
new ScrollPaneTestGUI();
}
};
SwingUtilities.invokeLater(r);
}
}


