带胶水的 GridBagLayout:没有固定的行高

GridBagLayout with glue : no fixed row height

我想创建一个面板,我可以在其中动态添加固定高度的子面板。我尝试使用胶水组件,但它不起作用。我想实现子面板在 gridbaglayout 的顶部可见。附带问题是,当我继续添加子面板时,它们开始重叠,因为 JScrollPane 没有调整。但是,当我调整框架大小时,这两个问题都解决了。

目前我还看不出哪里错了。为什么胶水组件不占用垂直space将侧板推到顶部?

这是我的 SSCCE 代码:

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

}

Why does the glue component not take up the vertical space to push the side panels to the top?

"glue" 组件仅在与 BoxLayout 一起使用时才有意义。它对 GridBagLayout 没有影响。

所以我的建议是忘记 GridBagLayout 并使用 BoxLayout。

最简单的方法是将 "interiorPanel" 转换为使用垂直框,然后将您的 PipelineStep 实例添加到此面板。

试试这个。但是,您会注意到面板的大小仍会增加,直到滚动窗格已满,此时您会看到滚动条出现。这是因为 BoxLayout 会将组件的大小调整为组件的最大尺寸。因此,为了防止这种调整大小,您可以覆盖 PipelineStep class:

getMaximumSize() 方法
@Override
public Dimension getMaximumSize()
{
    return getPreferredSize();
}

或者,另一种选择是为您的 "interiorPanel" 使用 "wrapper" 面板。类似于:

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 尊重添加到其中的组件的首选高度,因此 "interiorPanel" 将始终以其首选高度显示,并且当滚动窗格的视口已满时将出现滚动条。

我使用 "wrapper" 方法修改了您的代码。

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

}