问题
I would like to create a panel, to which I can dynamically add sub-panels with fixed height. I tried using a glue component, but it does not work. I would like to achieve that the sub-panels are visible at the top of the gridbaglayout. Side problem is that when I keep adding sub-panels, they start to overlap because the JScrollPane isn't adjusting. However, when I resize the frame, both problems are solved.
At this moment I don't see where I went wrong. Why does the glue component not take up the vertical space to push the side panels to the top?
This is my SSCCE code:
import javax.swing.*;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import jrdb.data.ProcessingCommand;
public class ProcessingPipelineBuilderSSCCE extends JFrame {
/**
*
*/
private static final long serialVersionUID = 2413084448601918744L;
private JPanel interiorPanel = null;
private GridBagConstraints gbc = null;
private Component glue = null;
public ProcessingPipelineBuilderSSCCE() {
super("SSCCE");
this.getContentPane().setLayout(new BorderLayout());
gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 0, 5);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.anchor = GridBagConstraints.PAGE_START;
JPanel pipelineBuilder = new JPanel();
pipelineBuilder.setLayout(new GridLayout(0, 1, 0, 0));
interiorPanel = new JPanel(new GridBagLayout());
interiorPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JScrollPane scrollPane = new JScrollPane(interiorPanel);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setPreferredSize(new Dimension(500,300));
pipelineBuilder.add(scrollPane);
JButton btnNew = new JButton("Add new panel");
btnNew.setPreferredSize(new Dimension(500, 30));
btnNew.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (glue!=null) {
interiorPanel.remove(glue);
} else {
glue = Box.createGlue();
}
gbc.gridy = gbc.gridy + 1;
interiorPanel.add(new PipelineStep(gbc.gridy),gbc);
interiorPanel.add(glue,gbc);
interiorPanel.validate();
interiorPanel.repaint();
}
});
this.getContentPane().add(btnNew, BorderLayout.PAGE_START);
this.getContentPane().add(pipelineBuilder,BorderLayout.CENTER);
}
public class PipelineStep extends JPanel {
int number;
public PipelineStep (int n) {
super();
JOptionPane.showMessageDialog(interiorPanel, "adding new panel");
this.number = n;
this.setLayout(new FlowLayout());
JLabel lbl = new JLabel(new Integer(this.number).toString());
lbl.setPreferredSize(new Dimension(45,45));
lbl.setFont(lbl.getFont().deriveFont(26));
this.add(lbl);
this.setPreferredSize(new Dimension(450, 50));
this.setBorder(BorderFactory.createLineBorder(Color.green));
}
}
public static void main (String args[]) {
ProcessingPipelineBuilderSSCCE frame = new ProcessingPipelineBuilderSSCCE();
frame.pack();
frame.setVisible(true);
}
}
回答1:
Why does the glue component not take up the vertical space to push the side panels to the top?
The "glue" component only has meaning when used with the BoxLayout. It has no effect with the GridBagLayout.
So my suggestion is to forget about the GridBagLayout and use the BoxLayout.
The easiest way to do this is to convert "interiorPanel" to use a vertical Box and just add your PipelineStep
instances to this panel.
Try this. However, you will notice that the panels will still increase in size until the scroll pane is full, at which time you will see scrollbars appear. This is because the BoxLayout will resize components up to the maximum size of the component. So to prevent this resizing you could override the getMaximumSize()
method of your PipelineStep
class:
@Override
public Dimension getMaximumSize()
{
return getPreferredSize();
}
Or, another option is to use a "wrapper" panel for your "interiorPanel". Something like:
JPanel wrapper = new JPanel( new BorderLayout() );
wrapper.add(interiorPanel, BorderLayout.PAGE_START);
//JScrollPane scrollPane = new JScrollPane(interiorPanel);
JScrollPane scrollPane = new JScrollPane(wrapper);
BorderLayout.PAGE_START
respects the preferred height of the component added to it so the "interiorPanel" will always be displayed at it preferred height and scrollbars will appear when the viewport of the scroll pane is full.
I modified you code using the "wrapper" approach.
import javax.swing.*;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
//import jrdb.data.ProcessingCommand;
public class SSCCE1 extends JFrame {
/**
*
*/
private static final long serialVersionUID = 2413084448601918744L;
// private JPanel interiorPanel = null;
private Box interiorPanel = null;
private GridBagConstraints gbc = null;
private Component glue = null;
public SSCCE1() {
super("SSCCE");
this.getContentPane().setLayout(new BorderLayout());
gbc = new GridBagConstraints();
//gbc.insets = new Insets(5, 5, 0, 5);
//gbc.fill = GridBagConstraints.HORIZONTAL;
//gbc.gridx = 0;
//gbc.gridy = 0;
//gbc.weightx = 1.0;
//gbc.weighty = 1.0;
//gbc.anchor = GridBagConstraints.PAGE_START;
JPanel pipelineBuilder = new JPanel();
pipelineBuilder.setLayout(new GridLayout(0, 1, 0, 0));
// interiorPanel = new JPanel(new GridBagLayout());
interiorPanel = Box.createVerticalBox();
interiorPanel.setBorder(BorderFactory.createLineBorder(Color.red));
JPanel wrapper = new JPanel( new BorderLayout() );
wrapper.add(interiorPanel, BorderLayout.PAGE_START);
// JScrollPane scrollPane = new JScrollPane(interiorPanel);
JScrollPane scrollPane = new JScrollPane(wrapper);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setPreferredSize(new Dimension(500,300));
pipelineBuilder.add(scrollPane);
JButton btnNew = new JButton("Add new panel");
btnNew.setPreferredSize(new Dimension(500, 30));
btnNew.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// if (glue!=null) {
// interiorPanel.remove(glue);
// } else {
// glue = Box.createGlue();
// }
gbc.gridy = gbc.gridy + 1;
// interiorPanel.add(new PipelineStep(gbc.gridy),gbc);
interiorPanel.add(new PipelineStep(gbc.gridy),gbc);
// interiorPanel.add(glue,gbc);
// interiorPanel.validate();
interiorPanel.revalidate();
interiorPanel.repaint();
}
});
this.getContentPane().add(btnNew, BorderLayout.PAGE_START);
this.getContentPane().add(pipelineBuilder,BorderLayout.CENTER);
}
public class PipelineStep extends JPanel {
int number;
public PipelineStep (int n) {
super();
JOptionPane.showMessageDialog(interiorPanel, "adding new panel");
this.number = n;
this.setLayout(new FlowLayout());
JLabel lbl = new JLabel(new Integer(this.number).toString());
lbl.setPreferredSize(new Dimension(45,45));
lbl.setFont(lbl.getFont().deriveFont(26));
this.add(lbl);
this.setPreferredSize(new Dimension(450, 50));
this.setBorder(BorderFactory.createLineBorder(Color.green));
}
}
public static void main (String args[]) {
SSCCE1 frame = new SSCCE1();
frame.pack();
frame.setVisible(true);
}
}
来源:https://stackoverflow.com/questions/61398928/gridbaglayout-with-glue-no-fixed-row-height